Compare commits

...

2 commits

Author SHA1 Message Date
alyssa
2f4f76993c try again lol
Some checks failed
Build and push Docker image / .net docker build (push) Has been cancelled
.net checks / run .net tests (push) Has been cancelled
.net checks / dotnet-format (push) Has been cancelled
Build and push Rust service Docker images / rust docker build (push) Has been cancelled
rust checks / cargo fmt (push) Has been cancelled
2026-01-20 06:47:17 -05:00
alyssa
f12ef84e55 chore: remove legacy C# DatabaseMigrator 2026-01-18 06:20:29 -05:00
7 changed files with 34 additions and 95 deletions

View file

@ -23,18 +23,16 @@ internal partial class Database: IDatabase
private readonly ILogger _logger;
private readonly IMetrics _metrics;
private readonly DbConnectionCountHolder _countHolder;
private readonly DatabaseMigrator _migrator;
private readonly NpgsqlDataSource _dataSource;
private readonly NpgsqlDataSource _dataSourceMessages;
public Database(CoreConfig config, DbConnectionCountHolder countHolder, ILogger logger,
IMetrics metrics, DatabaseMigrator migrator)
IMetrics metrics)
{
_config = config;
_countHolder = countHolder;
_metrics = metrics;
_migrator = migrator;
_logger = logger.ForContext<Database>();
string connectionString(string src)
@ -121,12 +119,6 @@ internal partial class Database: IDatabase
return conn;
}
public async Task ApplyMigrations()
{
using var conn = await Obtain();
await _migrator.ApplyMigrations(conn);
}
private class PassthroughTypeHandler<T>: SqlMapper.TypeHandler<T>
{
public override void SetValue(IDbDataParameter parameter, T value) => parameter.Value = value;

View file

@ -6,7 +6,6 @@ namespace PluralKit.Core;
public interface IDatabase
{
Task ApplyMigrations();
Task<IPKConnection> Obtain(bool messages = false);
Task Execute(Func<IPKConnection, Task> func);
Task<T> Execute<T>(Func<IPKConnection, Task<T>> func);

View file

@ -1,81 +0,0 @@
using System.Data;
using Dapper;
using Serilog;
namespace PluralKit.Core;
internal class DatabaseMigrator
{
private const string RootPath = "PluralKit.Core.Database"; // "resource path" root for SQL files
private const int TargetSchemaVersion = 52;
private readonly ILogger _logger;
public DatabaseMigrator(ILogger logger)
{
_logger = logger;
}
public async Task ApplyMigrations(IPKConnection conn)
{
// Run everything in a transaction
await using var tx = await conn.BeginTransactionAsync();
// Before applying migrations, clean out views/functions to prevent type errors
await ExecuteSqlFile($"{RootPath}.clean.sql", conn, tx);
// Apply all migrations between the current database version and the target version
await ApplyMigrations(conn, tx);
// Now, reapply views/functions (we deleted them above, no need to worry about conflicts)
await ExecuteSqlFile($"{RootPath}.Views.views.sql", conn, tx);
await ExecuteSqlFile($"{RootPath}.Functions.functions.sql", conn, tx);
// Finally, commit tx
await tx.CommitAsync();
}
private async Task ApplyMigrations(IPKConnection conn, IDbTransaction tx)
{
var currentVersion = await GetCurrentDatabaseVersion(conn);
_logger.Information("Current schema version: {CurrentVersion}", currentVersion);
for (var migration = currentVersion + 1; migration <= TargetSchemaVersion; migration++)
{
_logger.Information("Applying schema migration {MigrationId}", migration);
await ExecuteSqlFile($"{RootPath}.Migrations.{migration}.sql", conn, tx);
}
}
private async Task ExecuteSqlFile(string resourceName, IPKConnection conn, IDbTransaction tx = null)
{
await using var stream = typeof(Database).Assembly.GetManifestResourceStream(resourceName);
if (stream == null) throw new ArgumentException($"Invalid resource name '{resourceName}'");
using var reader = new StreamReader(stream);
var query = await reader.ReadToEndAsync();
await conn.ExecuteAsync(query, transaction: tx);
// If the above creates new enum/composite types, we must tell Npgsql to reload the internal type caches
// This will propagate to every other connection as well, since it marks the global type mapper collection dirty.
((PKConnection)conn).ReloadTypes();
}
private async Task<int> GetCurrentDatabaseVersion(IPKConnection conn)
{
// First, check if the "info" table exists (it may not, if this is a *really* old database)
var hasInfoTable =
await conn.QuerySingleOrDefaultAsync<int>(
"select count(*) from information_schema.tables where table_name = 'info'") == 1;
// If we have the table, read the schema version
if (hasInfoTable)
return await conn.QuerySingleOrDefaultAsync<int>("select schema_version from info");
// If not, we return version "-1"
// This means migration 0 will get executed, getting us into a consistent state
// Then, migration 1 gets executed, which creates the info table and sets version to 1
return -1;
}
}

View file

@ -10,7 +10,6 @@ public class DataStoreModule: Module
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<DbConnectionCountHolder>().SingleInstance();
builder.RegisterType<DatabaseMigrator>().SingleInstance();
builder.RegisterType<Database>().As<IDatabase>().SingleInstance();
builder.RegisterType<ModelRepository>().AsSelf().SingleInstance();

View file

@ -27,6 +27,6 @@
<br/><br/>
<span>for assistance please email us at <a href="mailto:billing@pluralkit.me">billing@pluralkit.me</a></span>
<br/>
<br/><a href="/info/">pricing/refunds</a> | <a href="https://pluralkit.me/terms-of-service/">terms of service</a> | <a href="https://pluralkit.me/privacy/">privacy policy</a>
<br/>PluralKit Premium is offered by PluralKit, LLC. | <a href="/info/">info/pricing/refund policy</a> | <a href="https://pluralkit.me/terms-of-service/">terms of service</a> | <a href="https://pluralkit.me/privacy/">privacy policy</a>
<br/><a href="/">home</a>
</body>

View file

@ -133,6 +133,21 @@ error initializing paddle client
{% endif %}
{% if show_login_form %}
<p>
PluralKit Premium is a paid subscription for the <a href="https://pluralkit.me">PluralKit Discord bot</a> that offers cosmetic perks as well as power user features.
<br/>Currently offered is
<ul>
<li>the ability to set custom system/member/group IDs,</li>
<li>lossless, higher resolution image hosting on PluralKit's CDN,</li>
<li>the ability to upload avatars/banners directly from the PluralKit Dashboard,</li>
<li>and a badge on your PluralKit system card to show off your support.</li>
</ul>
More features will be added in the future!
</p>
<strong>Premium is not yet released, please check back soon!</strong>
<p>Enter your email address to log in.</p>
<form method="POST" action="/login">
@ -148,6 +163,6 @@ error initializing paddle client
<br/><br/>
<span>for assistance please email us at <a href="mailto:billing@pluralkit.me">billing@pluralkit.me</a></span>
<br/>
<br/><a href="/info/">pricing/refunds</a> | <a href="https://pluralkit.me/terms-of-service/">terms of service</a> | <a href="https://pluralkit.me/privacy/">privacy policy</a>
<br/>PluralKit Premium is offered by PluralKit, LLC. | <a href="/info/">info/pricing/refund policy</a> | <a href="https://pluralkit.me/terms-of-service/">terms of service</a> | <a href="https://pluralkit.me/privacy/">privacy policy</a>
<br/><a href="/">home</a>
</body>

View file

@ -5,6 +5,21 @@
</head>
<body>
<h2>PluralKit Premium</h2>
<p>
PluralKit Premium is a paid subscription for the <a href="https://pluralkit.me">PluralKit Discord bot</a> that offers cosmetic perks as well as power user features.
<br/>Currently offered is
<ul>
<li>the ability to set custom system/member/group IDs,</li>
<li>lossless, higher resolution image hosting on PluralKit's CDN,</li>
<li>the ability to upload avatars/banners directly from the PluralKit Dashboard,</li>
<li>and a badge on your PluralKit system card to show off your support.</li>
</ul>
More features will be added in the future!
</p>
<strong>Premium is not yet released, please check back soon!</strong>
<h3>Pricing</h3>
<p>PluralKit Premium costs $5/mo plus tax applied as per your region.<br/>For any plans longer than 1 month, the equivalent price is applied - for instance, a 3-month plan is $15/3mo plus tax, or a yearly plan is $60/year plus tax.<br/>There is no discount for pre-paying multiple months.</p>
@ -14,6 +29,6 @@
<br/><br/>
<span>for assistance please email us at <a href="mailto:billing@pluralkit.me">billing@pluralkit.me</a></span>
<br/>
<br/><a href="/info/">pricing/refunds</a> | <a href="https://pluralkit.me/terms-of-service/">terms of service</a> | <a href="https://pluralkit.me/privacy/">privacy policy</a>
<br/>PluralKit Premium is offered by PluralKit, LLC. | <a href="/info/">info/pricing/refund policy</a> | <a href="https://pluralkit.me/terms-of-service/">terms of service</a> | <a href="https://pluralkit.me/privacy/">privacy policy</a>
<br/><a href="/">home</a>
</body>