diff --git a/.idea/.idea.catalogger/.idea/sqldialects.xml b/.idea/.idea.catalogger/.idea/sqldialects.xml
index 6df4889..10eef95 100644
--- a/.idea/.idea.catalogger/.idea/sqldialects.xml
+++ b/.idea/.idea.catalogger/.idea/sqldialects.xml
@@ -1,6 +1,7 @@
+
\ No newline at end of file
diff --git a/Catalogger.Backend/Api/DiscordRequestService.cs b/Catalogger.Backend/Api/DiscordRequestService.cs
index c61a850..abde930 100644
--- a/Catalogger.Backend/Api/DiscordRequestService.cs
+++ b/Catalogger.Backend/Api/DiscordRequestService.cs
@@ -15,8 +15,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
-using Catalogger.Backend.Database;
using Catalogger.Backend.Database.Models;
+using Catalogger.Backend.Database.Repositories;
using NodaTime;
namespace Catalogger.Backend.Api;
@@ -28,7 +28,7 @@ public class DiscordRequestService
private readonly ApiCache _apiCache;
private readonly Config _config;
private readonly IClock _clock;
- private readonly DatabaseContext _db;
+ private readonly ApiTokenRepository _tokenRepository;
private static readonly JsonSerializerOptions JsonOptions =
new() { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
@@ -38,14 +38,14 @@ public class DiscordRequestService
ApiCache apiCache,
Config config,
IClock clock,
- DatabaseContext db
+ ApiTokenRepository tokenRepository
)
{
_logger = logger.ForContext();
_apiCache = apiCache;
_config = config;
_clock = clock;
- _db = db;
+ _tokenRepository = tokenRepository;
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add(
@@ -154,16 +154,13 @@ public class DiscordRequestService
var meUser = await GetMeAsync($"Bearer {token.AccessToken}");
var meGuilds = await GetGuildsAsync($"Bearer {token.AccessToken}");
- var apiToken = new ApiToken
- {
- DashboardToken = ApiUtils.RandomToken(64),
- UserId = meUser.Id,
- AccessToken = token.AccessToken,
- RefreshToken = token.RefreshToken,
- ExpiresAt = _clock.GetCurrentInstant() + Duration.FromSeconds(token.ExpiresIn),
- };
- _db.Add(apiToken);
- await _db.SaveChangesAsync(ct);
+ var apiToken = await _tokenRepository.CreateAsync(
+ ApiUtils.RandomToken(64),
+ meUser.Id,
+ token.AccessToken,
+ token.RefreshToken,
+ token.ExpiresIn
+ );
return (apiToken, meUser, meGuilds);
}
@@ -231,8 +228,12 @@ public class DiscordRequestService
token.RefreshToken = discordToken.RefreshToken;
token.ExpiresAt = _clock.GetCurrentInstant() + Duration.FromSeconds(discordToken.ExpiresIn);
- _db.Update(token);
- await _db.SaveChangesAsync();
+ await _tokenRepository.UpdateAsync(
+ token.Id,
+ discordToken.AccessToken,
+ discordToken.RefreshToken,
+ discordToken.ExpiresIn
+ );
}
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")]
diff --git a/Catalogger.Backend/Api/GuildsController.Ignores.cs b/Catalogger.Backend/Api/GuildsController.Ignores.cs
index d2b1ec4..ea9e647 100644
--- a/Catalogger.Backend/Api/GuildsController.Ignores.cs
+++ b/Catalogger.Backend/Api/GuildsController.Ignores.cs
@@ -13,7 +13,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-using Catalogger.Backend.Database.Queries;
using Microsoft.AspNetCore.Mvc;
using Remora.Discord.API;
using Remora.Discord.API.Abstractions.Objects;
@@ -26,7 +25,7 @@ public partial class GuildsController
public async Task AddIgnoredChannelAsync(string id, ulong channelId)
{
var (guildId, _) = await ParseGuildAsync(id);
- var guildConfig = await db.GetGuildAsync(guildId);
+ var guildConfig = await guildRepository.GetAsync(guildId);
if (guildConfig.Channels.IgnoredChannels.Contains(channelId))
return NoContent();
@@ -47,8 +46,7 @@ public partial class GuildsController
return NoContent();
guildConfig.Channels.IgnoredChannels.Add(channelId);
- db.Update(guildConfig);
- await db.SaveChangesAsync();
+ await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
return NoContent();
}
@@ -57,11 +55,10 @@ public partial class GuildsController
public async Task RemoveIgnoredChannelAsync(string id, ulong channelId)
{
var (guildId, _) = await ParseGuildAsync(id);
- var guildConfig = await db.GetGuildAsync(guildId);
+ var guildConfig = await guildRepository.GetAsync(guildId);
guildConfig.Channels.IgnoredChannels.Remove(channelId);
- db.Update(guildConfig);
- await db.SaveChangesAsync();
+ await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
return NoContent();
}
@@ -81,7 +78,7 @@ public partial class GuildsController
public async Task AddIgnoredUserAsync(string id, ulong userId)
{
var (guildId, _) = await ParseGuildAsync(id);
- var guildConfig = await db.GetGuildAsync(guildId);
+ var guildConfig = await guildRepository.GetAsync(guildId);
if (guildConfig.Channels.IgnoredUsers.Contains(userId))
return NoContent();
@@ -91,8 +88,7 @@ public partial class GuildsController
return NoContent();
guildConfig.Channels.IgnoredUsers.Add(userId);
- db.Update(guildConfig);
- await db.SaveChangesAsync();
+ await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
return NoContent();
}
@@ -101,11 +97,10 @@ public partial class GuildsController
public async Task RemoveIgnoredUserAsync(string id, ulong userId)
{
var (guildId, _) = await ParseGuildAsync(id);
- var guildConfig = await db.GetGuildAsync(guildId);
+ var guildConfig = await guildRepository.GetAsync(guildId);
guildConfig.Channels.IgnoredUsers.Remove(userId);
- db.Update(guildConfig);
- await db.SaveChangesAsync();
+ await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
return NoContent();
}
diff --git a/Catalogger.Backend/Api/GuildsController.Redirects.cs b/Catalogger.Backend/Api/GuildsController.Redirects.cs
index 9c9f11a..9ce84de 100644
--- a/Catalogger.Backend/Api/GuildsController.Redirects.cs
+++ b/Catalogger.Backend/Api/GuildsController.Redirects.cs
@@ -15,7 +15,6 @@
using System.Net;
using Catalogger.Backend.Api.Middleware;
-using Catalogger.Backend.Database.Queries;
using Microsoft.AspNetCore.Mvc;
using Remora.Discord.API.Abstractions.Objects;
@@ -31,7 +30,7 @@ public partial class GuildsController
{
var (guildId, _) = await ParseGuildAsync(id);
var guildChannels = channelCache.GuildChannels(guildId).ToList();
- var guildConfig = await db.GetGuildAsync(guildId.Value);
+ var guildConfig = await guildRepository.GetAsync(guildId);
Console.WriteLine($"Source: {req.Source}, target: {req.Target}");
@@ -62,8 +61,7 @@ public partial class GuildsController
);
guildConfig.Channels.Redirects[source.ID.Value] = target.ID.Value;
- db.Update(guildConfig);
- await db.SaveChangesAsync();
+ await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
return NoContent();
}
@@ -72,7 +70,7 @@ public partial class GuildsController
public async Task DeleteRedirectAsync(string id, ulong channelId)
{
var (guildId, _) = await ParseGuildAsync(id);
- var guildConfig = await db.GetGuildAsync(guildId.Value);
+ var guildConfig = await guildRepository.GetAsync(guildId);
if (!guildConfig.Channels.Redirects.ContainsKey(channelId))
throw new ApiError(
@@ -82,8 +80,7 @@ public partial class GuildsController
);
guildConfig.Channels.Redirects.Remove(channelId, out _);
- db.Update(guildConfig);
- await db.SaveChangesAsync();
+ await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
return NoContent();
}
diff --git a/Catalogger.Backend/Api/GuildsController.Remove.cs b/Catalogger.Backend/Api/GuildsController.Remove.cs
index 08f0d61..2d2ff5e 100644
--- a/Catalogger.Backend/Api/GuildsController.Remove.cs
+++ b/Catalogger.Backend/Api/GuildsController.Remove.cs
@@ -16,12 +16,10 @@
using System.Net;
using Catalogger.Backend.Api.Middleware;
using Catalogger.Backend.Bot;
-using Catalogger.Backend.Database.Queries;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Dapper;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.EntityFrameworkCore;
using Remora.Discord.Extensions.Embeds;
namespace Catalogger.Backend.Api;
diff --git a/Catalogger.Backend/Api/GuildsController.cs b/Catalogger.Backend/Api/GuildsController.cs
index 6517ae2..560456f 100644
--- a/Catalogger.Backend/Api/GuildsController.cs
+++ b/Catalogger.Backend/Api/GuildsController.cs
@@ -18,8 +18,7 @@ using Catalogger.Backend.Api.Middleware;
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Services;
using Microsoft.AspNetCore.Mvc;
using Remora.Discord.API;
@@ -32,7 +31,6 @@ namespace Catalogger.Backend.Api;
[Route("/api/guilds/{id}")]
public partial class GuildsController(
ILogger logger,
- DatabaseContext db,
DatabaseConnection dbConn,
GuildRepository guildRepository,
GuildCache guildCache,
diff --git a/Catalogger.Backend/Api/MetaController.cs b/Catalogger.Backend/Api/MetaController.cs
index 533bc6d..17d3c27 100644
--- a/Catalogger.Backend/Api/MetaController.cs
+++ b/Catalogger.Backend/Api/MetaController.cs
@@ -15,10 +15,8 @@
using Catalogger.Backend.Api.Middleware;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Microsoft.AspNetCore.Mvc;
-using Remora.Discord.API.Abstractions.Objects;
namespace Catalogger.Backend.Api;
diff --git a/Catalogger.Backend/Api/Middleware/AuthenticationMiddleware.cs b/Catalogger.Backend/Api/Middleware/AuthenticationMiddleware.cs
index 21d339a..47447dc 100644
--- a/Catalogger.Backend/Api/Middleware/AuthenticationMiddleware.cs
+++ b/Catalogger.Backend/Api/Middleware/AuthenticationMiddleware.cs
@@ -14,14 +14,14 @@
// along with this program. If not, see .
using System.Net;
-using Catalogger.Backend.Database;
using Catalogger.Backend.Database.Models;
-using Microsoft.EntityFrameworkCore;
+using Catalogger.Backend.Database.Repositories;
using NodaTime;
namespace Catalogger.Backend.Api.Middleware;
-public class AuthenticationMiddleware(DatabaseContext db, IClock clock) : IMiddleware
+public class AuthenticationMiddleware(ApiTokenRepository tokenRepository, IClock clock)
+ : IMiddleware
{
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
{
@@ -37,9 +37,7 @@ public class AuthenticationMiddleware(DatabaseContext db, IClock clock) : IMiddl
var token = ctx.Request.Headers.Authorization.ToString();
- var apiToken = await db.ApiTokens.FirstOrDefaultAsync(t =>
- t.DashboardToken == token && t.ExpiresAt > clock.GetCurrentInstant()
- );
+ var apiToken = await tokenRepository.GetAsync(token);
if (apiToken == null)
{
if (requireAuth)
diff --git a/Catalogger.Backend/Bot/Commands/ChannelCommands.cs b/Catalogger.Backend/Bot/Commands/ChannelCommands.cs
index 438459a..fcf54b9 100644
--- a/Catalogger.Backend/Bot/Commands/ChannelCommands.cs
+++ b/Catalogger.Backend/Bot/Commands/ChannelCommands.cs
@@ -16,9 +16,7 @@
using System.ComponentModel;
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Commands.Attributes;
diff --git a/Catalogger.Backend/Bot/Commands/ChannelCommandsComponents.cs b/Catalogger.Backend/Bot/Commands/ChannelCommandsComponents.cs
index 38bfc71..1bda457 100644
--- a/Catalogger.Backend/Bot/Commands/ChannelCommandsComponents.cs
+++ b/Catalogger.Backend/Bot/Commands/ChannelCommandsComponents.cs
@@ -14,7 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Objects;
diff --git a/Catalogger.Backend/Bot/Commands/IgnoreChannelCommands.cs b/Catalogger.Backend/Bot/Commands/IgnoreChannelCommands.cs
index afa9165..63dce5e 100644
--- a/Catalogger.Backend/Bot/Commands/IgnoreChannelCommands.cs
+++ b/Catalogger.Backend/Bot/Commands/IgnoreChannelCommands.cs
@@ -16,7 +16,7 @@
using System.ComponentModel;
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Commands.Attributes;
diff --git a/Catalogger.Backend/Bot/Commands/InviteCommands.cs b/Catalogger.Backend/Bot/Commands/InviteCommands.cs
index 68d11f9..77757f6 100644
--- a/Catalogger.Backend/Bot/Commands/InviteCommands.cs
+++ b/Catalogger.Backend/Bot/Commands/InviteCommands.cs
@@ -17,7 +17,7 @@ using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
diff --git a/Catalogger.Backend/Bot/Commands/KeyRoleCommands.cs b/Catalogger.Backend/Bot/Commands/KeyRoleCommands.cs
index 94a1bcd..67d3ab8 100644
--- a/Catalogger.Backend/Bot/Commands/KeyRoleCommands.cs
+++ b/Catalogger.Backend/Bot/Commands/KeyRoleCommands.cs
@@ -15,7 +15,7 @@
using System.ComponentModel;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
diff --git a/Catalogger.Backend/Bot/Commands/RedirectCommands.cs b/Catalogger.Backend/Bot/Commands/RedirectCommands.cs
index 86e272f..c776675 100644
--- a/Catalogger.Backend/Bot/Commands/RedirectCommands.cs
+++ b/Catalogger.Backend/Bot/Commands/RedirectCommands.cs
@@ -15,7 +15,7 @@
using System.ComponentModel;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
diff --git a/Catalogger.Backend/Bot/DiscordUtils.cs b/Catalogger.Backend/Bot/DiscordUtils.cs
index 87bca46..8e0a867 100644
--- a/Catalogger.Backend/Bot/DiscordUtils.cs
+++ b/Catalogger.Backend/Bot/DiscordUtils.cs
@@ -17,9 +17,7 @@ using System.Drawing;
using Remora.Discord.API;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Objects;
-using Remora.Discord.Commands.Feedback.Services;
using Remora.Discord.Pagination;
-using Remora.Discord.Pagination.Extensions;
using Remora.Rest.Core;
namespace Catalogger.Backend.Bot;
diff --git a/Catalogger.Backend/Bot/Responders/Channels/ChannelCreateResponder.cs b/Catalogger.Backend/Bot/Responders/Channels/ChannelCreateResponder.cs
index b2d5f50..93666fe 100644
--- a/Catalogger.Backend/Bot/Responders/Channels/ChannelCreateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Channels/ChannelCreateResponder.cs
@@ -14,7 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Channels/ChannelDeleteResponder.cs b/Catalogger.Backend/Bot/Responders/Channels/ChannelDeleteResponder.cs
index 78b5e89..aaee939 100644
--- a/Catalogger.Backend/Bot/Responders/Channels/ChannelDeleteResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Channels/ChannelDeleteResponder.cs
@@ -14,9 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Channels/ChannelUpdateResponder.cs b/Catalogger.Backend/Bot/Responders/Channels/ChannelUpdateResponder.cs
index ea762f9..0ad0c08 100644
--- a/Catalogger.Backend/Bot/Responders/Channels/ChannelUpdateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Channels/ChannelUpdateResponder.cs
@@ -14,9 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Humanizer;
diff --git a/Catalogger.Backend/Bot/Responders/Guilds/AuditLogResponder.cs b/Catalogger.Backend/Bot/Responders/Guilds/AuditLogResponder.cs
index 1d9eae2..1d88db1 100644
--- a/Catalogger.Backend/Bot/Responders/Guilds/AuditLogResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Guilds/AuditLogResponder.cs
@@ -14,7 +14,6 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Remora.Discord.API;
using Remora.Discord.API.Abstractions.Gateway.Events;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.Gateway.Responders;
diff --git a/Catalogger.Backend/Bot/Responders/Guilds/GuildBanAddResponder.cs b/Catalogger.Backend/Bot/Responders/Guilds/GuildBanAddResponder.cs
index 2001efb..0100b1f 100644
--- a/Catalogger.Backend/Bot/Responders/Guilds/GuildBanAddResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Guilds/GuildBanAddResponder.cs
@@ -14,9 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Guilds/GuildBanRemoveResponder.cs b/Catalogger.Backend/Bot/Responders/Guilds/GuildBanRemoveResponder.cs
index 0885298..ee1d1bc 100644
--- a/Catalogger.Backend/Bot/Responders/Guilds/GuildBanRemoveResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Guilds/GuildBanRemoveResponder.cs
@@ -14,9 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Guilds/GuildCreateResponder.cs b/Catalogger.Backend/Bot/Responders/Guilds/GuildCreateResponder.cs
index 0ee92df..ab12125 100644
--- a/Catalogger.Backend/Bot/Responders/Guilds/GuildCreateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Guilds/GuildCreateResponder.cs
@@ -15,15 +15,13 @@
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Gateway.Responders;
using Remora.Results;
-using Guild = Catalogger.Backend.Database.Models.Guild;
namespace Catalogger.Backend.Bot.Responders.Guilds;
diff --git a/Catalogger.Backend/Bot/Responders/Guilds/GuildEmojisUpdateResponder.cs b/Catalogger.Backend/Bot/Responders/Guilds/GuildEmojisUpdateResponder.cs
index ebdd9bb..0ccc859 100644
--- a/Catalogger.Backend/Bot/Responders/Guilds/GuildEmojisUpdateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Guilds/GuildEmojisUpdateResponder.cs
@@ -14,9 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API;
diff --git a/Catalogger.Backend/Bot/Responders/Guilds/GuildUpdateResponder.cs b/Catalogger.Backend/Bot/Responders/Guilds/GuildUpdateResponder.cs
index 89ef472..ee43e98 100644
--- a/Catalogger.Backend/Bot/Responders/Guilds/GuildUpdateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Guilds/GuildUpdateResponder.cs
@@ -14,9 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API;
diff --git a/Catalogger.Backend/Bot/Responders/Invites/InviteCreateResponder.cs b/Catalogger.Backend/Bot/Responders/Invites/InviteCreateResponder.cs
index 225f04a..329d764 100644
--- a/Catalogger.Backend/Bot/Responders/Invites/InviteCreateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Invites/InviteCreateResponder.cs
@@ -14,9 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Invites/InviteDeleteResponder.cs b/Catalogger.Backend/Bot/Responders/Invites/InviteDeleteResponder.cs
index b33ad94..bb9d855 100644
--- a/Catalogger.Backend/Bot/Responders/Invites/InviteDeleteResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Invites/InviteDeleteResponder.cs
@@ -14,7 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Members/GuildMemberAddResponder.cs b/Catalogger.Backend/Bot/Responders/Members/GuildMemberAddResponder.cs
index dfb56f5..5ea725a 100644
--- a/Catalogger.Backend/Bot/Responders/Members/GuildMemberAddResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Members/GuildMemberAddResponder.cs
@@ -15,7 +15,7 @@
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Humanizer;
diff --git a/Catalogger.Backend/Bot/Responders/Members/GuildMemberRemoveResponder.cs b/Catalogger.Backend/Bot/Responders/Members/GuildMemberRemoveResponder.cs
index bbeea22..94cf8cc 100644
--- a/Catalogger.Backend/Bot/Responders/Members/GuildMemberRemoveResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Members/GuildMemberRemoveResponder.cs
@@ -15,7 +15,7 @@
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Members/GuildMemberUpdateResponder.cs b/Catalogger.Backend/Bot/Responders/Members/GuildMemberUpdateResponder.cs
index a4dac29..4a6e6b8 100644
--- a/Catalogger.Backend/Bot/Responders/Members/GuildMemberUpdateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Members/GuildMemberUpdateResponder.cs
@@ -15,9 +15,7 @@
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Messages/MessageCreateResponder.cs b/Catalogger.Backend/Bot/Responders/Messages/MessageCreateResponder.cs
index 8364dcf..cc45edc 100644
--- a/Catalogger.Backend/Bot/Responders/Messages/MessageCreateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Messages/MessageCreateResponder.cs
@@ -15,7 +15,7 @@
using System.Text.RegularExpressions;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Humanizer;
diff --git a/Catalogger.Backend/Bot/Responders/Messages/MessageDeleteBulkResponder.cs b/Catalogger.Backend/Bot/Responders/Messages/MessageDeleteBulkResponder.cs
index b9c66a4..6676839 100644
--- a/Catalogger.Backend/Bot/Responders/Messages/MessageDeleteBulkResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Messages/MessageDeleteBulkResponder.cs
@@ -15,7 +15,7 @@
using System.Text;
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using NodaTime.Extensions;
diff --git a/Catalogger.Backend/Bot/Responders/Messages/MessageDeleteResponder.cs b/Catalogger.Backend/Bot/Responders/Messages/MessageDeleteResponder.cs
index d97b09d..f866b94 100644
--- a/Catalogger.Backend/Bot/Responders/Messages/MessageDeleteResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Messages/MessageDeleteResponder.cs
@@ -14,7 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Humanizer;
diff --git a/Catalogger.Backend/Bot/Responders/Messages/MessageUpdateResponder.cs b/Catalogger.Backend/Bot/Responders/Messages/MessageUpdateResponder.cs
index d042765..1dd9290 100644
--- a/Catalogger.Backend/Bot/Responders/Messages/MessageUpdateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Messages/MessageUpdateResponder.cs
@@ -14,7 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API;
diff --git a/Catalogger.Backend/Bot/Responders/Roles/RoleCreateResponder.cs b/Catalogger.Backend/Bot/Responders/Roles/RoleCreateResponder.cs
index 6dc6374..4df76ea 100644
--- a/Catalogger.Backend/Bot/Responders/Roles/RoleCreateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Roles/RoleCreateResponder.cs
@@ -14,9 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Roles/RoleDeleteResponder.cs b/Catalogger.Backend/Bot/Responders/Roles/RoleDeleteResponder.cs
index 303f7dd..8566434 100644
--- a/Catalogger.Backend/Bot/Responders/Roles/RoleDeleteResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Roles/RoleDeleteResponder.cs
@@ -14,7 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
diff --git a/Catalogger.Backend/Bot/Responders/Roles/RoleUpdateResponder.cs b/Catalogger.Backend/Bot/Responders/Roles/RoleUpdateResponder.cs
index c6d142b..828ef22 100644
--- a/Catalogger.Backend/Bot/Responders/Roles/RoleUpdateResponder.cs
+++ b/Catalogger.Backend/Bot/Responders/Roles/RoleUpdateResponder.cs
@@ -14,7 +14,7 @@
// along with this program. If not, see .
using Catalogger.Backend.Cache.InMemoryCache;
-using Catalogger.Backend.Database.Dapper.Repositories;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Humanizer;
diff --git a/Catalogger.Backend/Catalogger.Backend.csproj b/Catalogger.Backend/Catalogger.Backend.csproj
index be55129..436899d 100644
--- a/Catalogger.Backend/Catalogger.Backend.csproj
+++ b/Catalogger.Backend/Catalogger.Backend.csproj
@@ -7,25 +7,20 @@
true
+
+
+
+
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
@@ -42,4 +37,8 @@
+
+
+
+
diff --git a/Catalogger.Backend/Database/Dapper/DatabaseConnection.cs b/Catalogger.Backend/Database/DatabaseConnection.cs
similarity index 98%
rename from Catalogger.Backend/Database/Dapper/DatabaseConnection.cs
rename to Catalogger.Backend/Database/DatabaseConnection.cs
index ed6bafe..61d2a7e 100644
--- a/Catalogger.Backend/Database/Dapper/DatabaseConnection.cs
+++ b/Catalogger.Backend/Database/DatabaseConnection.cs
@@ -18,7 +18,7 @@ using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using Npgsql;
-namespace Catalogger.Backend.Database.Dapper;
+namespace Catalogger.Backend.Database;
public class DatabaseConnection(Guid id, ILogger logger, NpgsqlConnection inner)
: DbConnection,
diff --git a/Catalogger.Backend/Database/DatabaseContext.cs b/Catalogger.Backend/Database/DatabaseContext.cs
deleted file mode 100644
index 0fa4fb4..0000000
--- a/Catalogger.Backend/Database/DatabaseContext.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (C) 2021-present sam (starshines.gay)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published
-// by the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see .
-
-using Catalogger.Backend.Database.Models;
-using Catalogger.Backend.Extensions;
-using EntityFramework.Exceptions.PostgreSQL;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.ChangeTracking;
-using Microsoft.EntityFrameworkCore.Design;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Npgsql;
-
-namespace Catalogger.Backend.Database;
-
-public class DatabaseContext : DbContext
-{
- private readonly NpgsqlDataSource _dataSource;
- private readonly ILoggerFactory? _loggerFactory;
-
- public DbSet Guilds { get; set; }
- public DbSet Messages { get; set; }
- public DbSet IgnoredMessages { get; set; }
- public DbSet Invites { get; set; }
- public DbSet Watchlists { get; set; }
- public DbSet ApiTokens { get; set; }
-
- public DatabaseContext(Config config, ILoggerFactory? loggerFactory)
- {
- var connString = new NpgsqlConnectionStringBuilder(config.Database.Url)
- {
- Timeout = config.Database.Timeout ?? 5,
- MaxPoolSize = config.Database.MaxPoolSize ?? 50,
- }.ConnectionString;
-
- var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString);
- dataSourceBuilder.EnableDynamicJson().UseNodaTime();
- _dataSource = dataSourceBuilder.Build();
- _loggerFactory = loggerFactory;
- }
-
- protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
- optionsBuilder
- .ConfigureWarnings(c =>
- c.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning)
- .Ignore(CoreEventId.SaveChangesFailed)
- )
- .UseNpgsql(_dataSource, o => o.UseNodaTime())
- .UseSnakeCaseNamingConvention()
- .UseLoggerFactory(_loggerFactory)
- .UseExceptionProcessor();
-
- protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
- {
- configurationBuilder.Properties().HaveConversion();
- configurationBuilder.Properties>().HaveConversion();
- }
-
- private static readonly ValueComparer> UlongListValueComparer =
- new(
- (c1, c2) => c1 != null && c2 != null && c1.SequenceEqual(c2),
- c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode()))
- );
-
- private static readonly ValueComparer UlongArrayValueComparer =
- new(
- (c1, c2) => c1 != null && c2 != null && c1.SequenceEqual(c2),
- c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode()))
- );
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder
- .Entity()
- .Property(g => g.KeyRoles)
- .Metadata.SetValueComparer(UlongArrayValueComparer);
-
- modelBuilder.Entity().HasKey(i => i.Code);
- modelBuilder.Entity().HasIndex(i => i.GuildId);
-
- modelBuilder.Entity().HasKey(w => new { w.GuildId, w.UserId });
- modelBuilder.Entity().Property(w => w.AddedAt).HasDefaultValueSql("now()");
- }
-}
-
-public class DesignTimeDatabaseContextFactory : IDesignTimeDbContextFactory
-{
- public DatabaseContext CreateDbContext(string[] args)
- {
- // Read the configuration file
- var config =
- new ConfigurationBuilder()
- .AddConfiguration()
- .Build()
- // Get the configuration as our config class
- .Get() ?? new();
-
- return new DatabaseContext(config, null);
- }
-}
-
-public class UlongValueConverter()
- : ValueConverter(
- convertToProviderExpression: x => (long)x,
- convertFromProviderExpression: x => (ulong)x
- );
-
-public class UlongArrayValueConverter()
- : ValueConverter, List>(
- convertToProviderExpression: x => x.Select(i => (long)i).ToList(),
- convertFromProviderExpression: x => x.Select(i => (ulong)i).ToList()
- );
diff --git a/Catalogger.Backend/Database/DatabaseMigrator.cs b/Catalogger.Backend/Database/DatabaseMigrator.cs
new file mode 100644
index 0000000..76a0dbc
--- /dev/null
+++ b/Catalogger.Backend/Database/DatabaseMigrator.cs
@@ -0,0 +1,145 @@
+// Copyright (C) 2021-present sam (starshines.gay)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+using System.Data.Common;
+using Dapper;
+using NodaTime;
+
+namespace Catalogger.Backend.Database;
+
+public class DatabaseMigrator(ILogger logger, IClock clock, DatabaseConnection conn)
+ : IDisposable,
+ IAsyncDisposable
+{
+ private const string RootPath = "Catalogger.Backend.Database";
+ private static readonly int MigrationsPathLength = $"{RootPath}.Migrations.".Length;
+
+ public async Task Migrate()
+ {
+ var migrations = GetMigrationNames().ToArray();
+ logger.Debug("Getting current database migration");
+ var currentMigration = await GetCurrentMigration();
+ if (currentMigration != null)
+ migrations = migrations
+ .Where(s => string.CompareOrdinal(s, currentMigration.MigrationName) > 0)
+ .ToArray();
+
+ logger.Information(
+ "Current migration: {Migration}. Applying {Count} migrations",
+ currentMigration?.MigrationName,
+ migrations.Length
+ );
+ if (migrations.Length == 0)
+ {
+ return;
+ }
+
+ // Wrap all migrations in a transaction
+ await using var tx = await conn.BeginTransactionAsync();
+ var totalStartTime = clock.GetCurrentInstant();
+ foreach (var migration in migrations)
+ {
+ logger.Debug("Executing migration {Migration}", migration);
+ var startTime = clock.GetCurrentInstant();
+ await ExecuteMigration(tx, migration);
+ var took = clock.GetCurrentInstant() - startTime;
+ logger.Debug("Executed migration {Migration} in {Took}", migration, took);
+ }
+
+ var totalTook = clock.GetCurrentInstant() - totalStartTime;
+ logger.Information("Executed {Count} migrations in {Took}", migrations.Length, totalTook);
+
+ // Finally, commit the transaction
+ await tx.CommitAsync();
+ }
+
+ private async Task ExecuteMigration(DbTransaction tx, string migrationName)
+ {
+ var query = await GetResource($"{RootPath}.Migrations.{migrationName}.up.sql");
+
+ // Run the migration
+ await conn.ExecuteAsync(query, transaction: tx);
+ // Store that we ran the migration
+ await conn.ExecuteAsync(
+ "INSERT INTO migrations (migration_name, applied_at) VALUES (@MigrationName, @AppliedAt)",
+ new { MigrationName = migrationName, AppliedAt = clock.GetCurrentInstant() }
+ );
+ }
+
+ /// Returns the current migration. If no migrations have been applied, returns null
+ private async Task GetCurrentMigration()
+ {
+ // Check if the migrations table exists
+ var hasMigrationTable =
+ await conn.QuerySingleOrDefaultAsync(
+ "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'migrations'"
+ ) == 1;
+ // If so, return the current migration
+ if (hasMigrationTable)
+ {
+ return await conn.QuerySingleOrDefaultAsync(
+ "SELECT * FROM migrations ORDER BY applied_at DESC LIMIT 1"
+ );
+ }
+
+ logger.Debug("Migrations table does not exist, assuming this is a new database");
+
+ // Else, create the migrations table then return null
+ var migrationTableQuery = await GetResource($"{RootPath}.setup_migrations.sql");
+ await conn.ExecuteAsync(migrationTableQuery);
+ return null;
+ }
+
+ /// Returns a resource by name as a string.
+ private static async Task GetResource(string name)
+ {
+ await using var stream =
+ typeof(DatabasePool).Assembly.GetManifestResourceStream(name)
+ ?? throw new ArgumentException($"Invalid resource '{name}'");
+ using var reader = new StreamReader(stream);
+ return await reader.ReadToEndAsync();
+ }
+
+ public static IEnumerable GetMigrationNames() =>
+ typeof(DatabasePool)
+ .Assembly.GetManifestResourceNames()
+ .Where(s => s.StartsWith($"{RootPath}.Migrations"))
+ .Where(s => s.EndsWith(".up.sql"))
+ .Select(s =>
+ s.Substring(
+ MigrationsPathLength,
+ s.Length - MigrationsPathLength - ".up.sql".Length
+ )
+ )
+ .OrderBy(s => s);
+
+ private record MigrationEntry
+ {
+ public string MigrationName { get; init; } = null!;
+ public Instant AppliedAt { get; init; }
+ }
+
+ public void Dispose()
+ {
+ conn.Dispose();
+ GC.SuppressFinalize(this);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await conn.DisposeAsync();
+ GC.SuppressFinalize(this);
+ }
+}
diff --git a/Catalogger.Backend/Database/Dapper/DatabasePool.cs b/Catalogger.Backend/Database/DatabasePool.cs
similarity index 98%
rename from Catalogger.Backend/Database/Dapper/DatabasePool.cs
rename to Catalogger.Backend/Database/DatabasePool.cs
index 3d1342f..82072f8 100644
--- a/Catalogger.Backend/Database/Dapper/DatabasePool.cs
+++ b/Catalogger.Backend/Database/DatabasePool.cs
@@ -15,13 +15,12 @@
using System.Data;
using System.Text.Json;
-using System.Text.Json.Serialization;
using Catalogger.Backend.Database.Models;
using Dapper;
using NodaTime;
using Npgsql;
-namespace Catalogger.Backend.Database.Dapper;
+namespace Catalogger.Backend.Database;
public class DatabasePool
{
diff --git a/Catalogger.Backend/Database/Migrations/001_init.down.sql b/Catalogger.Backend/Database/Migrations/001_init.down.sql
new file mode 100644
index 0000000..a8b2cb7
--- /dev/null
+++ b/Catalogger.Backend/Database/Migrations/001_init.down.sql
@@ -0,0 +1,6 @@
+drop table api_tokens;
+drop table watchlists;
+drop table messages;
+drop table invites;
+drop table ignored_messages;
+drop table guilds;
diff --git a/Catalogger.Backend/Database/Migrations/001_init.up.sql b/Catalogger.Backend/Database/Migrations/001_init.up.sql
new file mode 100644
index 0000000..43fc84d
--- /dev/null
+++ b/Catalogger.Backend/Database/Migrations/001_init.up.sql
@@ -0,0 +1,60 @@
+create table if not exists guilds
+(
+ id bigint not null primary key,
+ channels jsonb not null,
+ banned_systems text[] not null,
+ key_roles bigint[] not null
+);
+
+create table if not exists ignored_messages
+(
+ id bigint not null primary key
+);
+
+create table if not exists invites
+(
+ code text not null primary key,
+ guild_id bigint not null,
+ name text not null
+);
+
+create index if not exists ix_invites_guild_id on invites (guild_id);
+
+create table if not exists messages
+(
+ id bigint not null primary key,
+ original_id bigint,
+ user_id bigint not null,
+ channel_id bigint not null,
+ guild_id bigint not null,
+ member text,
+ system text,
+ username bytea not null,
+ content bytea not null,
+ metadata bytea,
+ attachment_size integer not null
+);
+
+create table if not exists watchlists
+(
+ guild_id bigint not null,
+ user_id bigint not null,
+ added_at timestamp with time zone default now() not null,
+ moderator_id bigint not null,
+ reason text not null,
+
+ primary key (guild_id, user_id)
+);
+
+create table if not exists api_tokens
+(
+ id integer generated by default as identity primary key,
+ dashboard_token text not null,
+ user_id text not null,
+ access_token text not null,
+ refresh_token text,
+ expires_at timestamp with time zone not null
+);
+
+-- Finally, drop the EFCore migrations table.
+drop table if exists "__EFMigrationsHistory";
diff --git a/Catalogger.Backend/Database/Migrations/20240803132306_Init.Designer.cs b/Catalogger.Backend/Database/Migrations/20240803132306_Init.Designer.cs
deleted file mode 100644
index 9167588..0000000
--- a/Catalogger.Backend/Database/Migrations/20240803132306_Init.Designer.cs
+++ /dev/null
@@ -1,180 +0,0 @@
-//
-using System.Collections.Generic;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Models;
-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 Catalogger.Backend.Database.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20240803132306_Init")]
- partial class Init
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "8.0.7")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Guild", b =>
- {
- b.Property("Id")
- .HasColumnType("bigint")
- .HasColumnName("id");
-
- b.Property>("BannedSystems")
- .IsRequired()
- .HasColumnType("text[]")
- .HasColumnName("banned_systems");
-
- b.Property("Channels")
- .IsRequired()
- .HasColumnType("jsonb")
- .HasColumnName("channels");
-
- b.Property>("KeyRoles")
- .IsRequired()
- .HasColumnType("bigint[]")
- .HasColumnName("key_roles");
-
- b.HasKey("Id")
- .HasName("pk_guilds");
-
- b.ToTable("guilds", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.IgnoredMessage", b =>
- {
- b.Property("Id")
- .HasColumnType("bigint")
- .HasColumnName("id");
-
- b.HasKey("Id")
- .HasName("pk_ignored_messages");
-
- b.ToTable("ignored_messages", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Invite", b =>
- {
- b.Property("Code")
- .HasColumnType("text")
- .HasColumnName("code");
-
- b.Property("GuildId")
- .HasColumnType("bigint")
- .HasColumnName("guild_id");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("name");
-
- b.HasKey("Code")
- .HasName("pk_invites");
-
- b.HasIndex("GuildId")
- .HasDatabaseName("ix_invites_guild_id");
-
- b.ToTable("invites", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Message", b =>
- {
- b.Property("Id")
- .HasColumnType("bigint")
- .HasColumnName("id");
-
- b.Property("AttachmentSize")
- .HasColumnType("integer")
- .HasColumnName("attachment_size");
-
- b.Property("ChannelId")
- .HasColumnType("bigint")
- .HasColumnName("channel_id");
-
- b.Property("EncryptedContent")
- .IsRequired()
- .HasColumnType("bytea")
- .HasColumnName("content");
-
- b.Property("EncryptedMetadata")
- .HasColumnType("bytea")
- .HasColumnName("metadata");
-
- b.Property("EncryptedUsername")
- .IsRequired()
- .HasColumnType("bytea")
- .HasColumnName("username");
-
- b.Property("GuildId")
- .HasColumnType("bigint")
- .HasColumnName("guild_id");
-
- b.Property("Member")
- .HasColumnType("text")
- .HasColumnName("member");
-
- b.Property("OriginalId")
- .HasColumnType("bigint")
- .HasColumnName("original_id");
-
- b.Property("System")
- .HasColumnType("text")
- .HasColumnName("system");
-
- b.Property("UserId")
- .HasColumnType("bigint")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("pk_messages");
-
- b.ToTable("messages", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Watchlist", b =>
- {
- b.Property("GuildId")
- .HasColumnType("bigint")
- .HasColumnName("guild_id");
-
- b.Property("UserId")
- .HasColumnType("bigint")
- .HasColumnName("user_id");
-
- b.Property("AddedAt")
- .ValueGeneratedOnAdd()
- .HasColumnType("timestamp with time zone")
- .HasColumnName("added_at")
- .HasDefaultValueSql("now()");
-
- b.Property("ModeratorId")
- .HasColumnType("bigint")
- .HasColumnName("moderator_id");
-
- b.Property("Reason")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("reason");
-
- b.HasKey("GuildId", "UserId")
- .HasName("pk_watchlists");
-
- b.ToTable("watchlists", (string)null);
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Catalogger.Backend/Database/Migrations/20240803132306_Init.cs b/Catalogger.Backend/Database/Migrations/20240803132306_Init.cs
deleted file mode 100644
index 51c7218..0000000
--- a/Catalogger.Backend/Database/Migrations/20240803132306_Init.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using System.Collections.Generic;
-using Catalogger.Backend.Database.Models;
-using Microsoft.EntityFrameworkCore.Migrations;
-using NodaTime;
-
-#nullable disable
-
-namespace Catalogger.Backend.Database.Migrations
-{
- ///
- public partial class Init : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "guilds",
- columns: table => new
- {
- id = table.Column(type: "bigint", nullable: false),
- channels = table.Column(type: "jsonb", nullable: false),
- banned_systems = table.Column>(type: "text[]", nullable: false),
- key_roles = table.Column>(type: "bigint[]", nullable: false),
- },
- constraints: table =>
- {
- table.PrimaryKey("pk_guilds", x => x.id);
- }
- );
-
- migrationBuilder.CreateTable(
- name: "ignored_messages",
- columns: table => new { id = table.Column(type: "bigint", nullable: false) },
- constraints: table =>
- {
- table.PrimaryKey("pk_ignored_messages", x => x.id);
- }
- );
-
- migrationBuilder.CreateTable(
- name: "invites",
- columns: table => new
- {
- code = table.Column(type: "text", nullable: false),
- guild_id = table.Column(type: "bigint", nullable: false),
- name = table.Column(type: "text", nullable: false),
- },
- constraints: table =>
- {
- table.PrimaryKey("pk_invites", x => x.code);
- }
- );
-
- migrationBuilder.CreateTable(
- name: "messages",
- columns: table => new
- {
- id = table.Column(type: "bigint", nullable: false),
- original_id = table.Column(type: "bigint", nullable: true),
- user_id = table.Column(type: "bigint", nullable: false),
- channel_id = table.Column(type: "bigint", nullable: false),
- guild_id = table.Column(type: "bigint", nullable: false),
- member = table.Column(type: "text", nullable: true),
- system = table.Column(type: "text", nullable: true),
- username = table.Column(type: "bytea", nullable: false),
- content = table.Column(type: "bytea", nullable: false),
- metadata = table.Column(type: "bytea", nullable: true),
- attachment_size = table.Column(type: "integer", nullable: false),
- },
- constraints: table =>
- {
- table.PrimaryKey("pk_messages", x => x.id);
- }
- );
-
- migrationBuilder.CreateTable(
- name: "watchlists",
- columns: table => new
- {
- guild_id = table.Column(type: "bigint", nullable: false),
- user_id = table.Column(type: "bigint", nullable: false),
- added_at = table.Column(
- type: "timestamp with time zone",
- nullable: false,
- defaultValueSql: "now()"
- ),
- moderator_id = table.Column(type: "bigint", nullable: false),
- reason = table.Column(type: "text", nullable: false),
- },
- constraints: table =>
- {
- table.PrimaryKey("pk_watchlists", x => new { x.guild_id, x.user_id });
- }
- );
-
- migrationBuilder.CreateIndex(
- name: "ix_invites_guild_id",
- table: "invites",
- column: "guild_id"
- );
- }
-
- ///
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(name: "guilds");
-
- migrationBuilder.DropTable(name: "ignored_messages");
-
- migrationBuilder.DropTable(name: "invites");
-
- migrationBuilder.DropTable(name: "messages");
-
- migrationBuilder.DropTable(name: "watchlists");
- }
- }
-}
diff --git a/Catalogger.Backend/Database/Migrations/20241017130936_AddDashboardTokens.Designer.cs b/Catalogger.Backend/Database/Migrations/20241017130936_AddDashboardTokens.Designer.cs
deleted file mode 100644
index 2399bb8..0000000
--- a/Catalogger.Backend/Database/Migrations/20241017130936_AddDashboardTokens.Designer.cs
+++ /dev/null
@@ -1,218 +0,0 @@
-//
-using System.Collections.Generic;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Models;
-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 Catalogger.Backend.Database.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20241017130936_AddDashboardTokens")]
- partial class AddDashboardTokens
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "8.0.8")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.ApiToken", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("AccessToken")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("access_token");
-
- b.Property("DashboardToken")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("dashboard_token");
-
- b.Property("ExpiresAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("expires_at");
-
- b.Property("RefreshToken")
- .HasColumnType("text")
- .HasColumnName("refresh_token");
-
- b.Property("UserId")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("pk_api_tokens");
-
- b.ToTable("api_tokens", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Guild", b =>
- {
- b.Property("Id")
- .HasColumnType("bigint")
- .HasColumnName("id");
-
- b.Property>("BannedSystems")
- .IsRequired()
- .HasColumnType("text[]")
- .HasColumnName("banned_systems");
-
- b.Property("Channels")
- .IsRequired()
- .HasColumnType("jsonb")
- .HasColumnName("channels");
-
- b.Property>("KeyRoles")
- .IsRequired()
- .HasColumnType("bigint[]")
- .HasColumnName("key_roles");
-
- b.HasKey("Id")
- .HasName("pk_guilds");
-
- b.ToTable("guilds", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.IgnoredMessage", b =>
- {
- b.Property("Id")
- .HasColumnType("bigint")
- .HasColumnName("id");
-
- b.HasKey("Id")
- .HasName("pk_ignored_messages");
-
- b.ToTable("ignored_messages", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Invite", b =>
- {
- b.Property("Code")
- .HasColumnType("text")
- .HasColumnName("code");
-
- b.Property("GuildId")
- .HasColumnType("bigint")
- .HasColumnName("guild_id");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("name");
-
- b.HasKey("Code")
- .HasName("pk_invites");
-
- b.HasIndex("GuildId")
- .HasDatabaseName("ix_invites_guild_id");
-
- b.ToTable("invites", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Message", b =>
- {
- b.Property("Id")
- .HasColumnType("bigint")
- .HasColumnName("id");
-
- b.Property("AttachmentSize")
- .HasColumnType("integer")
- .HasColumnName("attachment_size");
-
- b.Property("ChannelId")
- .HasColumnType("bigint")
- .HasColumnName("channel_id");
-
- b.Property("EncryptedContent")
- .IsRequired()
- .HasColumnType("bytea")
- .HasColumnName("content");
-
- b.Property("EncryptedMetadata")
- .HasColumnType("bytea")
- .HasColumnName("metadata");
-
- b.Property("EncryptedUsername")
- .IsRequired()
- .HasColumnType("bytea")
- .HasColumnName("username");
-
- b.Property("GuildId")
- .HasColumnType("bigint")
- .HasColumnName("guild_id");
-
- b.Property("Member")
- .HasColumnType("text")
- .HasColumnName("member");
-
- b.Property("OriginalId")
- .HasColumnType("bigint")
- .HasColumnName("original_id");
-
- b.Property("System")
- .HasColumnType("text")
- .HasColumnName("system");
-
- b.Property("UserId")
- .HasColumnType("bigint")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("pk_messages");
-
- b.ToTable("messages", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Watchlist", b =>
- {
- b.Property("GuildId")
- .HasColumnType("bigint")
- .HasColumnName("guild_id");
-
- b.Property("UserId")
- .HasColumnType("bigint")
- .HasColumnName("user_id");
-
- b.Property("AddedAt")
- .ValueGeneratedOnAdd()
- .HasColumnType("timestamp with time zone")
- .HasColumnName("added_at")
- .HasDefaultValueSql("now()");
-
- b.Property("ModeratorId")
- .HasColumnType("bigint")
- .HasColumnName("moderator_id");
-
- b.Property("Reason")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("reason");
-
- b.HasKey("GuildId", "UserId")
- .HasName("pk_watchlists");
-
- b.ToTable("watchlists", (string)null);
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Catalogger.Backend/Database/Migrations/20241017130936_AddDashboardTokens.cs b/Catalogger.Backend/Database/Migrations/20241017130936_AddDashboardTokens.cs
deleted file mode 100644
index a37ace5..0000000
--- a/Catalogger.Backend/Database/Migrations/20241017130936_AddDashboardTokens.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-using NodaTime;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-
-#nullable disable
-
-namespace Catalogger.Backend.Database.Migrations
-{
- ///
- public partial class AddDashboardTokens : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "api_tokens",
- columns: table => new
- {
- id = table
- .Column(type: "integer", nullable: false)
- .Annotation(
- "Npgsql:ValueGenerationStrategy",
- NpgsqlValueGenerationStrategy.IdentityByDefaultColumn
- ),
- dashboard_token = table.Column(type: "text", nullable: false),
- user_id = table.Column(type: "text", nullable: false),
- access_token = table.Column(type: "text", nullable: false),
- refresh_token = table.Column(type: "text", nullable: true),
- expires_at = table.Column(
- type: "timestamp with time zone",
- nullable: false
- ),
- },
- constraints: table =>
- {
- table.PrimaryKey("pk_api_tokens", x => x.id);
- }
- );
- }
-
- ///
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(name: "api_tokens");
- }
- }
-}
diff --git a/Catalogger.Backend/Database/Migrations/DatabaseContextModelSnapshot.cs b/Catalogger.Backend/Database/Migrations/DatabaseContextModelSnapshot.cs
deleted file mode 100644
index 2709829..0000000
--- a/Catalogger.Backend/Database/Migrations/DatabaseContextModelSnapshot.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-//
-using System.Collections.Generic;
-using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Models;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using NodaTime;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-
-#nullable disable
-
-namespace Catalogger.Backend.Database.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- partial class DatabaseContextModelSnapshot : ModelSnapshot
- {
- protected override void BuildModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "8.0.8")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.ApiToken", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("AccessToken")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("access_token");
-
- b.Property("DashboardToken")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("dashboard_token");
-
- b.Property("ExpiresAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("expires_at");
-
- b.Property("RefreshToken")
- .HasColumnType("text")
- .HasColumnName("refresh_token");
-
- b.Property("UserId")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("pk_api_tokens");
-
- b.ToTable("api_tokens", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Guild", b =>
- {
- b.Property("Id")
- .HasColumnType("bigint")
- .HasColumnName("id");
-
- b.Property>("BannedSystems")
- .IsRequired()
- .HasColumnType("text[]")
- .HasColumnName("banned_systems");
-
- b.Property("Channels")
- .IsRequired()
- .HasColumnType("jsonb")
- .HasColumnName("channels");
-
- b.Property>("KeyRoles")
- .IsRequired()
- .HasColumnType("bigint[]")
- .HasColumnName("key_roles");
-
- b.HasKey("Id")
- .HasName("pk_guilds");
-
- b.ToTable("guilds", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.IgnoredMessage", b =>
- {
- b.Property("Id")
- .HasColumnType("bigint")
- .HasColumnName("id");
-
- b.HasKey("Id")
- .HasName("pk_ignored_messages");
-
- b.ToTable("ignored_messages", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Invite", b =>
- {
- b.Property("Code")
- .HasColumnType("text")
- .HasColumnName("code");
-
- b.Property("GuildId")
- .HasColumnType("bigint")
- .HasColumnName("guild_id");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("name");
-
- b.HasKey("Code")
- .HasName("pk_invites");
-
- b.HasIndex("GuildId")
- .HasDatabaseName("ix_invites_guild_id");
-
- b.ToTable("invites", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Message", b =>
- {
- b.Property("Id")
- .HasColumnType("bigint")
- .HasColumnName("id");
-
- b.Property("AttachmentSize")
- .HasColumnType("integer")
- .HasColumnName("attachment_size");
-
- b.Property("ChannelId")
- .HasColumnType("bigint")
- .HasColumnName("channel_id");
-
- b.Property("EncryptedContent")
- .IsRequired()
- .HasColumnType("bytea")
- .HasColumnName("content");
-
- b.Property("EncryptedMetadata")
- .HasColumnType("bytea")
- .HasColumnName("metadata");
-
- b.Property("EncryptedUsername")
- .IsRequired()
- .HasColumnType("bytea")
- .HasColumnName("username");
-
- b.Property("GuildId")
- .HasColumnType("bigint")
- .HasColumnName("guild_id");
-
- b.Property("Member")
- .HasColumnType("text")
- .HasColumnName("member");
-
- b.Property("OriginalId")
- .HasColumnType("bigint")
- .HasColumnName("original_id");
-
- b.Property("System")
- .HasColumnType("text")
- .HasColumnName("system");
-
- b.Property("UserId")
- .HasColumnType("bigint")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("pk_messages");
-
- b.ToTable("messages", (string)null);
- });
-
- modelBuilder.Entity("Catalogger.Backend.Database.Models.Watchlist", b =>
- {
- b.Property("GuildId")
- .HasColumnType("bigint")
- .HasColumnName("guild_id");
-
- b.Property("UserId")
- .HasColumnType("bigint")
- .HasColumnName("user_id");
-
- b.Property("AddedAt")
- .ValueGeneratedOnAdd()
- .HasColumnType("timestamp with time zone")
- .HasColumnName("added_at")
- .HasDefaultValueSql("now()");
-
- b.Property("ModeratorId")
- .HasColumnType("bigint")
- .HasColumnName("moderator_id");
-
- b.Property("Reason")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("reason");
-
- b.HasKey("GuildId", "UserId")
- .HasName("pk_watchlists");
-
- b.ToTable("watchlists", (string)null);
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Catalogger.Backend/Database/Queries/QueryExtensions.cs b/Catalogger.Backend/Database/Queries/QueryExtensions.cs
deleted file mode 100644
index 457f961..0000000
--- a/Catalogger.Backend/Database/Queries/QueryExtensions.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2021-present sam (starshines.gay)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published
-// by the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see .
-
-using Catalogger.Backend.Database.Models;
-using Catalogger.Backend.Extensions;
-using Microsoft.EntityFrameworkCore;
-using Remora.Rest.Core;
-
-namespace Catalogger.Backend.Database.Queries;
-
-public static class QueryExtensions
-{
- public static async ValueTask GetGuildAsync(
- this DatabaseContext db,
- Snowflake id,
- bool shouldTrack = true,
- CancellationToken ct = default
- ) => await db.GetGuildAsync(id.ToUlong(), shouldTrack, ct);
-
- public static async ValueTask GetGuildAsync(
- this DatabaseContext db,
- Optional id,
- bool shouldTrack = true,
- CancellationToken ct = default
- ) => await db.GetGuildAsync(id.ToUlong(), shouldTrack, ct);
-
- public static async ValueTask GetGuildAsync(
- this DatabaseContext db,
- ulong id,
- bool shouldTrack = true,
- CancellationToken ct = default
- )
- {
- Guild? guild;
- if (shouldTrack)
- guild = await db.Guilds.FindAsync([id], ct);
- else
- guild = await db.Guilds.AsNoTracking().FirstOrDefaultAsync(g => g.Id == id, ct);
-
- if (guild == null)
- throw new CataloggerError("Guild not found, was not initialized during guild create");
- return guild;
- }
-}
diff --git a/Catalogger.Backend/Database/Repositories/ApiTokenRepository.cs b/Catalogger.Backend/Database/Repositories/ApiTokenRepository.cs
new file mode 100644
index 0000000..25e7782
--- /dev/null
+++ b/Catalogger.Backend/Database/Repositories/ApiTokenRepository.cs
@@ -0,0 +1,97 @@
+// Copyright (C) 2021-present sam (starshines.gay)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+using Catalogger.Backend.Database.Models;
+using Dapper;
+using NodaTime;
+
+namespace Catalogger.Backend.Database.Repositories;
+
+public class ApiTokenRepository(ILogger logger, DatabaseConnection conn, IClock clock)
+ : IDisposable,
+ IAsyncDisposable
+{
+ private readonly ILogger _logger = logger.ForContext();
+
+ public async Task GetAsync(string token) =>
+ await conn.QueryFirstOrDefaultAsync(
+ "select * from api_tokens where dashboard_token = @Token and expires_at > @Now",
+ new { Token = token, Now = clock.GetCurrentInstant() }
+ );
+
+ public async Task CreateAsync(
+ string dashboardToken,
+ string userId,
+ string accessToken,
+ string? refreshToken,
+ int expiresIn
+ )
+ {
+ var expiresAt = clock.GetCurrentInstant() + Duration.FromSeconds(expiresIn);
+
+ return await conn.QueryFirstAsync(
+ """
+ insert into api_tokens (dashboard_token, user_id, access_token, refresh_token, expires_at)
+ values (@dashboardToken, @userId, @accessToken, @refreshToken, @expiresAt)
+ returning *
+ """,
+ new
+ {
+ dashboardToken,
+ userId,
+ accessToken,
+ refreshToken,
+ expiresAt,
+ }
+ );
+ }
+
+ public async Task UpdateAsync(
+ int id,
+ string accessToken,
+ string? refreshToken,
+ int expiresIn
+ )
+ {
+ var expiresAt = clock.GetCurrentInstant() + Duration.FromSeconds(expiresIn);
+
+ return await conn.QueryFirstAsync(
+ """
+ update api_tokens set access_token = @accessToken, refresh_token = @refreshToken,
+ expires_at = @expiresAt where id = @id
+ returning *
+ """,
+ new
+ {
+ id,
+ accessToken,
+ refreshToken,
+ expiresAt,
+ }
+ );
+ }
+
+ public void Dispose()
+ {
+ conn.Dispose();
+ GC.SuppressFinalize(this);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await conn.DisposeAsync();
+ GC.SuppressFinalize(this);
+ }
+}
diff --git a/Catalogger.Backend/Database/Dapper/Repositories/GuildRepository.cs b/Catalogger.Backend/Database/Repositories/GuildRepository.cs
similarity index 95%
rename from Catalogger.Backend/Database/Dapper/Repositories/GuildRepository.cs
rename to Catalogger.Backend/Database/Repositories/GuildRepository.cs
index 44d39b3..de62b1d 100644
--- a/Catalogger.Backend/Database/Dapper/Repositories/GuildRepository.cs
+++ b/Catalogger.Backend/Database/Repositories/GuildRepository.cs
@@ -17,7 +17,7 @@ using Catalogger.Backend.Database.Models;
using Dapper;
using Remora.Rest.Core;
-namespace Catalogger.Backend.Database.Dapper.Repositories;
+namespace Catalogger.Backend.Database.Repositories;
public class GuildRepository(ILogger logger, DatabaseConnection conn)
: IDisposable,
@@ -89,8 +89,8 @@ public class GuildRepository(ILogger logger, DatabaseConnection conn)
public async Task UpdateChannelConfigAsync(Snowflake id, Guild.ChannelConfig config) =>
await conn.ExecuteAsync(
- "update guilds set channels = @Channels where id = @Id",
- new { Id = id, Channels = config }
+ "update guilds set channels = @Channels::jsonb where id = @Id",
+ new { Id = id.Value, Channels = config }
);
public void Dispose()
diff --git a/Catalogger.Backend/Database/Dapper/Repositories/InviteRepository.cs b/Catalogger.Backend/Database/Repositories/InviteRepository.cs
similarity index 97%
rename from Catalogger.Backend/Database/Dapper/Repositories/InviteRepository.cs
rename to Catalogger.Backend/Database/Repositories/InviteRepository.cs
index 45183de..9ea7382 100644
--- a/Catalogger.Backend/Database/Dapper/Repositories/InviteRepository.cs
+++ b/Catalogger.Backend/Database/Repositories/InviteRepository.cs
@@ -17,7 +17,7 @@ using Catalogger.Backend.Database.Models;
using Dapper;
using Remora.Rest.Core;
-namespace Catalogger.Backend.Database.Dapper.Repositories;
+namespace Catalogger.Backend.Database.Repositories;
public class InviteRepository(ILogger logger, DatabaseConnection conn)
: IDisposable,
diff --git a/Catalogger.Backend/Database/Dapper/Repositories/MessageRepository.cs b/Catalogger.Backend/Database/Repositories/MessageRepository.cs
similarity index 99%
rename from Catalogger.Backend/Database/Dapper/Repositories/MessageRepository.cs
rename to Catalogger.Backend/Database/Repositories/MessageRepository.cs
index 7cce3c0..17925c3 100644
--- a/Catalogger.Backend/Database/Dapper/Repositories/MessageRepository.cs
+++ b/Catalogger.Backend/Database/Repositories/MessageRepository.cs
@@ -20,7 +20,7 @@ using Remora.Discord.API;
using Remora.Discord.API.Abstractions.Gateway.Events;
using Remora.Rest.Core;
-namespace Catalogger.Backend.Database.Dapper.Repositories;
+namespace Catalogger.Backend.Database.Repositories;
public class MessageRepository(
ILogger logger,
diff --git a/Catalogger.Backend/Database/Dapper/Repositories/WatchlistRepository.cs b/Catalogger.Backend/Database/Repositories/WatchlistRepository.cs
similarity index 96%
rename from Catalogger.Backend/Database/Dapper/Repositories/WatchlistRepository.cs
rename to Catalogger.Backend/Database/Repositories/WatchlistRepository.cs
index 0f06ab8..8177f78 100644
--- a/Catalogger.Backend/Database/Dapper/Repositories/WatchlistRepository.cs
+++ b/Catalogger.Backend/Database/Repositories/WatchlistRepository.cs
@@ -17,7 +17,7 @@ using Catalogger.Backend.Database.Models;
using Dapper;
using Remora.Rest.Core;
-namespace Catalogger.Backend.Database.Dapper.Repositories;
+namespace Catalogger.Backend.Database.Repositories;
public class WatchlistRepository(ILogger logger, DatabaseConnection conn)
: IDisposable,
diff --git a/Catalogger.Backend/Database/setup_migrations.sql b/Catalogger.Backend/Database/setup_migrations.sql
new file mode 100644
index 0000000..d79df46
--- /dev/null
+++ b/Catalogger.Backend/Database/setup_migrations.sql
@@ -0,0 +1,4 @@
+create table if not exists migrations (
+ migration_name text primary key,
+ applied_at timestamptz not null default (now())
+);
diff --git a/Catalogger.Backend/Extensions/StartupExtensions.cs b/Catalogger.Backend/Extensions/StartupExtensions.cs
index 32a8ca3..176d821 100644
--- a/Catalogger.Backend/Extensions/StartupExtensions.cs
+++ b/Catalogger.Backend/Extensions/StartupExtensions.cs
@@ -22,12 +22,9 @@ using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Cache.RedisCache;
using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper;
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
using Catalogger.Backend.Database.Redis;
+using Catalogger.Backend.Database.Repositories;
using Catalogger.Backend.Services;
-using Microsoft.EntityFrameworkCore;
using NodaTime;
using Prometheus;
using Remora.Discord.API;
@@ -106,6 +103,7 @@ public static class StartupExtensions
services
.AddSingleton(SystemClock.Instance)
.AddDatabasePool()
+ .AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
@@ -159,6 +157,7 @@ public static class StartupExtensions
return services;
return services
+ .AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
@@ -197,17 +196,8 @@ public static class StartupExtensions
DatabasePool.ConfigureDapper();
- await using (var db = scope.ServiceProvider.GetRequiredService())
- {
- var migrationCount = (await db.Database.GetPendingMigrationsAsync()).Count();
- if (migrationCount != 0)
- {
- logger.Information("Applying {Count} database migrations", migrationCount);
- await db.Database.MigrateAsync();
- }
- else
- logger.Information("There are no pending migrations");
- }
+ await using var migrator = scope.ServiceProvider.GetRequiredService();
+ await migrator.Migrate();
var config = scope.ServiceProvider.GetRequiredService();
var slashService = scope.ServiceProvider.GetRequiredService();
diff --git a/Catalogger.Backend/Program.cs b/Catalogger.Backend/Program.cs
index 035d669..27710e0 100644
--- a/Catalogger.Backend/Program.cs
+++ b/Catalogger.Backend/Program.cs
@@ -16,7 +16,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Catalogger.Backend.Bot.Commands;
-using Catalogger.Backend.Database;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Prometheus;
@@ -110,8 +109,7 @@ if (!config.Logging.EnableMetrics)
builder.Services.AddHostedService();
builder
- .Services.AddDbContext()
- .MaybeAddDashboardServices(config)
+ .Services.MaybeAddDashboardServices(config)
.MaybeAddRedisCaches(config)
.AddCustomServices()
.AddEndpointsApiExplorer()
diff --git a/Catalogger.Backend/Services/BackgroundTasksService.cs b/Catalogger.Backend/Services/BackgroundTasksService.cs
index 936ed45..11a35da 100644
--- a/Catalogger.Backend/Services/BackgroundTasksService.cs
+++ b/Catalogger.Backend/Services/BackgroundTasksService.cs
@@ -13,8 +13,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-using Catalogger.Backend.Database.Dapper.Repositories;
-using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Database.Repositories;
namespace Catalogger.Backend.Services;
diff --git a/Catalogger.Backend/Services/GuildFetchService.cs b/Catalogger.Backend/Services/GuildFetchService.cs
index 040fdea..f843bb1 100644
--- a/Catalogger.Backend/Services/GuildFetchService.cs
+++ b/Catalogger.Backend/Services/GuildFetchService.cs
@@ -19,7 +19,6 @@ using Catalogger.Backend.Cache;
using Humanizer;
using Remora.Discord.API.Abstractions.Rest;
using Remora.Discord.API.Gateway.Commands;
-using Remora.Discord.Gateway;
using Remora.Rest.Core;
namespace Catalogger.Backend.Services;
diff --git a/Catalogger.Backend/Services/MetricsCollectionService.cs b/Catalogger.Backend/Services/MetricsCollectionService.cs
index cb0ec64..7ac9ffe 100644
--- a/Catalogger.Backend/Services/MetricsCollectionService.cs
+++ b/Catalogger.Backend/Services/MetricsCollectionService.cs
@@ -16,10 +16,8 @@
using System.Diagnostics;
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database;
-using Catalogger.Backend.Database.Dapper;
using Dapper;
using Humanizer;
-using Microsoft.EntityFrameworkCore;
using Prometheus;
namespace Catalogger.Backend.Services;
@@ -40,7 +38,6 @@ public class MetricsCollectionService(
var timer = CataloggerMetrics.MetricsCollectionTime.NewTimer();
await using var scope = services.CreateAsyncScope();
- await using var db = scope.ServiceProvider.GetRequiredService();
await using var conn = scope.ServiceProvider.GetRequiredService();
var messageCount = await conn.ExecuteScalarAsync("select count(id) from messages");
diff --git a/Catalogger.Backend/Services/WebhookExecutorService.cs b/Catalogger.Backend/Services/WebhookExecutorService.cs
index eed437e..dfde9b2 100644
--- a/Catalogger.Backend/Services/WebhookExecutorService.cs
+++ b/Catalogger.Backend/Services/WebhookExecutorService.cs
@@ -18,10 +18,10 @@ using System.Diagnostics.CodeAnalysis;
using Catalogger.Backend.Cache;
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Extensions;
+using OneOf;
using Remora.Discord.API;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Abstractions.Rest;
-using Remora.Discord.API.Objects;
using Remora.Rest.Core;
using Guild = Catalogger.Backend.Database.Models.Guild;
@@ -109,7 +109,7 @@ public class WebhookExecutorService(
}
var attachments = files
- .Select>(f => f)
+ .Select>(f => f)
.ToList();
if (embeds.Count == 0 && attachments.Count == 0)
diff --git a/Catalogger.Backend/packages.lock.json b/Catalogger.Backend/packages.lock.json
index cfc0acc..2af0ac1 100644
--- a/Catalogger.Backend/packages.lock.json
+++ b/Catalogger.Backend/packages.lock.json
@@ -8,27 +8,6 @@
"resolved": "2.1.35",
"contentHash": "YKRwjVfrG7GYOovlGyQoMvr1/IJdn+7QzNXJxyMh0YfFF5yvDmTYaJOVYWsckreNjGsGSEtrMTpnzxTUq/tZQw=="
},
- "EFCore.NamingConventions": {
- "type": "Direct",
- "requested": "[8.0.3, )",
- "resolved": "8.0.3",
- "contentHash": "TdDarM6kyIS2oVIhrs3W+r+xL/76ooFJxIXxfhzsNJQu0pB9VdFZwuyKvKJnhoc7OHYFNTBP08AN37kr4CPc+Q==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore": "[8.0.0, 9.0.0)",
- "Microsoft.EntityFrameworkCore.Relational": "[8.0.0, 9.0.0)",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
- }
- },
- "EntityFrameworkCore.Exceptions.PostgreSQL": {
- "type": "Direct",
- "requested": "[8.1.3, )",
- "resolved": "8.1.3",
- "contentHash": "hqTsPy2SeupzawK/AeH5/8/K7+KEdZjQbyKVlxBX45ei86eU8D14X9E06uS6MX+J5TdSKY6o+WXGS7G2k8Xvyw==",
- "dependencies": {
- "EntityFrameworkCore.Exceptions.Common": "8.1.3",
- "Npgsql": "8.0.3"
- }
- },
"Humanizer.Core": {
"type": "Direct",
"requested": "[2.14.1, )",
@@ -65,31 +44,6 @@
"Microsoft.OpenApi": "1.4.3"
}
},
- "Microsoft.EntityFrameworkCore": {
- "type": "Direct",
- "requested": "[8.0.8, )",
- "resolved": "8.0.8",
- "contentHash": "iK+jrJzkfbIxutB7or808BPmJtjUEi5O+eSM7cLDwsyde6+3iOujCSfWnrHrLxY3u+EQrJD+aD8DJ6ogPA2Rtw==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore.Abstractions": "8.0.8",
- "Microsoft.EntityFrameworkCore.Analyzers": "8.0.8",
- "Microsoft.Extensions.Caching.Memory": "8.0.0",
- "Microsoft.Extensions.Logging": "8.0.0"
- }
- },
- "Microsoft.EntityFrameworkCore.Design": {
- "type": "Direct",
- "requested": "[8.0.8, )",
- "resolved": "8.0.8",
- "contentHash": "MmQAMHdjZR8Iyn/FVQrh9weJQTn0HqtKa3vELS9ffQJat/qXgnTam9M9jqvePphjkYp5Scee+Hy+EJR4nmWmOA==",
- "dependencies": {
- "Humanizer.Core": "2.14.1",
- "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0",
- "Microsoft.EntityFrameworkCore.Relational": "8.0.8",
- "Microsoft.Extensions.DependencyModel": "8.0.1",
- "Mono.TextTemplating": "2.2.1"
- }
- },
"Newtonsoft.Json": {
"type": "Direct",
"requested": "[13.0.3, )",
@@ -123,28 +77,6 @@
"Microsoft.Extensions.Logging.Abstractions": "8.0.0"
}
},
- "Npgsql.EntityFrameworkCore.PostgreSQL": {
- "type": "Direct",
- "requested": "[8.0.8, )",
- "resolved": "8.0.8",
- "contentHash": "D5WWJZJTgZYUmGv66BARXbTlinp2a5f5RueJqGYoHuWJw02J0i2va/RA+8N4A5hLORK5YKMRqXhFWtKsZdrksw==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore": "8.0.8",
- "Microsoft.EntityFrameworkCore.Abstractions": "8.0.8",
- "Microsoft.EntityFrameworkCore.Relational": "8.0.8",
- "Npgsql": "8.0.4"
- }
- },
- "Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime": {
- "type": "Direct",
- "requested": "[8.0.8, )",
- "resolved": "8.0.8",
- "contentHash": "lq+0sHuHySoYb6AxluUN+4TMrLTNOL4f5kKtf8eWoN0IoTC8cTL6IXOhfTOI8klUOdnOTMx3f7mdDKdR5AV1Jw==",
- "dependencies": {
- "Npgsql.EntityFrameworkCore.PostgreSQL": "8.0.8",
- "Npgsql.NodaTime": "8.0.4"
- }
- },
"Npgsql.NodaTime": {
"type": "Direct",
"requested": "[8.0.5, )",
@@ -291,14 +223,6 @@
"resolved": "8.2.2",
"contentHash": "+zIp8d3sbtYaRbM6hqDs4Ui/z34j7DcUmleruZlYLE4CVxXq+MO8XJyIs42vzeTYFX+k0Iq1dEbBUnQ4z/Gnrw=="
},
- "EntityFrameworkCore.Exceptions.Common": {
- "type": "Transitive",
- "resolved": "8.1.3",
- "contentHash": "nweeiVHx4HbDi6+TqendOe0QmN0a9v0AB5FaL83eToqFFztwGIhOqLfveKqJDYSCU51CJShW8kbU1onZLdZZSg==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore.Relational": "8.0.0"
- }
- },
"FuzzySharp": {
"type": "Transitive",
"resolved": "2.0.2",
@@ -318,84 +242,11 @@
"Newtonsoft.Json": "13.0.3"
}
},
- "Microsoft.Bcl.AsyncInterfaces": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
- },
- "Microsoft.CodeAnalysis.Analyzers": {
- "type": "Transitive",
- "resolved": "3.3.3",
- "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ=="
- },
- "Microsoft.CodeAnalysis.Common": {
- "type": "Transitive",
- "resolved": "4.5.0",
- "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==",
- "dependencies": {
- "Microsoft.CodeAnalysis.Analyzers": "3.3.3",
- "System.Collections.Immutable": "6.0.0",
- "System.Reflection.Metadata": "6.0.1",
- "System.Runtime.CompilerServices.Unsafe": "6.0.0",
- "System.Text.Encoding.CodePages": "6.0.0"
- }
- },
- "Microsoft.CodeAnalysis.CSharp": {
- "type": "Transitive",
- "resolved": "4.5.0",
- "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==",
- "dependencies": {
- "Microsoft.CodeAnalysis.Common": "[4.5.0]"
- }
- },
- "Microsoft.CodeAnalysis.CSharp.Workspaces": {
- "type": "Transitive",
- "resolved": "4.5.0",
- "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==",
- "dependencies": {
- "Humanizer.Core": "2.14.1",
- "Microsoft.CodeAnalysis.CSharp": "[4.5.0]",
- "Microsoft.CodeAnalysis.Common": "[4.5.0]",
- "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]"
- }
- },
- "Microsoft.CodeAnalysis.Workspaces.Common": {
- "type": "Transitive",
- "resolved": "4.5.0",
- "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==",
- "dependencies": {
- "Humanizer.Core": "2.14.1",
- "Microsoft.Bcl.AsyncInterfaces": "6.0.0",
- "Microsoft.CodeAnalysis.Common": "[4.5.0]",
- "System.Composition": "6.0.0",
- "System.IO.Pipelines": "6.0.3",
- "System.Threading.Channels": "6.0.0"
- }
- },
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
- "Microsoft.EntityFrameworkCore.Abstractions": {
- "type": "Transitive",
- "resolved": "8.0.8",
- "contentHash": "9mMQkZsfL1c2iifBD8MWRmwy59rvsVtR9NOezJj7+g1j4P7g49MJHd8k8faC/v7d5KuHkQ6KOQiSItvoRt9PXA=="
- },
- "Microsoft.EntityFrameworkCore.Analyzers": {
- "type": "Transitive",
- "resolved": "8.0.8",
- "contentHash": "OlAXMU+VQgLz5y5/SBkLvAa9VeiR3dlJqgIebEEH2M2NGA3evm68/Tv7SLWmSxwnEAtA3nmDEZF2pacK6eXh4Q=="
- },
- "Microsoft.EntityFrameworkCore.Relational": {
- "type": "Transitive",
- "resolved": "8.0.8",
- "contentHash": "3WnrwdXxKg4L98cDx0lNEEau8U2lsfuBJCs0Yzht+5XVTmahboM7MukKfQHAzVsHUPszm6ci929S7Qas0WfVHA==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore": "8.0.8",
- "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
- }
- },
"Microsoft.Extensions.ApiDescription.Server": {
"type": "Transitive",
"resolved": "6.0.5",
@@ -585,14 +436,6 @@
"resolved": "1.6.14",
"contentHash": "tTaBT8qjk3xINfESyOPE2rIellPvB7qpVqiWiyA/lACVvz+xOGiXhFUfohcx82NLbi5avzLW0lx+s6oAqQijfw=="
},
- "Mono.TextTemplating": {
- "type": "Transitive",
- "resolved": "2.2.1",
- "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==",
- "dependencies": {
- "System.CodeDom": "4.4.0"
- }
- },
"Newtonsoft.Json.Bson": {
"type": "Transitive",
"resolved": "1.0.2",
@@ -860,72 +703,11 @@
"resolved": "6.8.1",
"contentHash": "lpEszYJ7vZaTTE5Dp8MrsbSHrgDfjhDMjzW1qOA1Xs1Dnj3ZRBJAcPZUTsa5Bva+nLaw91JJ8OI8FkSg8hhIyA=="
},
- "System.CodeDom": {
- "type": "Transitive",
- "resolved": "4.4.0",
- "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA=="
- },
- "System.Collections.Immutable": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==",
- "dependencies": {
- "System.Runtime.CompilerServices.Unsafe": "6.0.0"
- }
- },
"System.ComponentModel.Annotations": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
},
- "System.Composition": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==",
- "dependencies": {
- "System.Composition.AttributedModel": "6.0.0",
- "System.Composition.Convention": "6.0.0",
- "System.Composition.Hosting": "6.0.0",
- "System.Composition.Runtime": "6.0.0",
- "System.Composition.TypedParts": "6.0.0"
- }
- },
- "System.Composition.AttributedModel": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w=="
- },
- "System.Composition.Convention": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==",
- "dependencies": {
- "System.Composition.AttributedModel": "6.0.0"
- }
- },
- "System.Composition.Hosting": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==",
- "dependencies": {
- "System.Composition.Runtime": "6.0.0"
- }
- },
- "System.Composition.Runtime": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg=="
- },
- "System.Composition.TypedParts": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==",
- "dependencies": {
- "System.Composition.AttributedModel": "6.0.0",
- "System.Composition.Hosting": "6.0.0",
- "System.Composition.Runtime": "6.0.0"
- }
- },
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "8.0.0",
@@ -933,29 +715,13 @@
},
"System.IO.Pipelines": {
"type": "Transitive",
- "resolved": "6.0.3",
- "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw=="
- },
- "System.Reflection.Metadata": {
- "type": "Transitive",
- "resolved": "6.0.1",
- "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==",
- "dependencies": {
- "System.Collections.Immutable": "6.0.0"
- }
+ "resolved": "5.0.1",
+ "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
- },
- "System.Text.Encoding.CodePages": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
- "dependencies": {
- "System.Runtime.CompilerServices.Unsafe": "6.0.0"
- }
+ "resolved": "4.7.1",
+ "contentHash": "zOHkQmzPCn5zm/BH+cxC1XbUS3P4Yoi3xzW7eRgVpDR2tPGSzyMZ17Ig1iRkfJuY0nhxkQQde8pgePNiA7z7TQ=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",