chore: add csharpier to husky, format backend with csharpier
This commit is contained in:
parent
5fab66444f
commit
7f971e8549
73 changed files with 2098 additions and 1048 deletions
|
@ -3,4 +3,4 @@ namespace Foxnouns.Backend.Database;
|
|||
public abstract class BaseModel
|
||||
{
|
||||
public required Snowflake Id { get; init; } = SnowflakeGenerator.Instance.GenerateSnowflake();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,11 +45,12 @@ public class DatabaseContext : DbContext
|
|||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
|
||||
optionsBuilder
|
||||
.ConfigureWarnings(c =>
|
||||
c.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning)
|
||||
.Ignore(CoreEventId.SaveChangesFailed))
|
||||
.Ignore(CoreEventId.SaveChangesFailed)
|
||||
)
|
||||
.UseNpgsql(_dataSource, o => o.UseNodaTime())
|
||||
.UseSnakeCaseNamingConvention()
|
||||
.UseLoggerFactory(_loggerFactory)
|
||||
|
@ -76,7 +77,10 @@ public class DatabaseContext : DbContext
|
|||
modelBuilder.Entity<User>().Property(u => u.CustomPreferences).HasColumnType("jsonb");
|
||||
modelBuilder.Entity<User>().Property(u => u.Settings).HasColumnType("jsonb");
|
||||
|
||||
modelBuilder.Entity<Member>().Property(m => m.Sid).HasDefaultValueSql("find_free_member_sid()");
|
||||
modelBuilder
|
||||
.Entity<Member>()
|
||||
.Property(m => m.Sid)
|
||||
.HasDefaultValueSql("find_free_member_sid()");
|
||||
modelBuilder.Entity<Member>().Property(m => m.Fields).HasColumnType("jsonb");
|
||||
modelBuilder.Entity<Member>().Property(m => m.Names).HasColumnType("jsonb");
|
||||
modelBuilder.Entity<Member>().Property(m => m.Pronouns).HasColumnType("jsonb");
|
||||
|
@ -84,10 +88,12 @@ public class DatabaseContext : DbContext
|
|||
modelBuilder.Entity<UserFlag>().Navigation(f => f.PrideFlag).AutoInclude();
|
||||
modelBuilder.Entity<MemberFlag>().Navigation(f => f.PrideFlag).AutoInclude();
|
||||
|
||||
modelBuilder.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(FindFreeUserSid))!)
|
||||
modelBuilder
|
||||
.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(FindFreeUserSid))!)
|
||||
.HasName("find_free_user_sid");
|
||||
|
||||
modelBuilder.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(FindFreeMemberSid))!)
|
||||
modelBuilder
|
||||
.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(FindFreeMemberSid))!)
|
||||
.HasName("find_free_member_sid");
|
||||
}
|
||||
|
||||
|
@ -102,18 +108,23 @@ public class DatabaseContext : DbContext
|
|||
public string FindFreeMemberSid() => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedType.Global", Justification = "Used by EF Core's migration generator")]
|
||||
[SuppressMessage(
|
||||
"ReSharper",
|
||||
"UnusedType.Global",
|
||||
Justification = "Used by EF Core's migration generator"
|
||||
)]
|
||||
public class DesignTimeDatabaseContextFactory : IDesignTimeDbContextFactory<DatabaseContext>
|
||||
{
|
||||
public DatabaseContext CreateDbContext(string[] args)
|
||||
{
|
||||
// Read the configuration file
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddConfiguration()
|
||||
.Build()
|
||||
// Get the configuration as our config class
|
||||
.Get<Config>() ?? new();
|
||||
var config =
|
||||
new ConfigurationBuilder()
|
||||
.AddConfiguration()
|
||||
.Build()
|
||||
// Get the configuration as our config class
|
||||
.Get<Config>() ?? new();
|
||||
|
||||
return new DatabaseContext(config, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,89 +8,128 @@ namespace Foxnouns.Backend.Database;
|
|||
|
||||
public static class DatabaseQueryExtensions
|
||||
{
|
||||
public static async Task<User> ResolveUserAsync(this DatabaseContext context, string userRef, Token? token,
|
||||
CancellationToken ct = default)
|
||||
public static async Task<User> ResolveUserAsync(
|
||||
this DatabaseContext context,
|
||||
string userRef,
|
||||
Token? token,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
if (userRef == "@me")
|
||||
{
|
||||
return token != null
|
||||
? await context.Users.FirstAsync(u => u.Id == token.UserId, ct)
|
||||
: throw new ApiError.Unauthorized("This endpoint requires an authenticated user.",
|
||||
ErrorCode.AuthenticationRequired);
|
||||
: throw new ApiError.Unauthorized(
|
||||
"This endpoint requires an authenticated user.",
|
||||
ErrorCode.AuthenticationRequired
|
||||
);
|
||||
}
|
||||
|
||||
User? user;
|
||||
if (Snowflake.TryParse(userRef, out var snowflake))
|
||||
{
|
||||
user = await context.Users
|
||||
.Where(u => !u.Deleted)
|
||||
user = await context
|
||||
.Users.Where(u => !u.Deleted)
|
||||
.FirstOrDefaultAsync(u => u.Id == snowflake, ct);
|
||||
if (user != null) return user;
|
||||
if (user != null)
|
||||
return user;
|
||||
}
|
||||
|
||||
user = await context.Users
|
||||
.Where(u => !u.Deleted)
|
||||
user = await context
|
||||
.Users.Where(u => !u.Deleted)
|
||||
.FirstOrDefaultAsync(u => u.Username == userRef, ct);
|
||||
if (user != null) return user;
|
||||
throw new ApiError.NotFound("No user with that ID or username found.", code: ErrorCode.UserNotFound);
|
||||
if (user != null)
|
||||
return user;
|
||||
throw new ApiError.NotFound(
|
||||
"No user with that ID or username found.",
|
||||
code: ErrorCode.UserNotFound
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<User> ResolveUserAsync(this DatabaseContext context, Snowflake id,
|
||||
CancellationToken ct = default)
|
||||
public static async Task<User> ResolveUserAsync(
|
||||
this DatabaseContext context,
|
||||
Snowflake id,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var user = await context.Users
|
||||
.Where(u => !u.Deleted)
|
||||
var user = await context
|
||||
.Users.Where(u => !u.Deleted)
|
||||
.FirstOrDefaultAsync(u => u.Id == id, ct);
|
||||
if (user != null) return user;
|
||||
if (user != null)
|
||||
return user;
|
||||
throw new ApiError.NotFound("No user with that ID found.", code: ErrorCode.UserNotFound);
|
||||
}
|
||||
|
||||
public static async Task<Member> ResolveMemberAsync(this DatabaseContext context, Snowflake id,
|
||||
CancellationToken ct = default)
|
||||
public static async Task<Member> ResolveMemberAsync(
|
||||
this DatabaseContext context,
|
||||
Snowflake id,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var member = await context.Members
|
||||
.Include(m => m.User)
|
||||
var member = await context
|
||||
.Members.Include(m => m.User)
|
||||
.Where(m => !m.User.Deleted)
|
||||
.FirstOrDefaultAsync(m => m.Id == id, ct);
|
||||
if (member != null) return member;
|
||||
throw new ApiError.NotFound("No member with that ID found.", code: ErrorCode.MemberNotFound);
|
||||
if (member != null)
|
||||
return member;
|
||||
throw new ApiError.NotFound(
|
||||
"No member with that ID found.",
|
||||
code: ErrorCode.MemberNotFound
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<Member> ResolveMemberAsync(this DatabaseContext context, string userRef, string memberRef,
|
||||
Token? token, CancellationToken ct = default)
|
||||
public static async Task<Member> ResolveMemberAsync(
|
||||
this DatabaseContext context,
|
||||
string userRef,
|
||||
string memberRef,
|
||||
Token? token,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var user = await context.ResolveUserAsync(userRef, token, ct);
|
||||
return await context.ResolveMemberAsync(user.Id, memberRef, ct);
|
||||
}
|
||||
|
||||
public static async Task<Member> ResolveMemberAsync(this DatabaseContext context, Snowflake userId,
|
||||
string memberRef, CancellationToken ct = default)
|
||||
public static async Task<Member> ResolveMemberAsync(
|
||||
this DatabaseContext context,
|
||||
Snowflake userId,
|
||||
string memberRef,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
Member? member;
|
||||
if (Snowflake.TryParse(memberRef, out var snowflake))
|
||||
{
|
||||
member = await context.Members
|
||||
.Include(m => m.User)
|
||||
member = await context
|
||||
.Members.Include(m => m.User)
|
||||
.Include(m => m.ProfileFlags)
|
||||
.Where(m => !m.User.Deleted)
|
||||
.FirstOrDefaultAsync(m => m.Id == snowflake && m.UserId == userId, ct);
|
||||
if (member != null) return member;
|
||||
if (member != null)
|
||||
return member;
|
||||
}
|
||||
|
||||
member = await context.Members
|
||||
.Include(m => m.User)
|
||||
member = await context
|
||||
.Members.Include(m => m.User)
|
||||
.Include(m => m.ProfileFlags)
|
||||
.Where(m => !m.User.Deleted)
|
||||
.FirstOrDefaultAsync(m => m.Name == memberRef && m.UserId == userId, ct);
|
||||
if (member != null) return member;
|
||||
throw new ApiError.NotFound("No member with that ID or name found.", code: ErrorCode.MemberNotFound);
|
||||
if (member != null)
|
||||
return member;
|
||||
throw new ApiError.NotFound(
|
||||
"No member with that ID or name found.",
|
||||
code: ErrorCode.MemberNotFound
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<Application> GetFrontendApplicationAsync(this DatabaseContext context,
|
||||
CancellationToken ct = default)
|
||||
public static async Task<Application> GetFrontendApplicationAsync(
|
||||
this DatabaseContext context,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var app = await context.Applications.FirstOrDefaultAsync(a => a.Id == new Snowflake(0), ct);
|
||||
if (app != null) return app;
|
||||
if (app != null)
|
||||
return app;
|
||||
|
||||
app = new Application
|
||||
{
|
||||
|
@ -107,27 +146,42 @@ public static class DatabaseQueryExtensions
|
|||
return app;
|
||||
}
|
||||
|
||||
public static async Task<Token?> GetToken(this DatabaseContext context, byte[] rawToken,
|
||||
CancellationToken ct = default)
|
||||
public static async Task<Token?> GetToken(
|
||||
this DatabaseContext context,
|
||||
byte[] rawToken,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var hash = SHA512.HashData(rawToken);
|
||||
|
||||
var oauthToken = await context.Tokens
|
||||
.Include(t => t.Application)
|
||||
var oauthToken = await context
|
||||
.Tokens.Include(t => t.Application)
|
||||
.Include(t => t.User)
|
||||
.FirstOrDefaultAsync(
|
||||
t => t.Hash == hash && t.ExpiresAt > SystemClock.Instance.GetCurrentInstant() && !t.ManuallyExpired,
|
||||
ct);
|
||||
t =>
|
||||
t.Hash == hash
|
||||
&& t.ExpiresAt > SystemClock.Instance.GetCurrentInstant()
|
||||
&& !t.ManuallyExpired,
|
||||
ct
|
||||
);
|
||||
|
||||
return oauthToken;
|
||||
}
|
||||
|
||||
public static async Task<Snowflake?> GetTokenUserId(this DatabaseContext context, byte[] rawToken,
|
||||
CancellationToken ct = default)
|
||||
public static async Task<Snowflake?> GetTokenUserId(
|
||||
this DatabaseContext context,
|
||||
byte[] rawToken,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var hash = SHA512.HashData(rawToken);
|
||||
return await context.Tokens
|
||||
.Where(t => t.Hash == hash && t.ExpiresAt > SystemClock.Instance.GetCurrentInstant() && !t.ManuallyExpired)
|
||||
.Select(t => t.UserId).FirstOrDefaultAsync(ct);
|
||||
return await context
|
||||
.Tokens.Where(t =>
|
||||
t.Hash == hash
|
||||
&& t.ExpiresAt > SystemClock.Instance.GetCurrentInstant()
|
||||
&& !t.ManuallyExpired
|
||||
)
|
||||
.Select(t => t.UserId)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,23 +5,30 @@ namespace Foxnouns.Backend.Database;
|
|||
|
||||
public static class FlagQueryExtensions
|
||||
{
|
||||
private static async Task<List<PrideFlag>> GetFlagsAsync(this DatabaseContext db, Snowflake userId) =>
|
||||
await db.PrideFlags.Where(f => f.UserId == userId).OrderBy(f => f.Id).ToListAsync();
|
||||
private static async Task<List<PrideFlag>> GetFlagsAsync(
|
||||
this DatabaseContext db,
|
||||
Snowflake userId
|
||||
) => await db.PrideFlags.Where(f => f.UserId == userId).OrderBy(f => f.Id).ToListAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the user's profile flags to the given IDs. Returns a validation error if any of the flag IDs are unknown
|
||||
/// or if too many IDs are given. Duplicates are allowed.
|
||||
/// </summary>
|
||||
public static async Task<ValidationError?> SetUserFlagsAsync(this DatabaseContext db, Snowflake userId,
|
||||
Snowflake[] flagIds)
|
||||
public static async Task<ValidationError?> SetUserFlagsAsync(
|
||||
this DatabaseContext db,
|
||||
Snowflake userId,
|
||||
Snowflake[] flagIds
|
||||
)
|
||||
{
|
||||
var currentFlags = await db.UserFlags.Where(f => f.UserId == userId).ToListAsync();
|
||||
foreach (var flag in currentFlags)
|
||||
db.UserFlags.Remove(flag);
|
||||
|
||||
// If there's no new flags to set, we're done
|
||||
if (flagIds.Length == 0) return null;
|
||||
if (flagIds.Length > 100) return ValidationError.LengthError("Too many profile flags", 0, 100, flagIds.Length);
|
||||
if (flagIds.Length == 0)
|
||||
return null;
|
||||
if (flagIds.Length > 100)
|
||||
return ValidationError.LengthError("Too many profile flags", 0, 100, flagIds.Length);
|
||||
|
||||
var flags = await db.GetFlagsAsync(userId);
|
||||
var unknownFlagIds = flagIds.Where(id => flags.All(f => f.Id != id)).ToArray();
|
||||
|
@ -34,24 +41,34 @@ public static class FlagQueryExtensions
|
|||
return null;
|
||||
}
|
||||
|
||||
public static async Task<ValidationError?> SetMemberFlagsAsync(this DatabaseContext db, Snowflake userId,
|
||||
Snowflake memberId, Snowflake[] flagIds)
|
||||
public static async Task<ValidationError?> SetMemberFlagsAsync(
|
||||
this DatabaseContext db,
|
||||
Snowflake userId,
|
||||
Snowflake memberId,
|
||||
Snowflake[] flagIds
|
||||
)
|
||||
{
|
||||
var currentFlags = await db.MemberFlags.Where(f => f.MemberId == memberId).ToListAsync();
|
||||
foreach (var flag in currentFlags)
|
||||
db.MemberFlags.Remove(flag);
|
||||
|
||||
if (flagIds.Length == 0) return null;
|
||||
if (flagIds.Length > 100) return ValidationError.LengthError("Too many profile flags", 0, 100, flagIds.Length);
|
||||
if (flagIds.Length == 0)
|
||||
return null;
|
||||
if (flagIds.Length > 100)
|
||||
return ValidationError.LengthError("Too many profile flags", 0, 100, flagIds.Length);
|
||||
|
||||
var flags = await db.GetFlagsAsync(userId);
|
||||
var unknownFlagIds = flagIds.Where(id => flags.All(f => f.Id != id)).ToArray();
|
||||
if (unknownFlagIds.Length != 0)
|
||||
return ValidationError.GenericValidationError("Unknown flag IDs", unknownFlagIds);
|
||||
|
||||
var memberFlags = flagIds.Select(id => new MemberFlag { PrideFlagId = id, MemberId = memberId });
|
||||
var memberFlags = flagIds.Select(id => new MemberFlag
|
||||
{
|
||||
PrideFlagId = id,
|
||||
MemberId = memberId,
|
||||
});
|
||||
db.MemberFlags.AddRange(memberFlags);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@ namespace Foxnouns.Backend.Database;
|
|||
public interface ISnowflakeGenerator
|
||||
{
|
||||
Snowflake GenerateSnowflake(Instant? time = null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
@ -22,12 +22,13 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
domain = table.Column<string>(type: "text", nullable: false),
|
||||
client_id = table.Column<string>(type: "text", nullable: false),
|
||||
client_secret = table.Column<string>(type: "text", nullable: false),
|
||||
instance_type = table.Column<int>(type: "integer", nullable: false)
|
||||
instance_type = table.Column<int>(type: "integer", nullable: false),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_fediverse_applications", x => x.id);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "users",
|
||||
|
@ -43,12 +44,13 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
role = table.Column<int>(type: "integer", nullable: false),
|
||||
fields = table.Column<string>(type: "jsonb", nullable: false),
|
||||
names = table.Column<string>(type: "jsonb", nullable: false),
|
||||
pronouns = table.Column<string>(type: "jsonb", nullable: false)
|
||||
pronouns = table.Column<string>(type: "jsonb", nullable: false),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_users", x => x.id);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "auth_methods",
|
||||
|
@ -59,7 +61,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
remote_id = table.Column<string>(type: "text", nullable: false),
|
||||
remote_username = table.Column<string>(type: "text", nullable: true),
|
||||
user_id = table.Column<long>(type: "bigint", nullable: false),
|
||||
fediverse_application_id = table.Column<long>(type: "bigint", nullable: true)
|
||||
fediverse_application_id = table.Column<long>(type: "bigint", nullable: true),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -68,14 +70,17 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
name: "fk_auth_methods_fediverse_applications_fediverse_application_id",
|
||||
column: x => x.fediverse_application_id,
|
||||
principalTable: "fediverse_applications",
|
||||
principalColumn: "id");
|
||||
principalColumn: "id"
|
||||
);
|
||||
table.ForeignKey(
|
||||
name: "fk_auth_methods_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
onDelete: ReferentialAction.Cascade
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "members",
|
||||
|
@ -91,7 +96,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
user_id = table.Column<long>(type: "bigint", nullable: false),
|
||||
fields = table.Column<string>(type: "jsonb", nullable: false),
|
||||
names = table.Column<string>(type: "jsonb", nullable: false),
|
||||
pronouns = table.Column<string>(type: "jsonb", nullable: false)
|
||||
pronouns = table.Column<string>(type: "jsonb", nullable: false),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -101,18 +106,23 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
onDelete: ReferentialAction.Cascade
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "tokens",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<long>(type: "bigint", nullable: false),
|
||||
expires_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
expires_at = table.Column<Instant>(
|
||||
type: "timestamp with time zone",
|
||||
nullable: false
|
||||
),
|
||||
scopes = table.Column<string[]>(type: "text[]", nullable: false),
|
||||
manually_expired = table.Column<bool>(type: "boolean", nullable: false),
|
||||
user_id = table.Column<long>(type: "bigint", nullable: false)
|
||||
user_id = table.Column<long>(type: "bigint", nullable: false),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -122,53 +132,56 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
onDelete: ReferentialAction.Cascade
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_methods_fediverse_application_id",
|
||||
table: "auth_methods",
|
||||
column: "fediverse_application_id");
|
||||
column: "fediverse_application_id"
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_auth_methods_user_id",
|
||||
table: "auth_methods",
|
||||
column: "user_id");
|
||||
column: "user_id"
|
||||
);
|
||||
|
||||
// EF Core doesn't support creating indexes on arbitrary expressions, so we have to create it manually.
|
||||
// Due to historical reasons (I made a mistake while writing the initial migration for the Go version)
|
||||
// only members have case-insensitive names.
|
||||
migrationBuilder.Sql("CREATE UNIQUE INDEX ix_members_user_id_name ON members (user_id, lower(name))");
|
||||
migrationBuilder.Sql(
|
||||
"CREATE UNIQUE INDEX ix_members_user_id_name ON members (user_id, lower(name))"
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tokens_user_id",
|
||||
table: "tokens",
|
||||
column: "user_id");
|
||||
column: "user_id"
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_users_username",
|
||||
table: "users",
|
||||
column: "username",
|
||||
unique: true);
|
||||
unique: true
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "auth_methods");
|
||||
migrationBuilder.DropTable(name: "auth_methods");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "members");
|
||||
migrationBuilder.DropTable(name: "members");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "tokens");
|
||||
migrationBuilder.DropTable(name: "tokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "fediverse_applications");
|
||||
migrationBuilder.DropTable(name: "fediverse_applications");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "users");
|
||||
migrationBuilder.DropTable(name: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
@ -18,14 +18,16 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
table: "tokens",
|
||||
type: "bigint",
|
||||
nullable: false,
|
||||
defaultValue: 0L);
|
||||
defaultValue: 0L
|
||||
);
|
||||
|
||||
migrationBuilder.AddColumn<byte[]>(
|
||||
name: "hash",
|
||||
table: "tokens",
|
||||
type: "bytea",
|
||||
nullable: false,
|
||||
defaultValue: new byte[0]);
|
||||
defaultValue: new byte[0]
|
||||
);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "applications",
|
||||
|
@ -36,17 +38,19 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
client_secret = table.Column<string>(type: "text", nullable: false),
|
||||
name = table.Column<string>(type: "text", nullable: false),
|
||||
scopes = table.Column<string[]>(type: "text[]", nullable: false),
|
||||
redirect_uris = table.Column<string[]>(type: "text[]", nullable: false)
|
||||
redirect_uris = table.Column<string[]>(type: "text[]", nullable: false),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_applications", x => x.id);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tokens_application_id",
|
||||
table: "tokens",
|
||||
column: "application_id");
|
||||
column: "application_id"
|
||||
);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_tokens_applications_application_id",
|
||||
|
@ -54,7 +58,8 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
column: "application_id",
|
||||
principalTable: "applications",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Cascade
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -62,22 +67,16 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_tokens_applications_application_id",
|
||||
table: "tokens");
|
||||
table: "tokens"
|
||||
);
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "applications");
|
||||
migrationBuilder.DropTable(name: "applications");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_tokens_application_id",
|
||||
table: "tokens");
|
||||
migrationBuilder.DropIndex(name: "ix_tokens_application_id", table: "tokens");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "application_id",
|
||||
table: "tokens");
|
||||
migrationBuilder.DropColumn(name: "application_id", table: "tokens");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hash",
|
||||
table: "tokens");
|
||||
migrationBuilder.DropColumn(name: "hash", table: "tokens");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
@ -18,15 +18,14 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
table: "users",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
defaultValue: false
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "list_hidden",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "list_hidden", table: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
@ -17,15 +17,14 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
name: "password",
|
||||
table: "users",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
nullable: true
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "password",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "password", table: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
|
@ -19,29 +19,37 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
name: "temporary_keys",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
id = table
|
||||
.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation(
|
||||
"Npgsql:ValueGenerationStrategy",
|
||||
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn
|
||||
),
|
||||
key = table.Column<string>(type: "text", nullable: false),
|
||||
value = table.Column<string>(type: "text", nullable: false),
|
||||
expires = table.Column<Instant>(type: "timestamp with time zone", nullable: false)
|
||||
expires = table.Column<Instant>(
|
||||
type: "timestamp with time zone",
|
||||
nullable: false
|
||||
),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_temporary_keys", x => x.id);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_temporary_keys_key",
|
||||
table: "temporary_keys",
|
||||
column: "key",
|
||||
unique: true);
|
||||
unique: true
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "temporary_keys");
|
||||
migrationBuilder.DropTable(name: "temporary_keys");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
@ -19,15 +19,14 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
table: "users",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValueSql: "now()");
|
||||
defaultValueSql: "now()"
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_active",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "last_active", table: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
@ -19,35 +19,32 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
table: "users",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
defaultValue: false
|
||||
);
|
||||
|
||||
migrationBuilder.AddColumn<Instant>(
|
||||
name: "deleted_at",
|
||||
table: "users",
|
||||
type: "timestamp with time zone",
|
||||
nullable: true);
|
||||
nullable: true
|
||||
);
|
||||
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "deleted_by",
|
||||
table: "users",
|
||||
type: "bigint",
|
||||
nullable: true);
|
||||
nullable: true
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "deleted",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "deleted", table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "deleted_at",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "deleted_at", table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "deleted_by",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "deleted_by", table: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using System.Collections.Generic;
|
||||
using Foxnouns.Backend.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
@ -21,15 +21,14 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
table: "users",
|
||||
type: "jsonb",
|
||||
nullable: false,
|
||||
defaultValueSql: "'{}'");
|
||||
defaultValueSql: "'{}'"
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "custom_preferences",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "custom_preferences", table: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,15 +19,14 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
table: "users",
|
||||
type: "jsonb",
|
||||
nullable: false,
|
||||
defaultValueSql: "'{}'");
|
||||
defaultValueSql: "'{}'"
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "settings",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "settings", table: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
@ -18,38 +18,46 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
name: "sid",
|
||||
table: "users",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
nullable: true
|
||||
);
|
||||
|
||||
migrationBuilder.AddColumn<Instant>(
|
||||
name: "last_sid_reroll",
|
||||
table: "users",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValueSql: "now() - '1 hour'::interval");
|
||||
defaultValueSql: "now() - '1 hour'::interval"
|
||||
);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "sid",
|
||||
table: "members",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
nullable: true
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_users_sid",
|
||||
table: "users",
|
||||
column: "sid",
|
||||
unique: true);
|
||||
unique: true
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_members_sid",
|
||||
table: "members",
|
||||
column: "sid",
|
||||
unique: true);
|
||||
unique: true
|
||||
);
|
||||
|
||||
migrationBuilder.Sql(@"create function generate_sid(len int) returns text as $$
|
||||
migrationBuilder.Sql(
|
||||
@"create function generate_sid(len int) returns text as $$
|
||||
select string_agg(substr('abcdefghijklmnopqrstuvwxyz', ceil(random() * 26)::integer, 1), '') from generate_series(1, len)
|
||||
$$ language sql volatile;
|
||||
");
|
||||
migrationBuilder.Sql(@"create function find_free_user_sid() returns text as $$
|
||||
"
|
||||
);
|
||||
migrationBuilder.Sql(
|
||||
@"create function find_free_user_sid() returns text as $$
|
||||
declare new_sid text;
|
||||
begin
|
||||
loop
|
||||
|
@ -58,8 +66,10 @@ begin
|
|||
end loop;
|
||||
end
|
||||
$$ language plpgsql volatile;
|
||||
");
|
||||
migrationBuilder.Sql(@"create function find_free_member_sid() returns text as $$
|
||||
"
|
||||
);
|
||||
migrationBuilder.Sql(
|
||||
@"create function find_free_member_sid() returns text as $$
|
||||
declare new_sid text;
|
||||
begin
|
||||
loop
|
||||
|
@ -67,7 +77,8 @@ begin
|
|||
if not exists (select 1 from members where sid = new_sid) then return new_sid; end if;
|
||||
end loop;
|
||||
end
|
||||
$$ language plpgsql volatile;");
|
||||
$$ language plpgsql volatile;"
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -77,25 +88,15 @@ $$ language plpgsql volatile;");
|
|||
migrationBuilder.Sql("drop function find_free_user_sid;");
|
||||
migrationBuilder.Sql("drop function generate_sid;");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_users_sid",
|
||||
table: "users");
|
||||
migrationBuilder.DropIndex(name: "ix_users_sid", table: "users");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "ix_members_sid",
|
||||
table: "members");
|
||||
migrationBuilder.DropIndex(name: "ix_members_sid", table: "members");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "sid",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "sid", table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "last_sid_reroll",
|
||||
table: "users");
|
||||
migrationBuilder.DropColumn(name: "last_sid_reroll", table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "sid",
|
||||
table: "members");
|
||||
migrationBuilder.DropColumn(name: "sid", table: "members");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
@ -22,7 +22,8 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
defaultValueSql: "find_free_user_sid()",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
oldNullable: true
|
||||
);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "sid",
|
||||
|
@ -32,7 +33,8 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
defaultValueSql: "find_free_member_sid()",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
oldNullable: true
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -45,7 +47,8 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldDefaultValueSql: "find_free_user_sid()");
|
||||
oldDefaultValueSql: "find_free_user_sid()"
|
||||
);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "sid",
|
||||
|
@ -54,7 +57,8 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldDefaultValueSql: "find_free_member_sid()");
|
||||
oldDefaultValueSql: "find_free_member_sid()"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
@ -22,7 +22,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
user_id = table.Column<long>(type: "bigint", nullable: false),
|
||||
hash = table.Column<string>(type: "text", nullable: false),
|
||||
name = table.Column<string>(type: "text", nullable: false),
|
||||
description = table.Column<string>(type: "text", nullable: true)
|
||||
description = table.Column<string>(type: "text", nullable: true),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -32,17 +32,23 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
onDelete: ReferentialAction.Cascade
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "member_flags",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
id = table
|
||||
.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation(
|
||||
"Npgsql:ValueGenerationStrategy",
|
||||
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn
|
||||
),
|
||||
member_id = table.Column<long>(type: "bigint", nullable: false),
|
||||
pride_flag_id = table.Column<long>(type: "bigint", nullable: false)
|
||||
pride_flag_id = table.Column<long>(type: "bigint", nullable: false),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -52,23 +58,30 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
column: x => x.member_id,
|
||||
principalTable: "members",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Cascade
|
||||
);
|
||||
table.ForeignKey(
|
||||
name: "fk_member_flags_pride_flags_pride_flag_id",
|
||||
column: x => x.pride_flag_id,
|
||||
principalTable: "pride_flags",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
onDelete: ReferentialAction.Cascade
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_flags",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
id = table
|
||||
.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation(
|
||||
"Npgsql:ValueGenerationStrategy",
|
||||
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn
|
||||
),
|
||||
user_id = table.Column<long>(type: "bigint", nullable: false),
|
||||
pride_flag_id = table.Column<long>(type: "bigint", nullable: false)
|
||||
pride_flag_id = table.Column<long>(type: "bigint", nullable: false),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
|
@ -78,52 +91,57 @@ namespace Foxnouns.Backend.Database.Migrations
|
|||
column: x => x.pride_flag_id,
|
||||
principalTable: "pride_flags",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Cascade
|
||||
);
|
||||
table.ForeignKey(
|
||||
name: "fk_user_flags_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
onDelete: ReferentialAction.Cascade
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_member_flags_member_id",
|
||||
table: "member_flags",
|
||||
column: "member_id");
|
||||
column: "member_id"
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_member_flags_pride_flag_id",
|
||||
table: "member_flags",
|
||||
column: "pride_flag_id");
|
||||
column: "pride_flag_id"
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_pride_flags_user_id",
|
||||
table: "pride_flags",
|
||||
column: "user_id");
|
||||
column: "user_id"
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_user_flags_pride_flag_id",
|
||||
table: "user_flags",
|
||||
column: "pride_flag_id");
|
||||
column: "pride_flag_id"
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_user_flags_user_id",
|
||||
table: "user_flags",
|
||||
column: "user_id");
|
||||
column: "user_id"
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "member_flags");
|
||||
migrationBuilder.DropTable(name: "member_flags");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_flags");
|
||||
migrationBuilder.DropTable(name: "user_flags");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "pride_flags");
|
||||
migrationBuilder.DropTable(name: "pride_flags");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,20 +11,30 @@ public class Application : BaseModel
|
|||
public required string[] Scopes { get; init; }
|
||||
public required string[] RedirectUris { get; init; }
|
||||
|
||||
public static Application Create(ISnowflakeGenerator snowflakeGenerator, string name, string[] scopes,
|
||||
string[] redirectUrls)
|
||||
public static Application Create(
|
||||
ISnowflakeGenerator snowflakeGenerator,
|
||||
string name,
|
||||
string[] scopes,
|
||||
string[] redirectUrls
|
||||
)
|
||||
{
|
||||
var clientId = RandomNumberGenerator.GetHexString(32, true);
|
||||
var clientSecret = AuthUtils.RandomToken();
|
||||
|
||||
if (scopes.Except(AuthUtils.ApplicationScopes).Any())
|
||||
{
|
||||
throw new ArgumentException("Invalid scopes passed to Application.Create", nameof(scopes));
|
||||
throw new ArgumentException(
|
||||
"Invalid scopes passed to Application.Create",
|
||||
nameof(scopes)
|
||||
);
|
||||
}
|
||||
|
||||
if (redirectUrls.Any(s => !AuthUtils.ValidateRedirectUri(s)))
|
||||
{
|
||||
throw new ArgumentException("Invalid redirect URLs passed to Application.Create", nameof(redirectUrls));
|
||||
throw new ArgumentException(
|
||||
"Invalid redirect URLs passed to Application.Create",
|
||||
nameof(redirectUrls)
|
||||
);
|
||||
}
|
||||
|
||||
return new Application
|
||||
|
@ -34,7 +44,7 @@ public class Application : BaseModel
|
|||
ClientSecret = clientSecret,
|
||||
Name = name,
|
||||
Scopes = scopes,
|
||||
RedirectUris = redirectUrls
|
||||
RedirectUris = redirectUrls,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,4 @@ public enum AuthType
|
|||
Tumblr,
|
||||
Fediverse,
|
||||
Email,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,5 +11,5 @@ public class FediverseApplication : BaseModel
|
|||
public enum FediverseInstanceType
|
||||
{
|
||||
MastodonApi,
|
||||
MisskeyApi
|
||||
}
|
||||
MisskeyApi,
|
||||
}
|
||||
|
|
|
@ -15,4 +15,4 @@ public class FieldEntry
|
|||
public class Pronoun : FieldEntry
|
||||
{
|
||||
public string? DisplayText { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,4 +18,4 @@ public class Member : BaseModel
|
|||
|
||||
public Snowflake UserId { get; init; }
|
||||
public User User { get; init; } = null!;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,4 +22,4 @@ public class MemberFlag
|
|||
public required Snowflake MemberId { get; init; }
|
||||
public required Snowflake PrideFlagId { get; init; }
|
||||
public PrideFlag PrideFlag { get; init; } = null!;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,4 @@ public class TemporaryKey
|
|||
public required string Key { get; init; }
|
||||
public required string Value { get; set; }
|
||||
public Instant Expires { get; init; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,4 +14,4 @@ public class Token : BaseModel
|
|||
|
||||
public Snowflake ApplicationId { get; set; }
|
||||
public Application Application { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,9 @@ public class User : BaseModel
|
|||
public bool Deleted { get; set; }
|
||||
public Instant? DeletedAt { get; set; }
|
||||
public Snowflake? DeletedBy { get; set; }
|
||||
[NotMapped] public bool? SelfDelete => Deleted ? DeletedBy != null : null;
|
||||
|
||||
[NotMapped]
|
||||
public bool? SelfDelete => Deleted ? DeletedBy != null : null;
|
||||
|
||||
public class CustomPreference
|
||||
{
|
||||
|
@ -69,4 +71,4 @@ public enum PreferenceSize
|
|||
public class UserSettings
|
||||
{
|
||||
public bool? DarkMode { get; set; } = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,19 +41,26 @@ public readonly struct Snowflake(ulong value) : IEquatable<Snowflake>
|
|||
public short Increment => (short)(Value & 0xFFF);
|
||||
|
||||
public static bool operator <(Snowflake arg1, Snowflake arg2) => arg1.Value < arg2.Value;
|
||||
|
||||
public static bool operator >(Snowflake arg1, Snowflake arg2) => arg1.Value > arg2.Value;
|
||||
|
||||
public static bool operator ==(Snowflake arg1, Snowflake arg2) => arg1.Value == arg2.Value;
|
||||
|
||||
public static bool operator !=(Snowflake arg1, Snowflake arg2) => arg1.Value != arg2.Value;
|
||||
|
||||
public static implicit operator ulong(Snowflake s) => s.Value;
|
||||
|
||||
public static implicit operator long(Snowflake s) => (long)s.Value;
|
||||
|
||||
public static implicit operator Snowflake(ulong n) => new(n);
|
||||
|
||||
public static implicit operator Snowflake(long n) => new((ulong)n);
|
||||
|
||||
public static bool TryParse(string input, [NotNullWhen(true)] out Snowflake? snowflake)
|
||||
{
|
||||
snowflake = null;
|
||||
if (!ulong.TryParse(input, out var res)) return false;
|
||||
if (!ulong.TryParse(input, out var res))
|
||||
return false;
|
||||
snowflake = new Snowflake(res);
|
||||
return true;
|
||||
}
|
||||
|
@ -66,27 +73,37 @@ public readonly struct Snowflake(ulong value) : IEquatable<Snowflake>
|
|||
}
|
||||
|
||||
public override int GetHashCode() => Value.GetHashCode();
|
||||
|
||||
public override string ToString() => Value.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// An Entity Framework ValueConverter for Snowflakes to longs.
|
||||
/// </summary>
|
||||
// ReSharper disable once ClassNeverInstantiated.Global
|
||||
public class ValueConverter() : ValueConverter<Snowflake, long>(
|
||||
convertToProviderExpression: x => x,
|
||||
convertFromProviderExpression: x => x
|
||||
);
|
||||
public class ValueConverter()
|
||||
: ValueConverter<Snowflake, long>(
|
||||
convertToProviderExpression: x => x,
|
||||
convertFromProviderExpression: x => x
|
||||
);
|
||||
|
||||
private class JsonConverter : JsonConverter<Snowflake>
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, Snowflake value, JsonSerializer serializer)
|
||||
public override void WriteJson(
|
||||
JsonWriter writer,
|
||||
Snowflake value,
|
||||
JsonSerializer serializer
|
||||
)
|
||||
{
|
||||
writer.WriteValue(value.Value.ToString());
|
||||
}
|
||||
|
||||
public override Snowflake ReadJson(JsonReader reader, Type objectType, Snowflake existingValue,
|
||||
public override Snowflake ReadJson(
|
||||
JsonReader reader,
|
||||
Type objectType,
|
||||
Snowflake existingValue,
|
||||
bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
JsonSerializer serializer
|
||||
)
|
||||
{
|
||||
return ulong.Parse((string)reader.Value!);
|
||||
}
|
||||
|
@ -97,12 +114,18 @@ public readonly struct Snowflake(ulong value) : IEquatable<Snowflake>
|
|||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) =>
|
||||
sourceType == typeof(string);
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType) =>
|
||||
destinationType == typeof(Snowflake);
|
||||
public override bool CanConvertTo(
|
||||
ITypeDescriptorContext? context,
|
||||
[NotNullWhen(true)] Type? destinationType
|
||||
) => destinationType == typeof(Snowflake);
|
||||
|
||||
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||
public override object? ConvertFrom(
|
||||
ITypeDescriptorContext? context,
|
||||
CultureInfo? culture,
|
||||
object value
|
||||
)
|
||||
{
|
||||
return TryParse((string)value, out var snowflake) ? snowflake : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,14 +32,20 @@ public class SnowflakeGenerator : ISnowflakeGenerator
|
|||
var threadId = Environment.CurrentManagedThreadId % 32;
|
||||
var timestamp = time.Value.ToUnixTimeMilliseconds() - Snowflake.Epoch;
|
||||
|
||||
return (timestamp << 22) | (uint)(_processId << 17) | (uint)(threadId << 12) | (increment % 4096);
|
||||
return (timestamp << 22)
|
||||
| (uint)(_processId << 17)
|
||||
| (uint)(threadId << 12)
|
||||
| (increment % 4096);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SnowflakeGeneratorServiceExtensions
|
||||
{
|
||||
public static IServiceCollection AddSnowflakeGenerator(this IServiceCollection services, int? processId = null)
|
||||
public static IServiceCollection AddSnowflakeGenerator(
|
||||
this IServiceCollection services,
|
||||
int? processId = null
|
||||
)
|
||||
{
|
||||
return services.AddSingleton<ISnowflakeGenerator>(new SnowflakeGenerator(processId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue