excise entity framework from all remaining code
This commit is contained in:
parent
d6c3133d52
commit
ff92c5f335
62 changed files with 402 additions and 1339 deletions
1
.idea/.idea.catalogger/.idea/sqldialects.xml
generated
1
.idea/.idea.catalogger/.idea/sqldialects.xml
generated
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="SqlDialectMappings">
|
<component name="SqlDialectMappings">
|
||||||
|
<file url="file://$PROJECT_DIR$/Catalogger.Backend/Database/Migrations/001_init.up.sql" dialect="PostgreSQL" />
|
||||||
<file url="PROJECT" dialect="PostgreSQL" />
|
<file url="PROJECT" dialect="PostgreSQL" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Catalogger.Backend.Database;
|
|
||||||
using Catalogger.Backend.Database.Models;
|
using Catalogger.Backend.Database.Models;
|
||||||
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Api;
|
namespace Catalogger.Backend.Api;
|
||||||
|
|
@ -28,7 +28,7 @@ public class DiscordRequestService
|
||||||
private readonly ApiCache _apiCache;
|
private readonly ApiCache _apiCache;
|
||||||
private readonly Config _config;
|
private readonly Config _config;
|
||||||
private readonly IClock _clock;
|
private readonly IClock _clock;
|
||||||
private readonly DatabaseContext _db;
|
private readonly ApiTokenRepository _tokenRepository;
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions JsonOptions =
|
private static readonly JsonSerializerOptions JsonOptions =
|
||||||
new() { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
|
new() { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
|
||||||
|
|
@ -38,14 +38,14 @@ public class DiscordRequestService
|
||||||
ApiCache apiCache,
|
ApiCache apiCache,
|
||||||
Config config,
|
Config config,
|
||||||
IClock clock,
|
IClock clock,
|
||||||
DatabaseContext db
|
ApiTokenRepository tokenRepository
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_logger = logger.ForContext<DiscordRequestService>();
|
_logger = logger.ForContext<DiscordRequestService>();
|
||||||
_apiCache = apiCache;
|
_apiCache = apiCache;
|
||||||
_config = config;
|
_config = config;
|
||||||
_clock = clock;
|
_clock = clock;
|
||||||
_db = db;
|
_tokenRepository = tokenRepository;
|
||||||
|
|
||||||
_httpClient = new HttpClient();
|
_httpClient = new HttpClient();
|
||||||
_httpClient.DefaultRequestHeaders.Add(
|
_httpClient.DefaultRequestHeaders.Add(
|
||||||
|
|
@ -154,16 +154,13 @@ public class DiscordRequestService
|
||||||
var meUser = await GetMeAsync($"Bearer {token.AccessToken}");
|
var meUser = await GetMeAsync($"Bearer {token.AccessToken}");
|
||||||
var meGuilds = await GetGuildsAsync($"Bearer {token.AccessToken}");
|
var meGuilds = await GetGuildsAsync($"Bearer {token.AccessToken}");
|
||||||
|
|
||||||
var apiToken = new ApiToken
|
var apiToken = await _tokenRepository.CreateAsync(
|
||||||
{
|
ApiUtils.RandomToken(64),
|
||||||
DashboardToken = ApiUtils.RandomToken(64),
|
meUser.Id,
|
||||||
UserId = meUser.Id,
|
token.AccessToken,
|
||||||
AccessToken = token.AccessToken,
|
token.RefreshToken,
|
||||||
RefreshToken = token.RefreshToken,
|
token.ExpiresIn
|
||||||
ExpiresAt = _clock.GetCurrentInstant() + Duration.FromSeconds(token.ExpiresIn),
|
);
|
||||||
};
|
|
||||||
_db.Add(apiToken);
|
|
||||||
await _db.SaveChangesAsync(ct);
|
|
||||||
|
|
||||||
return (apiToken, meUser, meGuilds);
|
return (apiToken, meUser, meGuilds);
|
||||||
}
|
}
|
||||||
|
|
@ -231,8 +228,12 @@ public class DiscordRequestService
|
||||||
token.RefreshToken = discordToken.RefreshToken;
|
token.RefreshToken = discordToken.RefreshToken;
|
||||||
token.ExpiresAt = _clock.GetCurrentInstant() + Duration.FromSeconds(discordToken.ExpiresIn);
|
token.ExpiresAt = _clock.GetCurrentInstant() + Duration.FromSeconds(discordToken.ExpiresIn);
|
||||||
|
|
||||||
_db.Update(token);
|
await _tokenRepository.UpdateAsync(
|
||||||
await _db.SaveChangesAsync();
|
token.Id,
|
||||||
|
discordToken.AccessToken,
|
||||||
|
discordToken.RefreshToken,
|
||||||
|
discordToken.ExpiresIn
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")]
|
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")]
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
|
@ -26,7 +25,7 @@ public partial class GuildsController
|
||||||
public async Task<IActionResult> AddIgnoredChannelAsync(string id, ulong channelId)
|
public async Task<IActionResult> AddIgnoredChannelAsync(string id, ulong channelId)
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildConfig = await db.GetGuildAsync(guildId);
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
if (guildConfig.Channels.IgnoredChannels.Contains(channelId))
|
if (guildConfig.Channels.IgnoredChannels.Contains(channelId))
|
||||||
return NoContent();
|
return NoContent();
|
||||||
|
|
@ -47,8 +46,7 @@ public partial class GuildsController
|
||||||
return NoContent();
|
return NoContent();
|
||||||
|
|
||||||
guildConfig.Channels.IgnoredChannels.Add(channelId);
|
guildConfig.Channels.IgnoredChannels.Add(channelId);
|
||||||
db.Update(guildConfig);
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
|
||||||
await db.SaveChangesAsync();
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
@ -57,11 +55,10 @@ public partial class GuildsController
|
||||||
public async Task<IActionResult> RemoveIgnoredChannelAsync(string id, ulong channelId)
|
public async Task<IActionResult> RemoveIgnoredChannelAsync(string id, ulong channelId)
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildConfig = await db.GetGuildAsync(guildId);
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
guildConfig.Channels.IgnoredChannels.Remove(channelId);
|
guildConfig.Channels.IgnoredChannels.Remove(channelId);
|
||||||
db.Update(guildConfig);
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
|
||||||
await db.SaveChangesAsync();
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +78,7 @@ public partial class GuildsController
|
||||||
public async Task<IActionResult> AddIgnoredUserAsync(string id, ulong userId)
|
public async Task<IActionResult> AddIgnoredUserAsync(string id, ulong userId)
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildConfig = await db.GetGuildAsync(guildId);
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
if (guildConfig.Channels.IgnoredUsers.Contains(userId))
|
if (guildConfig.Channels.IgnoredUsers.Contains(userId))
|
||||||
return NoContent();
|
return NoContent();
|
||||||
|
|
@ -91,8 +88,7 @@ public partial class GuildsController
|
||||||
return NoContent();
|
return NoContent();
|
||||||
|
|
||||||
guildConfig.Channels.IgnoredUsers.Add(userId);
|
guildConfig.Channels.IgnoredUsers.Add(userId);
|
||||||
db.Update(guildConfig);
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
|
||||||
await db.SaveChangesAsync();
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
@ -101,11 +97,10 @@ public partial class GuildsController
|
||||||
public async Task<IActionResult> RemoveIgnoredUserAsync(string id, ulong userId)
|
public async Task<IActionResult> RemoveIgnoredUserAsync(string id, ulong userId)
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildConfig = await db.GetGuildAsync(guildId);
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
guildConfig.Channels.IgnoredUsers.Remove(userId);
|
guildConfig.Channels.IgnoredUsers.Remove(userId);
|
||||||
db.Update(guildConfig);
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
|
||||||
await db.SaveChangesAsync();
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Catalogger.Backend.Api.Middleware;
|
using Catalogger.Backend.Api.Middleware;
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
|
||||||
|
|
@ -31,7 +30,7 @@ public partial class GuildsController
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildChannels = channelCache.GuildChannels(guildId).ToList();
|
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}");
|
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;
|
guildConfig.Channels.Redirects[source.ID.Value] = target.ID.Value;
|
||||||
db.Update(guildConfig);
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
|
||||||
await db.SaveChangesAsync();
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +70,7 @@ public partial class GuildsController
|
||||||
public async Task<IActionResult> DeleteRedirectAsync(string id, ulong channelId)
|
public async Task<IActionResult> DeleteRedirectAsync(string id, ulong channelId)
|
||||||
{
|
{
|
||||||
var (guildId, _) = await ParseGuildAsync(id);
|
var (guildId, _) = await ParseGuildAsync(id);
|
||||||
var guildConfig = await db.GetGuildAsync(guildId.Value);
|
var guildConfig = await guildRepository.GetAsync(guildId);
|
||||||
|
|
||||||
if (!guildConfig.Channels.Redirects.ContainsKey(channelId))
|
if (!guildConfig.Channels.Redirects.ContainsKey(channelId))
|
||||||
throw new ApiError(
|
throw new ApiError(
|
||||||
|
|
@ -82,8 +80,7 @@ public partial class GuildsController
|
||||||
);
|
);
|
||||||
|
|
||||||
guildConfig.Channels.Redirects.Remove(channelId, out _);
|
guildConfig.Channels.Redirects.Remove(channelId, out _);
|
||||||
db.Update(guildConfig);
|
await guildRepository.UpdateChannelConfigAsync(guildId, guildConfig.Channels);
|
||||||
await db.SaveChangesAsync();
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,10 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Catalogger.Backend.Api.Middleware;
|
using Catalogger.Backend.Api.Middleware;
|
||||||
using Catalogger.Backend.Bot;
|
using Catalogger.Backend.Bot;
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Remora.Discord.Extensions.Embeds;
|
using Remora.Discord.Extensions.Embeds;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Api;
|
namespace Catalogger.Backend.Api;
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,7 @@ using Catalogger.Backend.Api.Middleware;
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database;
|
||||||
using Catalogger.Backend.Database.Dapper;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
|
|
@ -32,7 +31,6 @@ namespace Catalogger.Backend.Api;
|
||||||
[Route("/api/guilds/{id}")]
|
[Route("/api/guilds/{id}")]
|
||||||
public partial class GuildsController(
|
public partial class GuildsController(
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
DatabaseContext db,
|
|
||||||
DatabaseConnection dbConn,
|
DatabaseConnection dbConn,
|
||||||
GuildRepository guildRepository,
|
GuildRepository guildRepository,
|
||||||
GuildCache guildCache,
|
GuildCache guildCache,
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,8 @@
|
||||||
|
|
||||||
using Catalogger.Backend.Api.Middleware;
|
using Catalogger.Backend.Api.Middleware;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Extensions;
|
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
|
||||||
|
|
||||||
namespace Catalogger.Backend.Api;
|
namespace Catalogger.Backend.Api;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Catalogger.Backend.Database;
|
|
||||||
using Catalogger.Backend.Database.Models;
|
using Catalogger.Backend.Database.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Api.Middleware;
|
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)
|
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 token = ctx.Request.Headers.Authorization.ToString();
|
||||||
|
|
||||||
var apiToken = await db.ApiTokens.FirstOrDefaultAsync(t =>
|
var apiToken = await tokenRepository.GetAsync(token);
|
||||||
t.DashboardToken == token && t.ExpiresAt > clock.GetCurrentInstant()
|
|
||||||
);
|
|
||||||
if (apiToken == null)
|
if (apiToken == null)
|
||||||
{
|
{
|
||||||
if (requireAuth)
|
if (requireAuth)
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,7 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ using System.ComponentModel;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,7 @@ using System.Drawing;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
using Remora.Discord.API.Objects;
|
using Remora.Discord.API.Objects;
|
||||||
using Remora.Discord.Commands.Feedback.Services;
|
|
||||||
using Remora.Discord.Pagination;
|
using Remora.Discord.Pagination;
|
||||||
using Remora.Discord.Pagination.Extensions;
|
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Bot;
|
namespace Catalogger.Backend.Bot;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Remora.Discord.API;
|
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
using Remora.Discord.Gateway.Responders;
|
using Remora.Discord.Gateway.Responders;
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,13 @@
|
||||||
|
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
using Remora.Discord.Extensions.Embeds;
|
using Remora.Discord.Extensions.Embeds;
|
||||||
using Remora.Discord.Gateway.Responders;
|
using Remora.Discord.Gateway.Responders;
|
||||||
using Remora.Results;
|
using Remora.Results;
|
||||||
using Guild = Catalogger.Backend.Database.Models.Guild;
|
|
||||||
|
|
||||||
namespace Catalogger.Backend.Bot.Responders.Guilds;
|
namespace Catalogger.Backend.Bot.Responders.Guilds;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@
|
||||||
|
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using NodaTime.Extensions;
|
using NodaTime.Extensions;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
|
|
||||||
|
|
@ -7,25 +7,20 @@
|
||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Database/**/*.sql" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3"/>
|
|
||||||
<PackageReference Include="EntityFrameworkCore.Exceptions.PostgreSQL" Version="8.1.3"/>
|
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.14.1"/>
|
<PackageReference Include="Humanizer.Core" Version="2.14.1"/>
|
||||||
<PackageReference Include="LazyCache" Version="2.4.0"/>
|
<PackageReference Include="LazyCache" Version="2.4.0"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8"/>
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8"/>
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8"/>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
|
||||||
<PackageReference Include="NodaTime" Version="3.1.12"/>
|
<PackageReference Include="NodaTime" Version="3.1.12"/>
|
||||||
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.2.0"/>
|
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.2.0"/>
|
||||||
<PackageReference Include="Npgsql" Version="8.0.5" />
|
<PackageReference Include="Npgsql" Version="8.0.5" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.8"/>
|
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="8.0.8"/>
|
|
||||||
<PackageReference Include="Npgsql.NodaTime" Version="8.0.5" />
|
<PackageReference Include="Npgsql.NodaTime" Version="8.0.5" />
|
||||||
<PackageReference Include="Polly.Core" Version="8.4.2"/>
|
<PackageReference Include="Polly.Core" Version="8.4.2"/>
|
||||||
<PackageReference Include="Polly.RateLimiting" Version="8.4.2"/>
|
<PackageReference Include="Polly.RateLimiting" Version="8.4.2"/>
|
||||||
|
|
@ -42,4 +37,8 @@
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.8.1"/>
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.8.1"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Database\Dapper\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ using System.Data.Common;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Database.Dapper;
|
namespace Catalogger.Backend.Database;
|
||||||
|
|
||||||
public class DatabaseConnection(Guid id, ILogger logger, NpgsqlConnection inner)
|
public class DatabaseConnection(Guid id, ILogger logger, NpgsqlConnection inner)
|
||||||
: DbConnection,
|
: DbConnection,
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
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<Guild> Guilds { get; set; }
|
|
||||||
public DbSet<Message> Messages { get; set; }
|
|
||||||
public DbSet<IgnoredMessage> IgnoredMessages { get; set; }
|
|
||||||
public DbSet<Invite> Invites { get; set; }
|
|
||||||
public DbSet<Watchlist> Watchlists { get; set; }
|
|
||||||
public DbSet<ApiToken> 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<ulong>().HaveConversion<UlongValueConverter>();
|
|
||||||
configurationBuilder.Properties<List<ulong>>().HaveConversion<UlongArrayValueConverter>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly ValueComparer<List<ulong>> 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<ulong[]> 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<Guild>()
|
|
||||||
.Property(g => g.KeyRoles)
|
|
||||||
.Metadata.SetValueComparer(UlongArrayValueComparer);
|
|
||||||
|
|
||||||
modelBuilder.Entity<Invite>().HasKey(i => i.Code);
|
|
||||||
modelBuilder.Entity<Invite>().HasIndex(i => i.GuildId);
|
|
||||||
|
|
||||||
modelBuilder.Entity<Watchlist>().HasKey(w => new { w.GuildId, w.UserId });
|
|
||||||
modelBuilder.Entity<Watchlist>().Property(w => w.AddedAt).HasDefaultValueSql("now()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DesignTimeDatabaseContextFactory : IDesignTimeDbContextFactory<DatabaseContext>
|
|
||||||
{
|
|
||||||
public DatabaseContext CreateDbContext(string[] args)
|
|
||||||
{
|
|
||||||
// Read the configuration file
|
|
||||||
var config =
|
|
||||||
new ConfigurationBuilder()
|
|
||||||
.AddConfiguration()
|
|
||||||
.Build()
|
|
||||||
// Get the configuration as our config class
|
|
||||||
.Get<Config>() ?? new();
|
|
||||||
|
|
||||||
return new DatabaseContext(config, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UlongValueConverter()
|
|
||||||
: ValueConverter<ulong, long>(
|
|
||||||
convertToProviderExpression: x => (long)x,
|
|
||||||
convertFromProviderExpression: x => (ulong)x
|
|
||||||
);
|
|
||||||
|
|
||||||
public class UlongArrayValueConverter()
|
|
||||||
: ValueConverter<List<ulong>, List<long>>(
|
|
||||||
convertToProviderExpression: x => x.Select(i => (long)i).ToList(),
|
|
||||||
convertFromProviderExpression: x => x.Select(i => (ulong)i).ToList()
|
|
||||||
);
|
|
||||||
145
Catalogger.Backend/Database/DatabaseMigrator.cs
Normal file
145
Catalogger.Backend/Database/DatabaseMigrator.cs
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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<MigrationEntry?> GetCurrentMigration()
|
||||||
|
{
|
||||||
|
// Check if the migrations table exists
|
||||||
|
var hasMigrationTable =
|
||||||
|
await conn.QuerySingleOrDefaultAsync<int>(
|
||||||
|
"SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'migrations'"
|
||||||
|
) == 1;
|
||||||
|
// If so, return the current migration
|
||||||
|
if (hasMigrationTable)
|
||||||
|
{
|
||||||
|
return await conn.QuerySingleOrDefaultAsync<MigrationEntry>(
|
||||||
|
"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<string> 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<string> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,13 +15,12 @@
|
||||||
|
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using Catalogger.Backend.Database.Models;
|
using Catalogger.Backend.Database.Models;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Database.Dapper;
|
namespace Catalogger.Backend.Database;
|
||||||
|
|
||||||
public class DatabasePool
|
public class DatabasePool
|
||||||
{
|
{
|
||||||
6
Catalogger.Backend/Database/Migrations/001_init.down.sql
Normal file
6
Catalogger.Backend/Database/Migrations/001_init.down.sql
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
drop table api_tokens;
|
||||||
|
drop table watchlists;
|
||||||
|
drop table messages;
|
||||||
|
drop table invites;
|
||||||
|
drop table ignored_messages;
|
||||||
|
drop table guilds;
|
||||||
60
Catalogger.Backend/Database/Migrations/001_init.up.sql
Normal file
60
Catalogger.Backend/Database/Migrations/001_init.up.sql
Normal file
|
|
@ -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";
|
||||||
|
|
@ -1,180 +0,0 @@
|
||||||
// <auto-generated />
|
|
||||||
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
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
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<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<List<string>>("BannedSystems")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("banned_systems");
|
|
||||||
|
|
||||||
b.Property<Guild.ChannelConfig>("Channels")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("channels");
|
|
||||||
|
|
||||||
b.Property<List<long>>("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<long>("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<string>("Code")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("code");
|
|
||||||
|
|
||||||
b.Property<long>("GuildId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("guild_id");
|
|
||||||
|
|
||||||
b.Property<string>("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<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<int>("AttachmentSize")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("attachment_size");
|
|
||||||
|
|
||||||
b.Property<long>("ChannelId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("channel_id");
|
|
||||||
|
|
||||||
b.Property<byte[]>("EncryptedContent")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("content");
|
|
||||||
|
|
||||||
b.Property<byte[]>("EncryptedMetadata")
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("metadata");
|
|
||||||
|
|
||||||
b.Property<byte[]>("EncryptedUsername")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("username");
|
|
||||||
|
|
||||||
b.Property<long>("GuildId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("guild_id");
|
|
||||||
|
|
||||||
b.Property<string>("Member")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("member");
|
|
||||||
|
|
||||||
b.Property<long?>("OriginalId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("original_id");
|
|
||||||
|
|
||||||
b.Property<string>("System")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("system");
|
|
||||||
|
|
||||||
b.Property<long>("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<long>("GuildId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("guild_id");
|
|
||||||
|
|
||||||
b.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("user_id");
|
|
||||||
|
|
||||||
b.Property<Instant>("AddedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("added_at")
|
|
||||||
.HasDefaultValueSql("now()");
|
|
||||||
|
|
||||||
b.Property<long>("ModeratorId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("moderator_id");
|
|
||||||
|
|
||||||
b.Property<string>("Reason")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("reason");
|
|
||||||
|
|
||||||
b.HasKey("GuildId", "UserId")
|
|
||||||
.HasName("pk_watchlists");
|
|
||||||
|
|
||||||
b.ToTable("watchlists", (string)null);
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class Init : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "guilds",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
id = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
channels = table.Column<Guild.ChannelConfig>(type: "jsonb", nullable: false),
|
|
||||||
banned_systems = table.Column<List<string>>(type: "text[]", nullable: false),
|
|
||||||
key_roles = table.Column<List<long>>(type: "bigint[]", nullable: false),
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("pk_guilds", x => x.id);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "ignored_messages",
|
|
||||||
columns: table => new { id = table.Column<long>(type: "bigint", nullable: false) },
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("pk_ignored_messages", x => x.id);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "invites",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
code = table.Column<string>(type: "text", nullable: false),
|
|
||||||
guild_id = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
name = table.Column<string>(type: "text", nullable: false),
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("pk_invites", x => x.code);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "messages",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
id = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
original_id = table.Column<long>(type: "bigint", nullable: true),
|
|
||||||
user_id = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
channel_id = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
guild_id = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
member = table.Column<string>(type: "text", nullable: true),
|
|
||||||
system = table.Column<string>(type: "text", nullable: true),
|
|
||||||
username = table.Column<byte[]>(type: "bytea", nullable: false),
|
|
||||||
content = table.Column<byte[]>(type: "bytea", nullable: false),
|
|
||||||
metadata = table.Column<byte[]>(type: "bytea", nullable: true),
|
|
||||||
attachment_size = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("pk_messages", x => x.id);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "watchlists",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
guild_id = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
user_id = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
added_at = table.Column<Instant>(
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: false,
|
|
||||||
defaultValueSql: "now()"
|
|
||||||
),
|
|
||||||
moderator_id = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
reason = table.Column<string>(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"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,218 +0,0 @@
|
||||||
// <auto-generated />
|
|
||||||
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
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
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<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("AccessToken")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("access_token");
|
|
||||||
|
|
||||||
b.Property<string>("DashboardToken")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("dashboard_token");
|
|
||||||
|
|
||||||
b.Property<Instant>("ExpiresAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("expires_at");
|
|
||||||
|
|
||||||
b.Property<string>("RefreshToken")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("refresh_token");
|
|
||||||
|
|
||||||
b.Property<string>("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<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<List<string>>("BannedSystems")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("banned_systems");
|
|
||||||
|
|
||||||
b.Property<Guild.ChannelConfig>("Channels")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("channels");
|
|
||||||
|
|
||||||
b.Property<List<long>>("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<long>("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<string>("Code")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("code");
|
|
||||||
|
|
||||||
b.Property<long>("GuildId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("guild_id");
|
|
||||||
|
|
||||||
b.Property<string>("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<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<int>("AttachmentSize")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("attachment_size");
|
|
||||||
|
|
||||||
b.Property<long>("ChannelId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("channel_id");
|
|
||||||
|
|
||||||
b.Property<byte[]>("EncryptedContent")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("content");
|
|
||||||
|
|
||||||
b.Property<byte[]>("EncryptedMetadata")
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("metadata");
|
|
||||||
|
|
||||||
b.Property<byte[]>("EncryptedUsername")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("username");
|
|
||||||
|
|
||||||
b.Property<long>("GuildId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("guild_id");
|
|
||||||
|
|
||||||
b.Property<string>("Member")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("member");
|
|
||||||
|
|
||||||
b.Property<long?>("OriginalId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("original_id");
|
|
||||||
|
|
||||||
b.Property<string>("System")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("system");
|
|
||||||
|
|
||||||
b.Property<long>("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<long>("GuildId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("guild_id");
|
|
||||||
|
|
||||||
b.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("user_id");
|
|
||||||
|
|
||||||
b.Property<Instant>("AddedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("added_at")
|
|
||||||
.HasDefaultValueSql("now()");
|
|
||||||
|
|
||||||
b.Property<long>("ModeratorId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("moderator_id");
|
|
||||||
|
|
||||||
b.Property<string>("Reason")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("reason");
|
|
||||||
|
|
||||||
b.HasKey("GuildId", "UserId")
|
|
||||||
.HasName("pk_watchlists");
|
|
||||||
|
|
||||||
b.ToTable("watchlists", (string)null);
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using NodaTime;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Catalogger.Backend.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddDashboardTokens : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "api_tokens",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
id = table
|
|
||||||
.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation(
|
|
||||||
"Npgsql:ValueGenerationStrategy",
|
|
||||||
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn
|
|
||||||
),
|
|
||||||
dashboard_token = table.Column<string>(type: "text", nullable: false),
|
|
||||||
user_id = table.Column<string>(type: "text", nullable: false),
|
|
||||||
access_token = table.Column<string>(type: "text", nullable: false),
|
|
||||||
refresh_token = table.Column<string>(type: "text", nullable: true),
|
|
||||||
expires_at = table.Column<Instant>(
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: false
|
|
||||||
),
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("pk_api_tokens", x => x.id);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(name: "api_tokens");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,215 +0,0 @@
|
||||||
// <auto-generated />
|
|
||||||
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<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("AccessToken")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("access_token");
|
|
||||||
|
|
||||||
b.Property<string>("DashboardToken")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("dashboard_token");
|
|
||||||
|
|
||||||
b.Property<Instant>("ExpiresAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("expires_at");
|
|
||||||
|
|
||||||
b.Property<string>("RefreshToken")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("refresh_token");
|
|
||||||
|
|
||||||
b.Property<string>("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<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<List<string>>("BannedSystems")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]")
|
|
||||||
.HasColumnName("banned_systems");
|
|
||||||
|
|
||||||
b.Property<Guild.ChannelConfig>("Channels")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("channels");
|
|
||||||
|
|
||||||
b.Property<List<long>>("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<long>("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<string>("Code")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("code");
|
|
||||||
|
|
||||||
b.Property<long>("GuildId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("guild_id");
|
|
||||||
|
|
||||||
b.Property<string>("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<long>("Id")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<int>("AttachmentSize")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("attachment_size");
|
|
||||||
|
|
||||||
b.Property<long>("ChannelId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("channel_id");
|
|
||||||
|
|
||||||
b.Property<byte[]>("EncryptedContent")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("content");
|
|
||||||
|
|
||||||
b.Property<byte[]>("EncryptedMetadata")
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("metadata");
|
|
||||||
|
|
||||||
b.Property<byte[]>("EncryptedUsername")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("bytea")
|
|
||||||
.HasColumnName("username");
|
|
||||||
|
|
||||||
b.Property<long>("GuildId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("guild_id");
|
|
||||||
|
|
||||||
b.Property<string>("Member")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("member");
|
|
||||||
|
|
||||||
b.Property<long?>("OriginalId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("original_id");
|
|
||||||
|
|
||||||
b.Property<string>("System")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("system");
|
|
||||||
|
|
||||||
b.Property<long>("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<long>("GuildId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("guild_id");
|
|
||||||
|
|
||||||
b.Property<long>("UserId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("user_id");
|
|
||||||
|
|
||||||
b.Property<Instant>("AddedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("added_at")
|
|
||||||
.HasDefaultValueSql("now()");
|
|
||||||
|
|
||||||
b.Property<long>("ModeratorId")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("moderator_id");
|
|
||||||
|
|
||||||
b.Property<string>("Reason")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("reason");
|
|
||||||
|
|
||||||
b.HasKey("GuildId", "UserId")
|
|
||||||
.HasName("pk_watchlists");
|
|
||||||
|
|
||||||
b.ToTable("watchlists", (string)null);
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
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<Guild> GetGuildAsync(
|
|
||||||
this DatabaseContext db,
|
|
||||||
Snowflake id,
|
|
||||||
bool shouldTrack = true,
|
|
||||||
CancellationToken ct = default
|
|
||||||
) => await db.GetGuildAsync(id.ToUlong(), shouldTrack, ct);
|
|
||||||
|
|
||||||
public static async ValueTask<Guild> GetGuildAsync(
|
|
||||||
this DatabaseContext db,
|
|
||||||
Optional<Snowflake> id,
|
|
||||||
bool shouldTrack = true,
|
|
||||||
CancellationToken ct = default
|
|
||||||
) => await db.GetGuildAsync(id.ToUlong(), shouldTrack, ct);
|
|
||||||
|
|
||||||
public static async ValueTask<Guild> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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<ApiTokenRepository>();
|
||||||
|
|
||||||
|
public async Task<ApiToken?> GetAsync(string token) =>
|
||||||
|
await conn.QueryFirstOrDefaultAsync<ApiToken>(
|
||||||
|
"select * from api_tokens where dashboard_token = @Token and expires_at > @Now",
|
||||||
|
new { Token = token, Now = clock.GetCurrentInstant() }
|
||||||
|
);
|
||||||
|
|
||||||
|
public async Task<ApiToken> CreateAsync(
|
||||||
|
string dashboardToken,
|
||||||
|
string userId,
|
||||||
|
string accessToken,
|
||||||
|
string? refreshToken,
|
||||||
|
int expiresIn
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var expiresAt = clock.GetCurrentInstant() + Duration.FromSeconds(expiresIn);
|
||||||
|
|
||||||
|
return await conn.QueryFirstAsync<ApiToken>(
|
||||||
|
"""
|
||||||
|
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<ApiToken> UpdateAsync(
|
||||||
|
int id,
|
||||||
|
string accessToken,
|
||||||
|
string? refreshToken,
|
||||||
|
int expiresIn
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var expiresAt = clock.GetCurrentInstant() + Duration.FromSeconds(expiresIn);
|
||||||
|
|
||||||
|
return await conn.QueryFirstAsync<ApiToken>(
|
||||||
|
"""
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ using Catalogger.Backend.Database.Models;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Database.Dapper.Repositories;
|
namespace Catalogger.Backend.Database.Repositories;
|
||||||
|
|
||||||
public class GuildRepository(ILogger logger, DatabaseConnection conn)
|
public class GuildRepository(ILogger logger, DatabaseConnection conn)
|
||||||
: IDisposable,
|
: IDisposable,
|
||||||
|
|
@ -89,8 +89,8 @@ public class GuildRepository(ILogger logger, DatabaseConnection conn)
|
||||||
|
|
||||||
public async Task UpdateChannelConfigAsync(Snowflake id, Guild.ChannelConfig config) =>
|
public async Task UpdateChannelConfigAsync(Snowflake id, Guild.ChannelConfig config) =>
|
||||||
await conn.ExecuteAsync(
|
await conn.ExecuteAsync(
|
||||||
"update guilds set channels = @Channels where id = @Id",
|
"update guilds set channels = @Channels::jsonb where id = @Id",
|
||||||
new { Id = id, Channels = config }
|
new { Id = id.Value, Channels = config }
|
||||||
);
|
);
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
@ -17,7 +17,7 @@ using Catalogger.Backend.Database.Models;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Database.Dapper.Repositories;
|
namespace Catalogger.Backend.Database.Repositories;
|
||||||
|
|
||||||
public class InviteRepository(ILogger logger, DatabaseConnection conn)
|
public class InviteRepository(ILogger logger, DatabaseConnection conn)
|
||||||
: IDisposable,
|
: IDisposable,
|
||||||
|
|
@ -20,7 +20,7 @@ using Remora.Discord.API;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Database.Dapper.Repositories;
|
namespace Catalogger.Backend.Database.Repositories;
|
||||||
|
|
||||||
public class MessageRepository(
|
public class MessageRepository(
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
|
|
@ -17,7 +17,7 @@ using Catalogger.Backend.Database.Models;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Database.Dapper.Repositories;
|
namespace Catalogger.Backend.Database.Repositories;
|
||||||
|
|
||||||
public class WatchlistRepository(ILogger logger, DatabaseConnection conn)
|
public class WatchlistRepository(ILogger logger, DatabaseConnection conn)
|
||||||
: IDisposable,
|
: IDisposable,
|
||||||
4
Catalogger.Backend/Database/setup_migrations.sql
Normal file
4
Catalogger.Backend/Database/setup_migrations.sql
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
create table if not exists migrations (
|
||||||
|
migration_name text primary key,
|
||||||
|
applied_at timestamptz not null default (now())
|
||||||
|
);
|
||||||
|
|
@ -22,12 +22,9 @@ using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Cache.RedisCache;
|
using Catalogger.Backend.Cache.RedisCache;
|
||||||
using Catalogger.Backend.Database;
|
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.Redis;
|
||||||
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using Prometheus;
|
using Prometheus;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
|
|
@ -106,6 +103,7 @@ public static class StartupExtensions
|
||||||
services
|
services
|
||||||
.AddSingleton<IClock>(SystemClock.Instance)
|
.AddSingleton<IClock>(SystemClock.Instance)
|
||||||
.AddDatabasePool()
|
.AddDatabasePool()
|
||||||
|
.AddScoped<DatabaseMigrator>()
|
||||||
.AddScoped<MessageRepository>()
|
.AddScoped<MessageRepository>()
|
||||||
.AddScoped<GuildRepository>()
|
.AddScoped<GuildRepository>()
|
||||||
.AddScoped<InviteRepository>()
|
.AddScoped<InviteRepository>()
|
||||||
|
|
@ -159,6 +157,7 @@ public static class StartupExtensions
|
||||||
return services;
|
return services;
|
||||||
|
|
||||||
return services
|
return services
|
||||||
|
.AddScoped<ApiTokenRepository>()
|
||||||
.AddScoped<ApiCache>()
|
.AddScoped<ApiCache>()
|
||||||
.AddScoped<DiscordRequestService>()
|
.AddScoped<DiscordRequestService>()
|
||||||
.AddScoped<AuthenticationMiddleware>()
|
.AddScoped<AuthenticationMiddleware>()
|
||||||
|
|
@ -197,17 +196,8 @@ public static class StartupExtensions
|
||||||
|
|
||||||
DatabasePool.ConfigureDapper();
|
DatabasePool.ConfigureDapper();
|
||||||
|
|
||||||
await using (var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>())
|
await using var migrator = scope.ServiceProvider.GetRequiredService<DatabaseMigrator>();
|
||||||
{
|
await migrator.Migrate();
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = scope.ServiceProvider.GetRequiredService<Config>();
|
var config = scope.ServiceProvider.GetRequiredService<Config>();
|
||||||
var slashService = scope.ServiceProvider.GetRequiredService<SlashService>();
|
var slashService = scope.ServiceProvider.GetRequiredService<SlashService>();
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Catalogger.Backend.Bot.Commands;
|
using Catalogger.Backend.Bot.Commands;
|
||||||
using Catalogger.Backend.Database;
|
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
using Catalogger.Backend.Services;
|
using Catalogger.Backend.Services;
|
||||||
using Prometheus;
|
using Prometheus;
|
||||||
|
|
@ -110,8 +109,7 @@ if (!config.Logging.EnableMetrics)
|
||||||
builder.Services.AddHostedService<BackgroundMetricsCollectionService>();
|
builder.Services.AddHostedService<BackgroundMetricsCollectionService>();
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.Services.AddDbContext<DatabaseContext>()
|
.Services.MaybeAddDashboardServices(config)
|
||||||
.MaybeAddDashboardServices(config)
|
|
||||||
.MaybeAddRedisCaches(config)
|
.MaybeAddRedisCaches(config)
|
||||||
.AddCustomServices()
|
.AddCustomServices()
|
||||||
.AddEndpointsApiExplorer()
|
.AddEndpointsApiExplorer()
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,7 @@
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using Catalogger.Backend.Database.Dapper.Repositories;
|
using Catalogger.Backend.Database.Repositories;
|
||||||
using Catalogger.Backend.Database.Queries;
|
|
||||||
|
|
||||||
namespace Catalogger.Backend.Services;
|
namespace Catalogger.Backend.Services;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ using Catalogger.Backend.Cache;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using Remora.Discord.API.Abstractions.Rest;
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
using Remora.Discord.API.Gateway.Commands;
|
using Remora.Discord.API.Gateway.Commands;
|
||||||
using Remora.Discord.Gateway;
|
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Services;
|
namespace Catalogger.Backend.Services;
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,8 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Database;
|
using Catalogger.Backend.Database;
|
||||||
using Catalogger.Backend.Database.Dapper;
|
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Prometheus;
|
using Prometheus;
|
||||||
|
|
||||||
namespace Catalogger.Backend.Services;
|
namespace Catalogger.Backend.Services;
|
||||||
|
|
@ -40,7 +38,6 @@ public class MetricsCollectionService(
|
||||||
var timer = CataloggerMetrics.MetricsCollectionTime.NewTimer();
|
var timer = CataloggerMetrics.MetricsCollectionTime.NewTimer();
|
||||||
|
|
||||||
await using var scope = services.CreateAsyncScope();
|
await using var scope = services.CreateAsyncScope();
|
||||||
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
|
||||||
await using var conn = scope.ServiceProvider.GetRequiredService<DatabaseConnection>();
|
await using var conn = scope.ServiceProvider.GetRequiredService<DatabaseConnection>();
|
||||||
|
|
||||||
var messageCount = await conn.ExecuteScalarAsync<int>("select count(id) from messages");
|
var messageCount = await conn.ExecuteScalarAsync<int>("select count(id) from messages");
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
using Catalogger.Backend.Cache;
|
using Catalogger.Backend.Cache;
|
||||||
using Catalogger.Backend.Cache.InMemoryCache;
|
using Catalogger.Backend.Cache.InMemoryCache;
|
||||||
using Catalogger.Backend.Extensions;
|
using Catalogger.Backend.Extensions;
|
||||||
|
using OneOf;
|
||||||
using Remora.Discord.API;
|
using Remora.Discord.API;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
using Remora.Discord.API.Abstractions.Rest;
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
using Remora.Discord.API.Objects;
|
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
using Guild = Catalogger.Backend.Database.Models.Guild;
|
using Guild = Catalogger.Backend.Database.Models.Guild;
|
||||||
|
|
||||||
|
|
@ -109,7 +109,7 @@ public class WebhookExecutorService(
|
||||||
}
|
}
|
||||||
|
|
||||||
var attachments = files
|
var attachments = files
|
||||||
.Select<FileData, OneOf.OneOf<FileData, IPartialAttachment>>(f => f)
|
.Select<FileData, OneOf<FileData, IPartialAttachment>>(f => f)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (embeds.Count == 0 && attachments.Count == 0)
|
if (embeds.Count == 0 && attachments.Count == 0)
|
||||||
|
|
|
||||||
|
|
@ -8,27 +8,6 @@
|
||||||
"resolved": "2.1.35",
|
"resolved": "2.1.35",
|
||||||
"contentHash": "YKRwjVfrG7GYOovlGyQoMvr1/IJdn+7QzNXJxyMh0YfFF5yvDmTYaJOVYWsckreNjGsGSEtrMTpnzxTUq/tZQw=="
|
"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": {
|
"Humanizer.Core": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2.14.1, )",
|
"requested": "[2.14.1, )",
|
||||||
|
|
@ -65,31 +44,6 @@
|
||||||
"Microsoft.OpenApi": "1.4.3"
|
"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": {
|
"Newtonsoft.Json": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[13.0.3, )",
|
"requested": "[13.0.3, )",
|
||||||
|
|
@ -123,28 +77,6 @@
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.0"
|
"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": {
|
"Npgsql.NodaTime": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[8.0.5, )",
|
"requested": "[8.0.5, )",
|
||||||
|
|
@ -291,14 +223,6 @@
|
||||||
"resolved": "8.2.2",
|
"resolved": "8.2.2",
|
||||||
"contentHash": "+zIp8d3sbtYaRbM6hqDs4Ui/z34j7DcUmleruZlYLE4CVxXq+MO8XJyIs42vzeTYFX+k0Iq1dEbBUnQ4z/Gnrw=="
|
"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": {
|
"FuzzySharp": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.0.2",
|
"resolved": "2.0.2",
|
||||||
|
|
@ -318,84 +242,11 @@
|
||||||
"Newtonsoft.Json": "13.0.3"
|
"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": {
|
"Microsoft.CSharp": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.7.0",
|
"resolved": "4.7.0",
|
||||||
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
|
"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": {
|
"Microsoft.Extensions.ApiDescription.Server": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "6.0.5",
|
"resolved": "6.0.5",
|
||||||
|
|
@ -585,14 +436,6 @@
|
||||||
"resolved": "1.6.14",
|
"resolved": "1.6.14",
|
||||||
"contentHash": "tTaBT8qjk3xINfESyOPE2rIellPvB7qpVqiWiyA/lACVvz+xOGiXhFUfohcx82NLbi5avzLW0lx+s6oAqQijfw=="
|
"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": {
|
"Newtonsoft.Json.Bson": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.0.2",
|
"resolved": "1.0.2",
|
||||||
|
|
@ -860,72 +703,11 @@
|
||||||
"resolved": "6.8.1",
|
"resolved": "6.8.1",
|
||||||
"contentHash": "lpEszYJ7vZaTTE5Dp8MrsbSHrgDfjhDMjzW1qOA1Xs1Dnj3ZRBJAcPZUTsa5Bva+nLaw91JJ8OI8FkSg8hhIyA=="
|
"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": {
|
"System.ComponentModel.Annotations": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "5.0.0",
|
"resolved": "5.0.0",
|
||||||
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
|
"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": {
|
"System.Diagnostics.DiagnosticSource": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "8.0.0",
|
||||||
|
|
@ -933,29 +715,13 @@
|
||||||
},
|
},
|
||||||
"System.IO.Pipelines": {
|
"System.IO.Pipelines": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "6.0.3",
|
"resolved": "5.0.1",
|
||||||
"contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw=="
|
"contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg=="
|
||||||
},
|
|
||||||
"System.Reflection.Metadata": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "6.0.1",
|
|
||||||
"contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==",
|
|
||||||
"dependencies": {
|
|
||||||
"System.Collections.Immutable": "6.0.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"System.Runtime.CompilerServices.Unsafe": {
|
"System.Runtime.CompilerServices.Unsafe": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "6.0.0",
|
"resolved": "4.7.1",
|
||||||
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
|
"contentHash": "zOHkQmzPCn5zm/BH+cxC1XbUS3P4Yoi3xzW7eRgVpDR2tPGSzyMZ17Ig1iRkfJuY0nhxkQQde8pgePNiA7z7TQ=="
|
||||||
},
|
|
||||||
"System.Text.Encoding.CodePages": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "6.0.0",
|
|
||||||
"contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
|
|
||||||
"dependencies": {
|
|
||||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"System.Text.Encodings.Web": {
|
"System.Text.Encodings.Web": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue