diff --git a/.gitignore b/.gitignore index c61154d..5a2b908 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ proxy-config.json docker/config.ini docker/proxy-config.json docker/frontend.env + +Foxnouns.DataMigrator/apps.json diff --git a/Foxnouns.Backend/Database/Migrations/20241217195351_AddFediAppForceRefresh.cs b/Foxnouns.Backend/Database/Migrations/20241217195351_AddFediAppForceRefresh.cs new file mode 100644 index 0000000..8340273 --- /dev/null +++ b/Foxnouns.Backend/Database/Migrations/20241217195351_AddFediAppForceRefresh.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Foxnouns.Backend.Database.Migrations +{ + /// + [DbContext(typeof(DatabaseContext))] + [Migration("20241217195351_AddFediAppForceRefresh")] + public partial class AddFediAppForceRefresh : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn>( + name: "localization_params", + table: "notifications", + type: "hstore", + nullable: false, + oldClrType: typeof(Dictionary), + oldType: "hstore", + oldNullable: true + ); + + migrationBuilder.AddColumn( + name: "force_refresh", + table: "fediverse_applications", + type: "boolean", + nullable: false, + defaultValue: false + ); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn(name: "force_refresh", table: "fediverse_applications"); + + migrationBuilder.AlterColumn>( + name: "localization_params", + table: "notifications", + type: "hstore", + nullable: true, + oldClrType: typeof(Dictionary), + oldType: "hstore" + ); + } + } +} diff --git a/Foxnouns.Backend/Database/Migrations/DatabaseContextModelSnapshot.cs b/Foxnouns.Backend/Database/Migrations/DatabaseContextModelSnapshot.cs index 83a90fd..79a0232 100644 --- a/Foxnouns.Backend/Database/Migrations/DatabaseContextModelSnapshot.cs +++ b/Foxnouns.Backend/Database/Migrations/DatabaseContextModelSnapshot.cs @@ -216,6 +216,10 @@ namespace Foxnouns.Backend.Database.Migrations .HasColumnType("text") .HasColumnName("domain"); + b.Property("ForceRefresh") + .HasColumnType("boolean") + .HasColumnName("force_refresh"); + b.Property("InstanceType") .HasColumnType("integer") .HasColumnName("instance_type"); @@ -342,6 +346,7 @@ namespace Foxnouns.Backend.Database.Migrations .HasColumnName("localization_key"); b.Property>("LocalizationParams") + .IsRequired() .HasColumnType("hstore") .HasColumnName("localization_params"); @@ -411,7 +416,7 @@ namespace Foxnouns.Backend.Database.Migrations b.Property("ReporterId") .HasColumnType("bigint") .HasColumnName("reporter_id"); - + b.Property("Status") .HasColumnType("integer") .HasColumnName("status"); diff --git a/Foxnouns.Backend/Database/Models/FediverseApplication.cs b/Foxnouns.Backend/Database/Models/FediverseApplication.cs index 13e1318..9c61937 100644 --- a/Foxnouns.Backend/Database/Models/FediverseApplication.cs +++ b/Foxnouns.Backend/Database/Models/FediverseApplication.cs @@ -20,6 +20,7 @@ public class FediverseApplication : BaseModel public required string ClientId { get; set; } public required string ClientSecret { get; set; } public required FediverseInstanceType InstanceType { get; set; } + public bool ForceRefresh { get; set; } } public enum FediverseInstanceType diff --git a/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs b/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs index 9ca7290..49afe1d 100644 --- a/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs +++ b/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs @@ -58,7 +58,7 @@ public partial class FediverseAuthService ) { FediverseApplication app = await GetApplicationAsync(instance); - return await GenerateAuthUrlAsync(app, forceRefresh, state); + return await GenerateAuthUrlAsync(app, forceRefresh || app.ForceRefresh, state); } // thank you, gargron and syuilo, for agreeing on a name for *once* in your lives, diff --git a/Foxnouns.DataMigrator/Program.cs b/Foxnouns.DataMigrator/Program.cs index 307cda5..e79977b 100644 --- a/Foxnouns.DataMigrator/Program.cs +++ b/Foxnouns.DataMigrator/Program.cs @@ -6,6 +6,7 @@ using Foxnouns.Backend.Extensions; using Foxnouns.DataMigrator.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; using Npgsql; using Serilog; using Serilog.Sinks.SystemConsole.Themes; @@ -22,6 +23,12 @@ internal class Program .WriteTo.Console(theme: AnsiConsoleTheme.Sixteen) .CreateLogger(); + var minUserId = new Snowflake(0); + if (args.Length > 0) + minUserId = ulong.Parse(args[0]); + + Log.Information("Starting migration from user ID {MinUserId}", minUserId); + Config config = new ConfigurationBuilder() .AddConfiguration() @@ -35,11 +42,30 @@ internal class Program await context.Database.MigrateAsync(); - Log.Information("Migrating applications"); - Dictionary appIds = await MigrateAppsAsync(conn, context); + Dictionary appIds; + if (minUserId == new Snowflake(0)) + { + Log.Information("Migrating applications"); + appIds = await MigrateAppsAsync(conn, context); + + string appJson = JsonConvert.SerializeObject(appIds); + await File.WriteAllTextAsync("apps.json", appJson); + } + else + { + Log.Information( + "Not the first migration, reading application IDs from {Filename}", + "apps.json" + ); + + string appJson = await File.ReadAllTextAsync("apps.json"); + appIds = + JsonConvert.DeserializeObject>(appJson) + ?? throw new Exception("invalid apps.json file"); + } Log.Information("Migrating users"); - List users = await Queries.GetUsersAsync(conn); + List users = await Queries.GetUsersAsync(conn, minUserId); List userFields = await Queries.GetUserFieldsAsync(conn); List memberFields = await Queries.GetMemberFieldsAsync(conn); List prideFlags = await Queries.GetUserFlagsAsync(conn); @@ -70,6 +96,12 @@ internal class Program await context.SaveChangesAsync(); Log.Information("Migration complete!"); + Log.Information( + "Migrated {Count} users, last user was {UserId}. Complete? {Complete}", + users.Count, + users.Last().SnowflakeId, + users.Count != 1000 + ); } private static async Task> MigrateAppsAsync( @@ -92,6 +124,7 @@ internal class Program ClientId = app.ClientId, ClientSecret = app.ClientSecret, InstanceType = app.TypeToEnum(), + ForceRefresh = true, } ); } diff --git a/Foxnouns.DataMigrator/Queries.cs b/Foxnouns.DataMigrator/Queries.cs index 0d6e71f..0bc14a2 100644 --- a/Foxnouns.DataMigrator/Queries.cs +++ b/Foxnouns.DataMigrator/Queries.cs @@ -13,8 +13,13 @@ public static class Queries public static async Task> GetFediverseAppsAsync(NpgsqlConnection conn) => (await conn.QueryAsync("select * from fediverse_apps")).ToList(); - public static async Task> GetUsersAsync(NpgsqlConnection conn) => - (await conn.QueryAsync("select * from users order by id")).ToList(); + public static async Task> GetUsersAsync(NpgsqlConnection conn, Snowflake minId) => + ( + await conn.QueryAsync( + "select * from users where snowflake_id > @Id order by snowflake_id limit 1000", + new { Id = minId.Value } + ) + ).ToList(); public static async Task> GetUserFieldsAsync(NpgsqlConnection conn) => (await conn.QueryAsync("select * from user_fields order by id")).ToList();