Compare commits
No commits in common. "16f230b97d6ab6293d5cabfd5ae6ab04ab34b9c9" and "e95e0a79ff74ebd636ca7b8ec638a0b660c356ec" have entirely different histories.
16f230b97d
...
e95e0a79ff
20 changed files with 58 additions and 1307 deletions
45
ENDPOINTS.md
45
ENDPOINTS.md
|
@ -1,45 +0,0 @@
|
||||||
# List of API endpoints and scopes
|
|
||||||
|
|
||||||
## Scopes
|
|
||||||
|
|
||||||
- `identify`: `@me` will refer to token user (always granted)
|
|
||||||
- `user.read_privileged`: can read privileged information such as authentication methods
|
|
||||||
- `user.update`: can update the user's profile.
|
|
||||||
**cannot** update anything locked behind `user.read_privileged`
|
|
||||||
- `member.read`: can view member list if it's hidden and enumerate unlisted members
|
|
||||||
- `member.create`: can create new members
|
|
||||||
- `member.update`: can edit and delete members
|
|
||||||
|
|
||||||
## Meta
|
|
||||||
|
|
||||||
- [ ] GET `/meta`: gets stats and server information
|
|
||||||
|
|
||||||
## Users
|
|
||||||
|
|
||||||
- [ ] GET `/users/{userRef}`: views current user.
|
|
||||||
`identify` required to use `@me` as user reference.
|
|
||||||
`user.read_privileged` required to view authentication methods.
|
|
||||||
`member.read` required to view unlisted members.
|
|
||||||
- [ ] PATCH `/users/@me`: updates current user. `user.update` required.
|
|
||||||
- [ ] DELETE `/users/@me`: deletes current user. `*` required
|
|
||||||
- [ ] POST `/users/@me/export`: queues new data export. `*` required
|
|
||||||
- [ ] GET `/users/@me/export`: gets latest data export. `*` required
|
|
||||||
- [ ] GET `/users/@me/flags`: get all the user's flags. `identify` required
|
|
||||||
- [ ] POST `/users/@me/flags`: creates a new flag. `user.update` required
|
|
||||||
- [ ] PATCH `/users/@me/flags/{id}`: updates an existing flag. `user.update` required
|
|
||||||
- [ ] DELETE `/users/@me/flags/{id}`: deletes a user flag. `user.update` required
|
|
||||||
- [ ] POST `/users/@me/reroll`: rerolls a user's short ID. `user.update` required
|
|
||||||
|
|
||||||
## Members
|
|
||||||
|
|
||||||
- [ ] GET `/users/{userRef}/members`: gets list of a user's members.
|
|
||||||
if the user's member list is hidden,
|
|
||||||
and it is not the authenticated user (or the token doesn't have the `member.read` scope)
|
|
||||||
returns an empty array.
|
|
||||||
- [ ] GET `/users/{userRef}/members/{memberRef}`: gets a single member.
|
|
||||||
will always return a member if it exists, even if the member is unlisted.
|
|
||||||
- [ ] POST `/users/@me/members`: creates a new member. `member.create` required
|
|
||||||
- [ ] PATCH `/users/@me/members/{memberRef}`: edits a member. `member.update` required
|
|
||||||
- [ ] DELETE `/users/@me/members/{memberRef}`: deletes a member. `member.update` required
|
|
||||||
- [ ] POST `/users/@me/members/{memberRef}/reroll`: rerolls a member's short ID. `member.update` required.
|
|
||||||
-
|
|
|
@ -10,7 +10,6 @@ public class Config
|
||||||
public string MediaBaseUrl { get; set; } = null!;
|
public string MediaBaseUrl { get; set; } = null!;
|
||||||
|
|
||||||
public string Address => $"http://{Host}:{Port}";
|
public string Address => $"http://{Host}:{Port}";
|
||||||
public string? MetricsAddress => Logging.MetricsPort != null ? $"http://{Host}:{Logging.MetricsPort}" : null;
|
|
||||||
|
|
||||||
public LoggingConfig Logging { get; init; } = new();
|
public LoggingConfig Logging { get; init; } = new();
|
||||||
public DatabaseConfig Database { get; init; } = new();
|
public DatabaseConfig Database { get; init; } = new();
|
||||||
|
@ -27,8 +26,6 @@ public class Config
|
||||||
public string? SentryUrl { get; init; }
|
public string? SentryUrl { get; init; }
|
||||||
public bool SentryTracing { get; init; } = false;
|
public bool SentryTracing { get; init; } = false;
|
||||||
public double SentryTracesSampleRate { get; init; } = 0.0;
|
public double SentryTracesSampleRate { get; init; } = 0.0;
|
||||||
public bool LogQueries { get; init; } = false;
|
|
||||||
public int? MetricsPort { get; init; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DatabaseConfig
|
public class DatabaseConfig
|
||||||
|
|
|
@ -1,38 +1,29 @@
|
||||||
using Foxnouns.Backend.Database;
|
using Foxnouns.Backend.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using NodaTime;
|
|
||||||
|
|
||||||
namespace Foxnouns.Backend.Controllers;
|
namespace Foxnouns.Backend.Controllers;
|
||||||
|
|
||||||
[Route("/api/v2/meta")]
|
[Route("/api/v2/meta")]
|
||||||
public class MetaController(DatabaseContext db, IClock clock) : ApiControllerBase
|
public class MetaController(DatabaseContext db) : ApiControllerBase
|
||||||
{
|
{
|
||||||
private const string Repository = "https://codeberg.org/pronounscc/pronouns.cc";
|
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(MetaResponse))]
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(MetaResponse))]
|
||||||
public async Task<IActionResult> GetMeta()
|
public async Task<IActionResult> GetMeta()
|
||||||
{
|
{
|
||||||
var now = clock.GetCurrentInstant();
|
var userCount = await db.Users.CountAsync();
|
||||||
var users = await db.Users.Select(u => u.LastActive).ToListAsync();
|
|
||||||
var memberCount = await db.Members.CountAsync();
|
var memberCount = await db.Members.CountAsync();
|
||||||
|
|
||||||
return Ok(new MetaResponse(
|
return Ok(new MetaResponse(
|
||||||
Repository, BuildInfo.Version, BuildInfo.Hash, memberCount,
|
BuildInfo.Version, BuildInfo.Hash, memberCount,
|
||||||
new UserInfo(
|
new UserInfo(userCount, 0, 0, 0))
|
||||||
users.Count,
|
|
||||||
users.Count(i => i > now - Duration.FromDays(30)),
|
|
||||||
users.Count(i => i > now - Duration.FromDays(7)),
|
|
||||||
users.Count(i => i > now - Duration.FromDays(1))
|
|
||||||
))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("/api/v2/coffee")]
|
[HttpGet("/api/v2/coffee")]
|
||||||
public IActionResult BrewCoffee() => Problem("Sorry, I'm a teapot!", statusCode: StatusCodes.Status418ImATeapot);
|
public IActionResult BrewCoffee() => Problem("Sorry, I'm a teapot!", statusCode: StatusCodes.Status418ImATeapot);
|
||||||
|
|
||||||
private record MetaResponse(string Repository, string Version, string Hash, int Members, UserInfo Users);
|
private record MetaResponse(string Version, string Hash, int Members, UserInfo Users);
|
||||||
|
|
||||||
private record UserInfo(int Total, int ActiveMonth, int ActiveWeek, int ActiveDay);
|
private record UserInfo(int Total, int ActiveMonth, int ActiveWeek, int ActiveDay);
|
||||||
}
|
}
|
|
@ -10,7 +10,6 @@ namespace Foxnouns.Backend.Database;
|
||||||
public class DatabaseContext : DbContext
|
public class DatabaseContext : DbContext
|
||||||
{
|
{
|
||||||
private readonly NpgsqlDataSource _dataSource;
|
private readonly NpgsqlDataSource _dataSource;
|
||||||
private readonly ILoggerFactory? _loggerFactory;
|
|
||||||
|
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
public DbSet<Member> Members { get; set; }
|
public DbSet<Member> Members { get; set; }
|
||||||
|
@ -20,7 +19,7 @@ public class DatabaseContext : DbContext
|
||||||
public DbSet<Application> Applications { get; set; }
|
public DbSet<Application> Applications { get; set; }
|
||||||
public DbSet<TemporaryKey> TemporaryKeys { get; set; }
|
public DbSet<TemporaryKey> TemporaryKeys { get; set; }
|
||||||
|
|
||||||
public DatabaseContext(Config config, ILoggerFactory? loggerFactory)
|
public DatabaseContext(Config config)
|
||||||
{
|
{
|
||||||
var connString = new NpgsqlConnectionStringBuilder(config.Database.Url)
|
var connString = new NpgsqlConnectionStringBuilder(config.Database.Url)
|
||||||
{
|
{
|
||||||
|
@ -31,15 +30,13 @@ public class DatabaseContext : DbContext
|
||||||
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString);
|
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString);
|
||||||
dataSourceBuilder.UseNodaTime();
|
dataSourceBuilder.UseNodaTime();
|
||||||
_dataSource = dataSourceBuilder.Build();
|
_dataSource = dataSourceBuilder.Build();
|
||||||
_loggerFactory = loggerFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
=> optionsBuilder
|
=> optionsBuilder
|
||||||
.ConfigureWarnings(c => c.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning))
|
.ConfigureWarnings(c => c.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning))
|
||||||
.UseNpgsql(_dataSource, o => o.UseNodaTime())
|
.UseNpgsql(_dataSource, o => o.UseNodaTime())
|
||||||
.UseSnakeCaseNamingConvention()
|
.UseSnakeCaseNamingConvention();
|
||||||
.UseLoggerFactory(_loggerFactory);
|
|
||||||
|
|
||||||
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
|
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
|
||||||
{
|
{
|
||||||
|
@ -76,6 +73,6 @@ public class DesignTimeDatabaseContextFactory : IDesignTimeDbContextFactory<Data
|
||||||
// Get the configuration as our config class
|
// Get the configuration as our config class
|
||||||
.Get<Config>() ?? new();
|
.Get<Config>() ?? new();
|
||||||
|
|
||||||
return new DatabaseContext(config, null);
|
return new DatabaseContext(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,13 +15,11 @@ public static class DatabaseQueryExtensions
|
||||||
if (Snowflake.TryParse(userRef, out var snowflake))
|
if (Snowflake.TryParse(userRef, out var snowflake))
|
||||||
{
|
{
|
||||||
user = await context.Users
|
user = await context.Users
|
||||||
.Where(u => !u.Deleted)
|
|
||||||
.FirstOrDefaultAsync(u => u.Id == snowflake);
|
.FirstOrDefaultAsync(u => u.Id == snowflake);
|
||||||
if (user != null) return user;
|
if (user != null) return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
user = await context.Users
|
user = await context.Users
|
||||||
.Where(u => !u.Deleted)
|
|
||||||
.FirstOrDefaultAsync(u => u.Username == userRef);
|
.FirstOrDefaultAsync(u => u.Username == userRef);
|
||||||
if (user != null) return user;
|
if (user != null) return user;
|
||||||
throw new ApiError.NotFound("No user with that ID or username found.", code: ErrorCode.UserNotFound);
|
throw new ApiError.NotFound("No user with that ID or username found.", code: ErrorCode.UserNotFound);
|
||||||
|
@ -30,7 +28,6 @@ public static class DatabaseQueryExtensions
|
||||||
public static async Task<User> ResolveUserAsync(this DatabaseContext context, Snowflake id)
|
public static async Task<User> ResolveUserAsync(this DatabaseContext context, Snowflake id)
|
||||||
{
|
{
|
||||||
var user = await context.Users
|
var user = await context.Users
|
||||||
.Where(u => !u.Deleted)
|
|
||||||
.FirstOrDefaultAsync(u => u.Id == id);
|
.FirstOrDefaultAsync(u => u.Id == id);
|
||||||
if (user != null) return user;
|
if (user != null) return user;
|
||||||
throw new ApiError.NotFound("No user with that ID found.", code: ErrorCode.UserNotFound);
|
throw new ApiError.NotFound("No user with that ID found.", code: ErrorCode.UserNotFound);
|
||||||
|
@ -40,7 +37,6 @@ public static class DatabaseQueryExtensions
|
||||||
{
|
{
|
||||||
var member = await context.Members
|
var member = await context.Members
|
||||||
.Include(m => m.User)
|
.Include(m => m.User)
|
||||||
.Where(m => !m.User.Deleted)
|
|
||||||
.FirstOrDefaultAsync(m => m.Id == id);
|
.FirstOrDefaultAsync(m => m.Id == id);
|
||||||
if (member != null) return member;
|
if (member != null) return member;
|
||||||
throw new ApiError.NotFound("No member with that ID found.", code: ErrorCode.MemberNotFound);
|
throw new ApiError.NotFound("No member with that ID found.", code: ErrorCode.MemberNotFound);
|
||||||
|
@ -60,14 +56,12 @@ public static class DatabaseQueryExtensions
|
||||||
{
|
{
|
||||||
member = await context.Members
|
member = await context.Members
|
||||||
.Include(m => m.User)
|
.Include(m => m.User)
|
||||||
.Where(m => !m.User.Deleted)
|
|
||||||
.FirstOrDefaultAsync(m => m.Id == snowflake && m.UserId == userId);
|
.FirstOrDefaultAsync(m => m.Id == snowflake && m.UserId == userId);
|
||||||
if (member != null) return member;
|
if (member != null) return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
member = await context.Members
|
member = await context.Members
|
||||||
.Include(m => m.User)
|
.Include(m => m.User)
|
||||||
.Where(m => !m.User.Deleted)
|
|
||||||
.FirstOrDefaultAsync(m => m.Name == memberRef && m.UserId == userId);
|
.FirstOrDefaultAsync(m => m.Name == memberRef && m.UserId == userId);
|
||||||
if (member != null) return member;
|
if (member != null) return member;
|
||||||
throw new ApiError.NotFound("No member with that ID or name found.", code: ErrorCode.MemberNotFound);
|
throw new ApiError.NotFound("No member with that ID or name found.", code: ErrorCode.MemberNotFound);
|
||||||
|
|
|
@ -1,515 +0,0 @@
|
||||||
// <auto-generated />
|
|
||||||
using Foxnouns.Backend.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using NodaTime;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Foxnouns.Backend.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DatabaseContext))]
|
|
||||||
[Migration("20240712233806_AddUserLastActive")]
|
|
||||||
partial class AddUserLastActive
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "8.0.5")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Application", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<string>("ClientId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("client_id");
|
|
||||||
|
|
||||||
b.Property<string>("ClientSecret")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("client_secret");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("name");
|
|
||||||
|
|
||||||
b.Property<string[]>("RedirectUris")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("redirect_uris");
|
|
||||||
|
|
||||||
b.Property<string[]>("Scopes")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("scopes");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_applications");
|
|
||||||
|
|
||||||
b.ToTable("applications", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.AuthMethod", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<int>("AuthType")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("auth_type");
|
|
||||||
|
|
||||||
b.Property<long?>("FediverseApplicationId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("fediverse_application_id");
|
|
||||||
|
|
||||||
b.Property<string>("RemoteId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("remote_id");
|
|
||||||
|
|
||||||
b.Property<string>("RemoteUsername")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("remote_username");
|
|
||||||
|
|
||||||
b.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("user_id");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_auth_methods");
|
|
||||||
|
|
||||||
b.HasIndex("FediverseApplicationId")
|
|
||||||
.HasDatabaseName("ix_auth_methods_fediverse_application_id");
|
|
||||||
|
|
||||||
b.HasIndex("UserId")
|
|
||||||
.HasDatabaseName("ix_auth_methods_user_id");
|
|
||||||
|
|
||||||
b.ToTable("auth_methods", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.FediverseApplication", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<string>("ClientId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("client_id");
|
|
||||||
|
|
||||||
b.Property<string>("ClientSecret")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("client_secret");
|
|
||||||
|
|
||||||
b.Property<string>("Domain")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("domain");
|
|
||||||
|
|
||||||
b.Property<int>("InstanceType")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("instance_type");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_fediverse_applications");
|
|
||||||
|
|
||||||
b.ToTable("fediverse_applications", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Member", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<string>("Avatar")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("avatar");
|
|
||||||
|
|
||||||
b.Property<string>("Bio")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("bio");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("display_name");
|
|
||||||
|
|
||||||
b.Property<string[]>("Links")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("links");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("name");
|
|
||||||
|
|
||||||
b.Property<bool>("Unlisted")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("unlisted");
|
|
||||||
|
|
||||||
b.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("user_id");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_members");
|
|
||||||
|
|
||||||
b.HasIndex("UserId", "Name")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("ix_members_user_id_name");
|
|
||||||
|
|
||||||
b.ToTable("members", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.TemporaryKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<Instant>("Expires")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("expires");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("key");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("value");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_temporary_keys");
|
|
||||||
|
|
||||||
b.HasIndex("Key")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("ix_temporary_keys_key");
|
|
||||||
|
|
||||||
b.ToTable("temporary_keys", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Token", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<long>("ApplicationId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("application_id");
|
|
||||||
|
|
||||||
b.Property<Instant>("ExpiresAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("expires_at");
|
|
||||||
|
|
||||||
b.Property<byte[]>("Hash")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("hash");
|
|
||||||
|
|
||||||
b.Property<bool>("ManuallyExpired")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("manually_expired");
|
|
||||||
|
|
||||||
b.Property<string[]>("Scopes")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("scopes");
|
|
||||||
|
|
||||||
b.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("user_id");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_tokens");
|
|
||||||
|
|
||||||
b.HasIndex("ApplicationId")
|
|
||||||
.HasDatabaseName("ix_tokens_application_id");
|
|
||||||
|
|
||||||
b.HasIndex("UserId")
|
|
||||||
.HasDatabaseName("ix_tokens_user_id");
|
|
||||||
|
|
||||||
b.ToTable("tokens", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<string>("Avatar")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("avatar");
|
|
||||||
|
|
||||||
b.Property<string>("Bio")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("bio");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("display_name");
|
|
||||||
|
|
||||||
b.Property<Instant>("LastActive")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("last_active");
|
|
||||||
|
|
||||||
b.Property<string[]>("Links")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("links");
|
|
||||||
|
|
||||||
b.Property<bool>("ListHidden")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("list_hidden");
|
|
||||||
|
|
||||||
b.Property<string>("MemberTitle")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("member_title");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("password");
|
|
||||||
|
|
||||||
b.Property<int>("Role")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("role");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("username");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_users");
|
|
||||||
|
|
||||||
b.HasIndex("Username")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("ix_users_username");
|
|
||||||
|
|
||||||
b.ToTable("users", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.AuthMethod", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.FediverseApplication", "FediverseApplication")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("FediverseApplicationId")
|
|
||||||
.HasConstraintName("fk_auth_methods_fediverse_applications_fediverse_application_id");
|
|
||||||
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.User", "User")
|
|
||||||
.WithMany("AuthMethods")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_auth_methods_users_user_id");
|
|
||||||
|
|
||||||
b.Navigation("FediverseApplication");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Member", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.User", "User")
|
|
||||||
.WithMany("Members")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_members_users_user_id");
|
|
||||||
|
|
||||||
b.OwnsOne("System.Collections.Generic.List<Foxnouns.Backend.Database.Models.Field>", "Fields", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("MemberId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("MemberId");
|
|
||||||
|
|
||||||
b1.ToTable("members");
|
|
||||||
|
|
||||||
b1.ToJson("fields");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("MemberId")
|
|
||||||
.HasConstraintName("fk_members_members_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.OwnsOne("System.Collections.Generic.List<Foxnouns.Backend.Database.Models.FieldEntry>", "Names", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("MemberId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("MemberId");
|
|
||||||
|
|
||||||
b1.ToTable("members");
|
|
||||||
|
|
||||||
b1.ToJson("names");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("MemberId")
|
|
||||||
.HasConstraintName("fk_members_members_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.OwnsOne("System.Collections.Generic.List<Foxnouns.Backend.Database.Models.Pronoun>", "Pronouns", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("MemberId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("MemberId");
|
|
||||||
|
|
||||||
b1.ToTable("members");
|
|
||||||
|
|
||||||
b1.ToJson("pronouns");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("MemberId")
|
|
||||||
.HasConstraintName("fk_members_members_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.Navigation("Fields")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Names")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Pronouns")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Token", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.Application", "Application")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("ApplicationId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_tokens_applications_application_id");
|
|
||||||
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.User", "User")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_tokens_users_user_id");
|
|
||||||
|
|
||||||
b.Navigation("Application");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Fields#List", "Fields", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("UserId")
|
|
||||||
.HasName("pk_users");
|
|
||||||
|
|
||||||
b1.ToTable("users");
|
|
||||||
|
|
||||||
b1.ToJson("fields");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.HasConstraintName("fk_users_users_user_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Names#List", "Names", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("UserId")
|
|
||||||
.HasName("pk_users");
|
|
||||||
|
|
||||||
b1.ToTable("users");
|
|
||||||
|
|
||||||
b1.ToJson("names");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.HasConstraintName("fk_users_users_user_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Pronouns#List", "Pronouns", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("UserId")
|
|
||||||
.HasName("pk_users");
|
|
||||||
|
|
||||||
b1.ToTable("users");
|
|
||||||
|
|
||||||
b1.ToJson("pronouns");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.HasConstraintName("fk_users_users_user_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.Navigation("Fields")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Names")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Pronouns")
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("AuthMethods");
|
|
||||||
|
|
||||||
b.Navigation("Members");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using NodaTime;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Foxnouns.Backend.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddUserLastActive : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<Instant>(
|
|
||||||
name: "last_active",
|
|
||||||
table: "users",
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: false,
|
|
||||||
defaultValueSql: "now()");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "last_active",
|
|
||||||
table: "users");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,528 +0,0 @@
|
||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Foxnouns.Backend.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using NodaTime;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Foxnouns.Backend.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DatabaseContext))]
|
|
||||||
[Migration("20240713000719_AddDeleted")]
|
|
||||||
partial class AddDeleted
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "8.0.5")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Application", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<string>("ClientId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("client_id");
|
|
||||||
|
|
||||||
b.Property<string>("ClientSecret")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("client_secret");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("name");
|
|
||||||
|
|
||||||
b.Property<string[]>("RedirectUris")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("redirect_uris");
|
|
||||||
|
|
||||||
b.Property<string[]>("Scopes")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("scopes");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_applications");
|
|
||||||
|
|
||||||
b.ToTable("applications", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.AuthMethod", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<int>("AuthType")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("auth_type");
|
|
||||||
|
|
||||||
b.Property<long?>("FediverseApplicationId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("fediverse_application_id");
|
|
||||||
|
|
||||||
b.Property<string>("RemoteId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("remote_id");
|
|
||||||
|
|
||||||
b.Property<string>("RemoteUsername")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("remote_username");
|
|
||||||
|
|
||||||
b.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("user_id");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_auth_methods");
|
|
||||||
|
|
||||||
b.HasIndex("FediverseApplicationId")
|
|
||||||
.HasDatabaseName("ix_auth_methods_fediverse_application_id");
|
|
||||||
|
|
||||||
b.HasIndex("UserId")
|
|
||||||
.HasDatabaseName("ix_auth_methods_user_id");
|
|
||||||
|
|
||||||
b.ToTable("auth_methods", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.FediverseApplication", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<string>("ClientId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("client_id");
|
|
||||||
|
|
||||||
b.Property<string>("ClientSecret")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("client_secret");
|
|
||||||
|
|
||||||
b.Property<string>("Domain")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("domain");
|
|
||||||
|
|
||||||
b.Property<int>("InstanceType")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("instance_type");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_fediverse_applications");
|
|
||||||
|
|
||||||
b.ToTable("fediverse_applications", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Member", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<string>("Avatar")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("avatar");
|
|
||||||
|
|
||||||
b.Property<string>("Bio")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("bio");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("display_name");
|
|
||||||
|
|
||||||
b.Property<string[]>("Links")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("links");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("name");
|
|
||||||
|
|
||||||
b.Property<bool>("Unlisted")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("unlisted");
|
|
||||||
|
|
||||||
b.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("user_id");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_members");
|
|
||||||
|
|
||||||
b.HasIndex("UserId", "Name")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("ix_members_user_id_name");
|
|
||||||
|
|
||||||
b.ToTable("members", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.TemporaryKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<Instant>("Expires")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("expires");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("key");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("value");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_temporary_keys");
|
|
||||||
|
|
||||||
b.HasIndex("Key")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("ix_temporary_keys_key");
|
|
||||||
|
|
||||||
b.ToTable("temporary_keys", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Token", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<long>("ApplicationId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("application_id");
|
|
||||||
|
|
||||||
b.Property<Instant>("ExpiresAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("expires_at");
|
|
||||||
|
|
||||||
b.Property<byte[]>("Hash")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("hash");
|
|
||||||
|
|
||||||
b.Property<bool>("ManuallyExpired")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("manually_expired");
|
|
||||||
|
|
||||||
b.Property<string[]>("Scopes")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("scopes");
|
|
||||||
|
|
||||||
b.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("user_id");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_tokens");
|
|
||||||
|
|
||||||
b.HasIndex("ApplicationId")
|
|
||||||
.HasDatabaseName("ix_tokens_application_id");
|
|
||||||
|
|
||||||
b.HasIndex("UserId")
|
|
||||||
.HasDatabaseName("ix_tokens_user_id");
|
|
||||||
|
|
||||||
b.ToTable("tokens", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<string>("Avatar")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("avatar");
|
|
||||||
|
|
||||||
b.Property<string>("Bio")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("bio");
|
|
||||||
|
|
||||||
b.Property<bool>("Deleted")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("deleted");
|
|
||||||
|
|
||||||
b.Property<Instant?>("DeletedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("deleted_at");
|
|
||||||
|
|
||||||
b.Property<long?>("DeletedBy")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("deleted_by");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("display_name");
|
|
||||||
|
|
||||||
b.Property<Instant>("LastActive")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("last_active");
|
|
||||||
|
|
||||||
b.Property<string[]>("Links")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("links");
|
|
||||||
|
|
||||||
b.Property<bool>("ListHidden")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("list_hidden");
|
|
||||||
|
|
||||||
b.Property<string>("MemberTitle")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("member_title");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("password");
|
|
||||||
|
|
||||||
b.Property<int>("Role")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("role");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("username");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk_users");
|
|
||||||
|
|
||||||
b.HasIndex("Username")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("ix_users_username");
|
|
||||||
|
|
||||||
b.ToTable("users", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.AuthMethod", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.FediverseApplication", "FediverseApplication")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("FediverseApplicationId")
|
|
||||||
.HasConstraintName("fk_auth_methods_fediverse_applications_fediverse_application_id");
|
|
||||||
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.User", "User")
|
|
||||||
.WithMany("AuthMethods")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_auth_methods_users_user_id");
|
|
||||||
|
|
||||||
b.Navigation("FediverseApplication");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Member", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.User", "User")
|
|
||||||
.WithMany("Members")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_members_users_user_id");
|
|
||||||
|
|
||||||
b.OwnsOne("System.Collections.Generic.List<Foxnouns.Backend.Database.Models.Field>", "Fields", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("MemberId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("MemberId");
|
|
||||||
|
|
||||||
b1.ToTable("members");
|
|
||||||
|
|
||||||
b1.ToJson("fields");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("MemberId")
|
|
||||||
.HasConstraintName("fk_members_members_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.OwnsOne("System.Collections.Generic.List<Foxnouns.Backend.Database.Models.FieldEntry>", "Names", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("MemberId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("MemberId");
|
|
||||||
|
|
||||||
b1.ToTable("members");
|
|
||||||
|
|
||||||
b1.ToJson("names");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("MemberId")
|
|
||||||
.HasConstraintName("fk_members_members_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.OwnsOne("System.Collections.Generic.List<Foxnouns.Backend.Database.Models.Pronoun>", "Pronouns", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("MemberId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("MemberId");
|
|
||||||
|
|
||||||
b1.ToTable("members");
|
|
||||||
|
|
||||||
b1.ToJson("pronouns");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("MemberId")
|
|
||||||
.HasConstraintName("fk_members_members_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.Navigation("Fields")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Names")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Pronouns")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Token", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.Application", "Application")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("ApplicationId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_tokens_applications_application_id");
|
|
||||||
|
|
||||||
b.HasOne("Foxnouns.Backend.Database.Models.User", "User")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_tokens_users_user_id");
|
|
||||||
|
|
||||||
b.Navigation("Application");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Fields#List", "Fields", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("UserId")
|
|
||||||
.HasName("pk_users");
|
|
||||||
|
|
||||||
b1.ToTable("users");
|
|
||||||
|
|
||||||
b1.ToJson("fields");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.HasConstraintName("fk_users_users_user_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Names#List", "Names", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("UserId")
|
|
||||||
.HasName("pk_users");
|
|
||||||
|
|
||||||
b1.ToTable("users");
|
|
||||||
|
|
||||||
b1.ToJson("names");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.HasConstraintName("fk_users_users_user_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Pronouns#List", "Pronouns", b1 =>
|
|
||||||
{
|
|
||||||
b1.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b1.Property<int>("Capacity")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("UserId")
|
|
||||||
.HasName("pk_users");
|
|
||||||
|
|
||||||
b1.ToTable("users");
|
|
||||||
|
|
||||||
b1.ToJson("pronouns");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.HasConstraintName("fk_users_users_user_id");
|
|
||||||
});
|
|
||||||
|
|
||||||
b.Navigation("Fields")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Names")
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Pronouns")
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("AuthMethods");
|
|
||||||
|
|
||||||
b.Navigation("Members");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using NodaTime;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Foxnouns.Backend.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddDeleted : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<bool>(
|
|
||||||
name: "deleted",
|
|
||||||
table: "users",
|
|
||||||
type: "boolean",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: false);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<Instant>(
|
|
||||||
name: "deleted_at",
|
|
||||||
table: "users",
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<long>(
|
|
||||||
name: "deleted_by",
|
|
||||||
table: "users",
|
|
||||||
type: "bigint",
|
|
||||||
nullable: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "deleted",
|
|
||||||
table: "users");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "deleted_at",
|
|
||||||
table: "users");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "deleted_by",
|
|
||||||
table: "users");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -267,26 +267,10 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
.HasColumnType("text")
|
.HasColumnType("text")
|
||||||
.HasColumnName("bio");
|
.HasColumnName("bio");
|
||||||
|
|
||||||
b.Property<bool>("Deleted")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("deleted");
|
|
||||||
|
|
||||||
b.Property<Instant?>("DeletedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("deleted_at");
|
|
||||||
|
|
||||||
b.Property<long?>("DeletedBy")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("deleted_by");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
b.Property<string>("DisplayName")
|
||||||
.HasColumnType("text")
|
.HasColumnType("text")
|
||||||
.HasColumnName("display_name");
|
.HasColumnName("display_name");
|
||||||
|
|
||||||
b.Property<Instant>("LastActive")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("last_active");
|
|
||||||
|
|
||||||
b.Property<string[]>("Links")
|
b.Property<string[]>("Links")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text[]")
|
.HasColumnType("text[]")
|
||||||
|
@ -351,7 +335,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasConstraintName("fk_members_users_user_id");
|
.HasConstraintName("fk_members_users_user_id");
|
||||||
|
|
||||||
b.OwnsOne("System.Collections.Generic.List<Foxnouns.Backend.Database.Models.Field>", "Fields", b1 =>
|
b.OwnsOne("Foxnouns.Backend.Database.Models.Member.Fields#System.Collections.Generic.List<Foxnouns.Backend.Database.Models.Field>", "Fields", b1 =>
|
||||||
{
|
{
|
||||||
b1.Property<long>("MemberId")
|
b1.Property<long>("MemberId")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
@ -361,7 +345,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
|
|
||||||
b1.HasKey("MemberId");
|
b1.HasKey("MemberId");
|
||||||
|
|
||||||
b1.ToTable("members");
|
b1.ToTable("members", (string)null);
|
||||||
|
|
||||||
b1.ToJson("fields");
|
b1.ToJson("fields");
|
||||||
|
|
||||||
|
@ -370,7 +354,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
.HasConstraintName("fk_members_members_id");
|
.HasConstraintName("fk_members_members_id");
|
||||||
});
|
});
|
||||||
|
|
||||||
b.OwnsOne("System.Collections.Generic.List<Foxnouns.Backend.Database.Models.FieldEntry>", "Names", b1 =>
|
b.OwnsOne("Foxnouns.Backend.Database.Models.Member.Names#System.Collections.Generic.List<Foxnouns.Backend.Database.Models.FieldEntry>", "Names", b1 =>
|
||||||
{
|
{
|
||||||
b1.Property<long>("MemberId")
|
b1.Property<long>("MemberId")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
@ -380,7 +364,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
|
|
||||||
b1.HasKey("MemberId");
|
b1.HasKey("MemberId");
|
||||||
|
|
||||||
b1.ToTable("members");
|
b1.ToTable("members", (string)null);
|
||||||
|
|
||||||
b1.ToJson("names");
|
b1.ToJson("names");
|
||||||
|
|
||||||
|
@ -389,7 +373,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
.HasConstraintName("fk_members_members_id");
|
.HasConstraintName("fk_members_members_id");
|
||||||
});
|
});
|
||||||
|
|
||||||
b.OwnsOne("System.Collections.Generic.List<Foxnouns.Backend.Database.Models.Pronoun>", "Pronouns", b1 =>
|
b.OwnsOne("Foxnouns.Backend.Database.Models.Member.Pronouns#System.Collections.Generic.List<Foxnouns.Backend.Database.Models.Pronoun>", "Pronouns", b1 =>
|
||||||
{
|
{
|
||||||
b1.Property<long>("MemberId")
|
b1.Property<long>("MemberId")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
@ -399,7 +383,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
|
|
||||||
b1.HasKey("MemberId");
|
b1.HasKey("MemberId");
|
||||||
|
|
||||||
b1.ToTable("members");
|
b1.ToTable("members", (string)null);
|
||||||
|
|
||||||
b1.ToJson("pronouns");
|
b1.ToJson("pronouns");
|
||||||
|
|
||||||
|
@ -443,7 +427,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
|
|
||||||
modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b =>
|
modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b =>
|
||||||
{
|
{
|
||||||
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Fields#List", "Fields", b1 =>
|
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Fields#Foxnouns.Backend.Database.Models.User.Fields#List", "Fields", b1 =>
|
||||||
{
|
{
|
||||||
b1.Property<long>("UserId")
|
b1.Property<long>("UserId")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
@ -454,7 +438,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
b1.HasKey("UserId")
|
b1.HasKey("UserId")
|
||||||
.HasName("pk_users");
|
.HasName("pk_users");
|
||||||
|
|
||||||
b1.ToTable("users");
|
b1.ToTable("users", (string)null);
|
||||||
|
|
||||||
b1.ToJson("fields");
|
b1.ToJson("fields");
|
||||||
|
|
||||||
|
@ -463,7 +447,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
.HasConstraintName("fk_users_users_user_id");
|
.HasConstraintName("fk_users_users_user_id");
|
||||||
});
|
});
|
||||||
|
|
||||||
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Names#List", "Names", b1 =>
|
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Names#Foxnouns.Backend.Database.Models.User.Names#List", "Names", b1 =>
|
||||||
{
|
{
|
||||||
b1.Property<long>("UserId")
|
b1.Property<long>("UserId")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
@ -474,7 +458,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
b1.HasKey("UserId")
|
b1.HasKey("UserId")
|
||||||
.HasName("pk_users");
|
.HasName("pk_users");
|
||||||
|
|
||||||
b1.ToTable("users");
|
b1.ToTable("users", (string)null);
|
||||||
|
|
||||||
b1.ToJson("names");
|
b1.ToJson("names");
|
||||||
|
|
||||||
|
@ -483,7 +467,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
.HasConstraintName("fk_users_users_user_id");
|
.HasConstraintName("fk_users_users_user_id");
|
||||||
});
|
});
|
||||||
|
|
||||||
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Pronouns#List", "Pronouns", b1 =>
|
b.OwnsOne("Foxnouns.Backend.Database.Models.User.Pronouns#Foxnouns.Backend.Database.Models.User.Pronouns#List", "Pronouns", b1 =>
|
||||||
{
|
{
|
||||||
b1.Property<long>("UserId")
|
b1.Property<long>("UserId")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
@ -494,7 +478,7 @@ namespace Foxnouns.Backend.Database.Migrations
|
||||||
b1.HasKey("UserId")
|
b1.HasKey("UserId")
|
||||||
.HasName("pk_users");
|
.HasName("pk_users");
|
||||||
|
|
||||||
b1.ToTable("users");
|
b1.ToTable("users", (string)null);
|
||||||
|
|
||||||
b1.ToJson("pronouns");
|
b1.ToJson("pronouns");
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.Text.RegularExpressions;
|
||||||
using NodaTime;
|
|
||||||
|
|
||||||
namespace Foxnouns.Backend.Database.Models;
|
namespace Foxnouns.Backend.Database.Models;
|
||||||
|
|
||||||
|
@ -22,13 +21,6 @@ public class User : BaseModel
|
||||||
|
|
||||||
public List<Member> Members { get; } = [];
|
public List<Member> Members { get; } = [];
|
||||||
public List<AuthMethod> AuthMethods { get; } = [];
|
public List<AuthMethod> AuthMethods { get; } = [];
|
||||||
|
|
||||||
public required Instant LastActive { get; set; }
|
|
||||||
|
|
||||||
public bool Deleted { get; set; }
|
|
||||||
public Instant? DeletedAt { get; set; }
|
|
||||||
public Snowflake? DeletedBy { get; set; }
|
|
||||||
[NotMapped] public bool? SelfDelete => Deleted ? DeletedBy != null : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum UserRole
|
public enum UserRole
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
using App.Metrics;
|
|
||||||
using App.Metrics.AspNetCore;
|
|
||||||
using App.Metrics.Formatters.Prometheus;
|
|
||||||
using Foxnouns.Backend.Database;
|
using Foxnouns.Backend.Database;
|
||||||
using Foxnouns.Backend.Jobs;
|
using Foxnouns.Backend.Jobs;
|
||||||
using Foxnouns.Backend.Middleware;
|
using Foxnouns.Backend.Middleware;
|
||||||
|
@ -9,7 +6,6 @@ using Microsoft.EntityFrameworkCore;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using IClock = NodaTime.IClock;
|
|
||||||
|
|
||||||
namespace Foxnouns.Backend.Extensions;
|
namespace Foxnouns.Backend.Extensions;
|
||||||
|
|
||||||
|
@ -28,12 +24,9 @@ public static class WebApplicationExtensions
|
||||||
// ASP.NET's built in request logs are extremely verbose, so we use Serilog's instead.
|
// ASP.NET's built in request logs are extremely verbose, so we use Serilog's instead.
|
||||||
// Serilog doesn't disable the built-in logs, so we do it here.
|
// Serilog doesn't disable the built-in logs, so we do it here.
|
||||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||||
.MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command",
|
|
||||||
config.Logging.LogQueries ? LogEventLevel.Information : LogEventLevel.Warning)
|
|
||||||
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning)
|
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning)
|
||||||
.MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning)
|
.MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning)
|
||||||
.MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning)
|
.MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning)
|
||||||
.MinimumLevel.Override("Hangfire", LogEventLevel.Information)
|
|
||||||
.WriteTo.Console();
|
.WriteTo.Console();
|
||||||
|
|
||||||
if (config.Logging.SeqLogUrl != null)
|
if (config.Logging.SeqLogUrl != null)
|
||||||
|
@ -41,8 +34,10 @@ public static class WebApplicationExtensions
|
||||||
logCfg.WriteTo.Seq(config.Logging.SeqLogUrl, restrictedToMinimumLevel: LogEventLevel.Verbose);
|
logCfg.WriteTo.Seq(config.Logging.SeqLogUrl, restrictedToMinimumLevel: LogEventLevel.Verbose);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.Logger = logCfg.CreateLogger();
|
||||||
|
|
||||||
// AddSerilog doesn't seem to add an ILogger to the service collection, so add that manually.
|
// AddSerilog doesn't seem to add an ILogger to the service collection, so add that manually.
|
||||||
builder.Services.AddSerilog().AddSingleton(Log.Logger = logCfg.CreateLogger());
|
builder.Services.AddSerilog().AddSingleton(Log.Logger);
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
@ -57,36 +52,6 @@ public static class WebApplicationExtensions
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebApplicationBuilder AddMetrics(this WebApplicationBuilder builder)
|
|
||||||
{
|
|
||||||
var config = builder.Configuration.Get<Config>() ?? new();
|
|
||||||
var metrics = AppMetrics.CreateDefaultBuilder()
|
|
||||||
.OutputMetrics.AsPrometheusPlainText()
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
builder.Services.AddSingleton(metrics);
|
|
||||||
builder.Services.AddSingleton<IMetrics>(metrics);
|
|
||||||
|
|
||||||
builder.WebHost
|
|
||||||
.ConfigureMetrics(metrics)
|
|
||||||
.UseMetrics(opts =>
|
|
||||||
{
|
|
||||||
opts.EndpointOptions = options =>
|
|
||||||
{
|
|
||||||
// Metrics must listen on a separate port for security reasons. If no metrics port is set, disable the endpoint entirely.
|
|
||||||
options.MetricsEndpointEnabled = config.Logging.MetricsPort != null;
|
|
||||||
options.EnvironmentInfoEndpointEnabled = config.Logging.MetricsPort != null;
|
|
||||||
options.MetricsTextEndpointEnabled = false;
|
|
||||||
options.MetricsEndpointOutputFormatter = metrics.OutputMetricsFormatters
|
|
||||||
.OfType<MetricsPrometheusTextOutputFormatter>().First();
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.UseMetricsWebTracking()
|
|
||||||
.ConfigureAppMetricsHostingConfiguration(opts => { opts.AllEndpointsPort = config.Logging.MetricsPort; });
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IConfigurationBuilder AddConfiguration(this IConfigurationBuilder builder)
|
public static IConfigurationBuilder AddConfiguration(this IConfigurationBuilder builder)
|
||||||
{
|
{
|
||||||
var file = Environment.GetEnvironmentVariable("FOXNOUNS_CONFIG_FILE") ?? "config.ini";
|
var file = Environment.GetEnvironmentVariable("FOXNOUNS_CONFIG_FILE") ?? "config.ini";
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="App.Metrics" Version="4.3.0" />
|
|
||||||
<PackageReference Include="App.Metrics.AspNetCore.All" Version="4.3.0" />
|
|
||||||
<PackageReference Include="App.Metrics.Prometheus" Version="4.3.0" />
|
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3"/>
|
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3"/>
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
||||||
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
|
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace Foxnouns.Backend;
|
|
||||||
|
|
||||||
public static class Metrics
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -21,7 +21,7 @@ var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
var config = builder.AddConfiguration();
|
var config = builder.AddConfiguration();
|
||||||
|
|
||||||
builder.AddSerilog().AddMetrics();
|
builder.AddSerilog();
|
||||||
|
|
||||||
builder.WebHost
|
builder.WebHost
|
||||||
.UseSentry(opts =>
|
.UseSentry(opts =>
|
||||||
|
@ -75,9 +75,9 @@ builder.Services
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
builder.Services.AddHangfire(c => c.UseSentry().UseRedisStorage(config.Jobs.Redis, new RedisStorageOptions
|
builder.Services.AddHangfire(c => c.UseSentry().UseRedisStorage(config.Jobs.Redis, new RedisStorageOptions
|
||||||
{
|
{
|
||||||
Prefix = "foxnouns_"
|
Prefix = "foxnouns_"
|
||||||
}))
|
}))
|
||||||
.AddHangfireServer(options => { options.WorkerCount = config.Jobs.Workers; });
|
.AddHangfireServer(options => { options.WorkerCount = config.Jobs.Workers; });
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
@ -103,7 +103,6 @@ app.UseHangfireDashboard("/hangfire", new DashboardOptions
|
||||||
|
|
||||||
app.Urls.Clear();
|
app.Urls.Clear();
|
||||||
app.Urls.Add(config.Address);
|
app.Urls.Add(config.Address);
|
||||||
if (config.MetricsAddress != null) app.Urls.Add(config.MetricsAddress);
|
|
||||||
|
|
||||||
// Fire off the periodic tasks loop in the background
|
// Fire off the periodic tasks loop in the background
|
||||||
_ = new Timer(_ =>
|
_ = new Timer(_ =>
|
||||||
|
|
|
@ -8,7 +8,7 @@ using NodaTime;
|
||||||
|
|
||||||
namespace Foxnouns.Backend.Services;
|
namespace Foxnouns.Backend.Services;
|
||||||
|
|
||||||
public class AuthService(ILogger logger, IClock clock, DatabaseContext db, ISnowflakeGenerator snowflakeGenerator)
|
public class AuthService(ILogger logger, DatabaseContext db, ISnowflakeGenerator snowflakeGenerator)
|
||||||
{
|
{
|
||||||
private readonly PasswordHasher<User> _passwordHasher = new();
|
private readonly PasswordHasher<User> _passwordHasher = new();
|
||||||
|
|
||||||
|
@ -26,8 +26,7 @@ public class AuthService(ILogger logger, IClock clock, DatabaseContext db, ISnow
|
||||||
{
|
{
|
||||||
new AuthMethod
|
new AuthMethod
|
||||||
{ Id = snowflakeGenerator.GenerateSnowflake(), AuthType = AuthType.Email, RemoteId = email }
|
{ Id = snowflakeGenerator.GenerateSnowflake(), AuthType = AuthType.Email, RemoteId = email }
|
||||||
},
|
}
|
||||||
LastActive = clock.GetCurrentInstant()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
db.Add(user);
|
db.Add(user);
|
||||||
|
@ -60,8 +59,7 @@ public class AuthService(ILogger logger, IClock clock, DatabaseContext db, ISnow
|
||||||
Id = snowflakeGenerator.GenerateSnowflake(), AuthType = authType, RemoteId = remoteId,
|
Id = snowflakeGenerator.GenerateSnowflake(), AuthType = authType, RemoteId = remoteId,
|
||||||
RemoteUsername = remoteUsername, FediverseApplication = instance
|
RemoteUsername = remoteUsername, FediverseApplication = instance
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
LastActive = clock.GetCurrentInstant()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
db.Add(user);
|
db.Add(user);
|
||||||
|
|
|
@ -3,7 +3,6 @@ using Foxnouns.Backend.Database.Models;
|
||||||
using Foxnouns.Backend.Utils;
|
using Foxnouns.Backend.Utils;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NodaTime;
|
|
||||||
|
|
||||||
namespace Foxnouns.Backend.Services;
|
namespace Foxnouns.Backend.Services;
|
||||||
|
|
||||||
|
@ -15,12 +14,12 @@ public class UserRendererService(DatabaseContext db, MemberRendererService membe
|
||||||
bool renderAuthMethods = false)
|
bool renderAuthMethods = false)
|
||||||
{
|
{
|
||||||
var isSelfUser = selfUser?.Id == user.Id;
|
var isSelfUser = selfUser?.Id == user.Id;
|
||||||
var tokenCanReadHiddenMembers = token.HasScope("member.read") && isSelfUser;
|
var tokenCanReadHiddenMembers = token.HasScope("member.read");
|
||||||
var tokenPrivileged = token.HasScope("user.read_privileged") && isSelfUser;
|
var tokenCanReadAuth = token.HasScope("user.read_privileged");
|
||||||
|
|
||||||
renderMembers = renderMembers &&
|
renderMembers = renderMembers &&
|
||||||
(!user.ListHidden || tokenCanReadHiddenMembers);
|
(!user.ListHidden || (isSelfUser && tokenCanReadHiddenMembers));
|
||||||
renderAuthMethods = renderAuthMethods && tokenPrivileged;
|
renderAuthMethods = renderAuthMethods && isSelfUser && tokenCanReadAuth;
|
||||||
|
|
||||||
IEnumerable<Member> members =
|
IEnumerable<Member> members =
|
||||||
renderMembers ? await db.Members.Where(m => m.UserId == user.Id).ToListAsync() : [];
|
renderMembers ? await db.Members.Where(m => m.UserId == user.Id).ToListAsync() : [];
|
||||||
|
@ -35,8 +34,7 @@ public class UserRendererService(DatabaseContext db, MemberRendererService membe
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
return new UserResponse(
|
return new UserResponse(
|
||||||
user.Id, user.Username, user.DisplayName, user.Bio, user.MemberTitle, AvatarUrlFor(user), user.Links,
|
user.Id, user.Username, user.DisplayName, user.Bio, user.MemberTitle, AvatarUrlFor(user), user.Links, user.Names,
|
||||||
user.Names,
|
|
||||||
user.Pronouns, user.Fields,
|
user.Pronouns, user.Fields,
|
||||||
renderMembers ? members.Select(memberRendererService.RenderPartialMember) : null,
|
renderMembers ? members.Select(memberRendererService.RenderPartialMember) : null,
|
||||||
renderAuthMethods
|
renderAuthMethods
|
||||||
|
@ -44,8 +42,7 @@ public class UserRendererService(DatabaseContext db, MemberRendererService membe
|
||||||
a.Id, a.AuthType, a.RemoteId,
|
a.Id, a.AuthType, a.RemoteId,
|
||||||
a.RemoteUsername, a.FediverseApplication?.Domain
|
a.RemoteUsername, a.FediverseApplication?.Domain
|
||||||
))
|
))
|
||||||
: null,
|
: null
|
||||||
tokenPrivileged ? user.LastActive : null
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,9 +63,7 @@ public class UserRendererService(DatabaseContext db, MemberRendererService membe
|
||||||
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||||
IEnumerable<MemberRendererService.PartialMember>? Members,
|
IEnumerable<MemberRendererService.PartialMember>? Members,
|
||||||
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||||
IEnumerable<AuthenticationMethodResponse>? AuthMethods,
|
IEnumerable<AuthenticationMethodResponse>? AuthMethods
|
||||||
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
|
||||||
Instant? LastActive
|
|
||||||
);
|
);
|
||||||
|
|
||||||
public record AuthenticationMethodResponse(
|
public record AuthenticationMethodResponse(
|
||||||
|
|
|
@ -75,6 +75,7 @@ public static class AuthUtils
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine($"Error converting string: {e}");
|
||||||
bytes = [];
|
bytes = [];
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,11 @@ SentryUrl = https://examplePublicKey@o0.ingest.sentry.io/0
|
||||||
SentryTracing = true
|
SentryTracing = true
|
||||||
; Percentage of performance traces to send to Sentry (optional). Defaults to 0.0 (no traces at all)
|
; Percentage of performance traces to send to Sentry (optional). Defaults to 0.0 (no traces at all)
|
||||||
SentryTracesSampleRate = 1.0
|
SentryTracesSampleRate = 1.0
|
||||||
; Whether to log SQL queries. Note that this is very verbose. Defaults to false.
|
|
||||||
LogQueries = false
|
|
||||||
; The port the /metrics endpoint will listen on. If not set, metrics will be disabled.
|
|
||||||
MetricsPort = 5001
|
|
||||||
|
|
||||||
[Database]
|
[Database]
|
||||||
; The database URL in ADO.NET format.
|
; The database URL in ADO.NET format.
|
||||||
Url = "Host=localhost;Database=foxnouns_net;Username=pronouns;Password=pronouns"
|
Url = "Host=localhost;Database=foxnouns_net;Username=pronouns;Password=pronouns"
|
||||||
|
|
||||||
; The timeout for opening new connections. Defaults to 5.
|
; The timeout for opening new connections. Defaults to 5.
|
||||||
Timeout = 5
|
Timeout = 5
|
||||||
; The maximum number of open connections. Defaults to 50.
|
; The maximum number of open connections. Defaults to 50.
|
||||||
|
|
18
SCOPES.md
Normal file
18
SCOPES.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# List of API endpoints and scopes
|
||||||
|
|
||||||
|
## Scopes
|
||||||
|
|
||||||
|
- `identify`: `@me` will refer to token user (always granted)
|
||||||
|
- `user.read_privileged`: can read privileged information such as authentication methods
|
||||||
|
- `user.update`: can update the user's profile.
|
||||||
|
**cannot** update anything locked behind `user.read_privileged`
|
||||||
|
- `member.read`: can view member list if it's hidden and enumerate unlisted members
|
||||||
|
- `member.create`: can create new members
|
||||||
|
- `member.update`: can edit and delete members
|
||||||
|
|
||||||
|
## Users
|
||||||
|
|
||||||
|
- GET `/users/{userRef}`: `identify` required to use `@me` as user reference.
|
||||||
|
`user.read_privileged` required to view authentication methods.
|
||||||
|
`member.read` required to view unlisted members.
|
||||||
|
- PATCH `/users/@me`: `user.update` required.
|
Loading…
Add table
Add a link
Reference in a new issue