summary refs log tree commit diff
diff options
context:
space:
mode:
authorCyber <mateusz.hf18@gmail.com>2025-12-15 08:29:43 +0100
committerGitHub <noreply@github.com>2025-12-15 08:29:43 +0100
commita583d85d443a3af7ebbd3ab87f887748f1439d25 (patch)
tree4da1cc7dc9514969825ef003706849e58840e34f
parentfix: check if event is "opened" or "closed" (diff)
parentfix cors (diff)
downloadserver-ts-a583d85d443a3af7ebbd3ab87f887748f1439d25.tar.xz
Merge branch 'spacebarchat:master' into fix/gh-webhook-events-more
-rw-r--r--.gitignore4
-rw-r--r--.idea/workspace.xml17
-rw-r--r--assets/openapi.json25
-rw-r--r--assets/schemas.json18
-rw-r--r--extra/admin-api/.gitignore2
-rw-r--r--extra/admin-api/ConfigTest/ConfigTest.csproj20
-rw-r--r--extra/admin-api/ConfigTest/Program.cs14
-rw-r--r--extra/admin-api/ConfigTest/Properties/launchSettings.json12
-rw-r--r--extra/admin-api/ConfigTest/Worker.cs43
-rw-r--r--extra/admin-api/ConfigTest/appsettings.Development.json (renamed from extra/admin-api/Spacebar.AdminAPI/appsettings.Development.json)0
-rw-r--r--extra/admin-api/ConfigTest/appsettings.json (renamed from extra/admin-api/Spacebar.AdminAPI/appsettings.json)0
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Controllers/ConfigController.cs61
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs (renamed from extra/admin-api/Spacebar.AdminAPI/Controllers/GuildController.cs)6
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Controllers/Media/UserMediaController.cs (renamed from extra/admin-api/Spacebar.AdminAPI/Controllers/Media/UserMediaController.cs)6
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Controllers/PingController.cs (renamed from extra/admin-api/Spacebar.AdminAPI/Controllers/PingController.cs)4
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs (renamed from extra/admin-api/Spacebar.AdminAPI/Controllers/UserController.cs)8
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Extensions/DbExtensions.cs (renamed from extra/admin-api/Spacebar.AdminAPI/Extensions/DbExtensions.cs)2
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Middleware/AuthenticationMiddleware.cs (renamed from extra/admin-api/Spacebar.AdminAPI/Middleware/AuthenticationMiddleware.cs)4
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Program.cs (renamed from extra/admin-api/Spacebar.AdminAPI/Program.cs)4
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Properties/launchSettings.json (renamed from extra/admin-api/Spacebar.AdminAPI/Properties/launchSettings.json)0
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Services/AuthenticationService.cs (renamed from extra/admin-api/Spacebar.AdminAPI/Services/AuthenticationService.cs)2
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Services/Configuration.cs (renamed from extra/admin-api/Spacebar.AdminAPI/Services/Configuration.cs)2
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.csproj (renamed from extra/admin-api/Spacebar.AdminAPI/Spacebar.AdminAPI.csproj)1
-rw-r--r--extra/admin-api/Spacebar.AdminApi/appsettings.Development.json27
-rw-r--r--extra/admin-api/Spacebar.AdminApi/appsettings.json9
-rw-r--r--extra/admin-api/Spacebar.AdminApi/deps.json447
-rw-r--r--extra/admin-api/Spacebar.CleanSettingsRows/deps.json492
-rw-r--r--extra/admin-api/Spacebar.ConfigModel/Class1.cs64
-rw-r--r--extra/admin-api/Spacebar.ConfigModel/Extensions/JsonExtensions.cs119
-rw-r--r--extra/admin-api/Spacebar.ConfigModel/Spacebar.ConfigModel.csproj9
-rw-r--r--extra/admin-api/Spacebar.Db/Contexts/SpacebarDbContext.cs44
-rw-r--r--extra/admin-api/Spacebar.Db/Models/Application.cs12
-rw-r--r--extra/admin-api/Spacebar.Db/Models/ApplicationCommand.cs66
-rw-r--r--extra/admin-api/Spacebar.Db/Models/Ban.cs2
-rw-r--r--extra/admin-api/Spacebar.Db/Models/InstanceBan.cs47
-rw-r--r--extra/admin-api/Spacebar.Db/Models/Message.cs19
-rw-r--r--extra/admin-api/Spacebar.Db/Models/Session.cs8
-rw-r--r--extra/admin-api/Spacebar.Db/Models/TeamMember.cs6
-rw-r--r--extra/admin-api/Spacebar.Db/Models/User.cs18
-rw-r--r--extra/admin-api/Spacebar.Db/Models/UserSetting.cs6
-rw-r--r--extra/admin-api/Spacebar.Db/Spacebar.Db.csproj7
-rw-r--r--extra/admin-api/Spacebar.Db/deps.json362
-rw-r--r--extra/admin-api/SpacebarAdminAPI.sln18
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/App.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/App.razor)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Classes/OpenAPI/OpenAPISchema.cs (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Classes/OpenAPI/OpenAPISchema.cs)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/MainLayout.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/MainLayout.razor)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/MainLayout.razor.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/MainLayout.razor.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/NavMenu.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/NavMenu.razor)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/NavMenu.razor.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/NavMenu.razor.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Guilds.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Guilds.razor)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Home.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Home.razor)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/HttpTestClient.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/HttpTestClient.razor)7
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/HttpTestClientParts/OpenAPIParameterDescription.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/HttpTestClientParts/OpenAPIParameterDescription.razor)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Login.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Login.razor)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Media/Index.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Media/Index.razor)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Media/Users.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Media/Users.razor)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Users.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Users.razor)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/UsersDelete.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/UsersDelete.razor)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Program.cs (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Program.cs)4
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Properties/launchSettings.json (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Properties/launchSettings.json)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Services/Config.cs (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Services/Config.cs)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Services/StreamingHttpClient.cs (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Services/StreamingHttpClient.cs)21
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Spacebar.AdminApi.TestClient.csproj (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Spacebar.AdminAPI.TestClient.csproj)4
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/_Imports.razor (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/_Imports.razor)4
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/deps.json217
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/appsettings.json (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/appsettings.json)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/css/app.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/css/app.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/favicon.png (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/favicon.png)bin1148 -> 1148 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/icon-192.png (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/icon-192.png)bin2626 -> 2626 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/index.html (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/index.html)2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/jetbrains-mono.css (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/jetbrains-mono.css)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Bold.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Bold.woff2)bin94588 -> 94588 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-BoldItalic.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-BoldItalic.woff2)bin98152 -> 98152 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBold.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBold.woff2)bin93692 -> 93692 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBoldItalic.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBoldItalic.woff2)bin96864 -> 96864 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLight.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLight.woff2)bin92712 -> 92712 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLightItalic.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLightItalic.woff2)bin95816 -> 95816 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Italic.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Italic.woff2)bin95864 -> 95864 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Light.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Light.woff2)bin93856 -> 93856 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-LightItalic.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-LightItalic.woff2)bin97280 -> 97280 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Medium.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Medium.woff2)bin93824 -> 93824 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-MediumItalic.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-MediumItalic.woff2)bin97808 -> 97808 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Regular.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Regular.woff2)bin92164 -> 92164 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBold.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBold.woff2)bin94472 -> 94472 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBoldItalic.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBoldItalic.woff2)bin98124 -> 98124 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Thin.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Thin.woff2)bin90612 -> 90612 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ThinItalic.woff2 (renamed from extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ThinItalic.woff2)bin94316 -> 94316 bytes
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApiTest/Program.cs (renamed from extra/admin-api/Utilities/Spacebar.AdminAPITest/Program.cs)0
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApiTest/Spacebar.AdminApiTest.csproj (renamed from extra/admin-api/Utilities/Spacebar.AdminAPITest/Spacebar.AdminAPITest.csproj)4
-rw-r--r--extra/admin-api/db-patches/00-use-stream-class.patch14
-rw-r--r--extra/admin-api/db-patches/01-ulong-user-rights.patch28
-rw-r--r--extra/admin-api/db-patches/db-00-fix-flags.patch13
-rw-r--r--extra/admin-api/flake.lock6
-rw-r--r--extra/admin-api/flake.nix82
-rw-r--r--extra/admin-api/nuget.config9
-rw-r--r--extra/admin-api/outputs.nix128
-rwxr-xr-xextra/admin-api/scaffold-db10
-rw-r--r--flake.nix265
-rw-r--r--nix/tests/test-bundle-starts.nix9
-rw-r--r--scripts/test.js17
-rw-r--r--scripts/util/getRouteDescriptions.js2
-rw-r--r--src/api/middlewares/Authentication.ts16
-rw-r--r--src/api/middlewares/CORS.ts14
-rw-r--r--src/api/middlewares/RateLimit.ts48
-rw-r--r--src/api/routes/auth/forgot.ts15
-rw-r--r--src/api/routes/auth/location-metadata.ts4
-rw-r--r--src/api/routes/auth/login.ts45
-rw-r--r--src/api/routes/auth/register.ts8
-rw-r--r--src/api/routes/auth/reset.ts2
-rw-r--r--src/api/routes/auth/verify/index.ts17
-rw-r--r--src/api/routes/channels/#channel_id/messages/index.ts2
-rw-r--r--src/api/routes/guilds/#guild_id/bans.ts17
-rw-r--r--src/api/routes/guilds/#guild_id/bulk-ban.ts4
-rw-r--r--src/api/routes/guilds/#guild_id/regions.ts9
-rw-r--r--src/api/routes/invites/index.ts4
-rw-r--r--src/api/routes/oauth2/authorize.ts49
-rw-r--r--src/api/routes/users/#user_id/delete.ts146
-rw-r--r--src/api/routes/voice/regions.ts4
-rw-r--r--src/api/util/utility/ipAddress.ts33
-rw-r--r--src/gateway/Server.ts5
-rw-r--r--src/gateway/events/Connection.ts7
-rw-r--r--src/gateway/listener/listener.ts123
-rw-r--r--src/gateway/util/WebSocket.ts5
-rw-r--r--src/schemas/api/users/InstanceUserDeleteSchema.ts6
-rw-r--r--src/schemas/api/users/index.ts1
-rw-r--r--src/schemas/uncategorised/BanCreateSchema.ts4
-rw-r--r--src/util/config/types/SecurityConfiguration.ts4
-rw-r--r--src/util/entities/BackupCodes.ts3
-rw-r--r--src/util/entities/Channel.ts14
-rw-r--r--src/util/entities/InstanceBan.ts111
-rw-r--r--src/util/entities/User.ts14
-rw-r--r--src/util/entities/index.ts1
-rw-r--r--src/util/interfaces/Event.ts1
-rw-r--r--src/util/migration/postgres-initial.ts727
-rw-r--r--src/util/migration/postgres/1696420827239-guildChannelOrdering.ts26
-rw-r--r--src/util/migration/postgres/1713116476900-messageFlagsNotNull.ts14
-rw-r--r--src/util/migration/postgres/1720628601997-badges.ts1
-rw-r--r--src/util/migration/postgres/1752321571508-RoleColors.ts2
-rw-r--r--src/util/migration/postgres/1752342900886-RoleColorsSolidColor.ts15
-rw-r--r--src/util/migration/postgres/1752383879533-message_pinned_at.ts6
-rw-r--r--src/util/migration/postgres/1761113394664-delete-bot-users-without-an-application.ts1
-rw-r--r--src/util/migration/postgres/1762611552514-fix-gif-stickers-format_type.ts1
-rw-r--r--src/util/migration/postgres/1765578570423-InstanceBanTable.ts16
-rw-r--r--src/util/migration/postgres/1765587835846-InstanceBanAllowlist.ts14
-rw-r--r--src/util/migration/postgres/1765665440000-DropDefaultIPDataKey.ts14
-rw-r--r--src/util/util/Database.ts47
-rw-r--r--src/util/util/ElapsedTime.ts13
-rw-r--r--src/util/util/Event.ts63
-rw-r--r--src/util/util/RabbitMQ.ts32
-rw-r--r--src/util/util/Token.ts68
-rw-r--r--src/util/util/WebAuthn.ts41
195 files changed, 3952 insertions, 788 deletions
diff --git a/.gitignore b/.gitignore

index bb79fbf7..c1a78e41 100644 --- a/.gitignore +++ b/.gitignore
@@ -26,4 +26,6 @@ bun.lock # optional generated outputs from schema.js schemas_orig/ schemas_nested/ -schemas_final/ \ No newline at end of file +schemas_final/ + +!/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/ \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index c52fdde4..dfbf2c4a 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml
@@ -55,9 +55,10 @@ "Node.js.Server.ts.executor": "Debug", "RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.git.unshallow": "true", + "git-widget-placeholder": "master", "javascript.nodejs.core.library.configured.version": "24.8.0", "javascript.nodejs.core.library.typings.version": "24.7.0", - "last_opened_file_path": "/home/Rory/git/spacebar/server-master/src/util/migration/postgres", + "last_opened_file_path": "/home/Rory/git/spacebar/server-master/src/schemas/api/users", "node.js.detected.package.eslint": "true", "node.js.detected.package.standard": "true", "node.js.selected.package.eslint": "(autodetect)", @@ -71,7 +72,7 @@ "npm.build.executor": "Run", "npm.start.executor": "Debug", "prettierjs.PrettierConfiguration.Package": "/home/Rory/git/spacebar/server-master/node_modules/prettier", - "settings.editor.selected.configurable": "preferences.sourceCode", + "settings.editor.selected.configurable": "com.github.copilot.settings.customInstructions.CopilotInstructionsConfigurable", "ts.external.directory.path": "/home/Rory/git/spacebar/server-master/node_modules/typescript/lib" }, "keyToStringList": { @@ -87,11 +88,11 @@ }]]></component> <component name="RecentsManager"> <key name="CopyFile.RECENT_KEYS"> - <recent name="$PROJECT_DIR$/src/util/migration/postgres" /> - <recent name="$PROJECT_DIR$/src/util/entities" /> - <recent name="$PROJECT_DIR$/src/schemas/api/guilds" /> - <recent name="$PROJECT_DIR$/src/schemas" /> <recent name="$PROJECT_DIR$/src/schemas/api/users" /> + <recent name="$PROJECT_DIR$/src/util/entities" /> + <recent name="$PROJECT_DIR$/src/gateway/opcodes" /> + <recent name="$PROJECT_DIR$/src/api/routes/users/@me/billing" /> + <recent name="$PROJECT_DIR$/src/util/util/networking/abuseipdb" /> </key> <key name="MoveFile.RECENT_KEYS"> <recent name="$PROJECT_DIR$/src/schemas/api/guilds" /> @@ -161,7 +162,9 @@ <workItem from="1760044946282" duration="43683000" /> <workItem from="1760402350251" duration="49898000" /> <workItem from="1760538864442" duration="1330000" /> - <workItem from="1764432507485" duration="22274000" /> + <workItem from="1764432507485" duration="30779000" /> + <workItem from="1764521899659" duration="22029000" /> + <workItem from="1765142585081" duration="16912000" /> </task> <servers /> </component> diff --git a/assets/openapi.json b/assets/openapi.json
index e502af61..83138115 100644 --- a/assets/openapi.json +++ b/assets/openapi.json
@@ -1034,6 +1034,9 @@ "id" ] }, + "InstanceUserDeleteSchema": { + "$ref": "#/components/schemas/InstanceUserDeleteSchemaContent" + }, "UserSettingsUpdateSchema": { "type": "object", "properties": { @@ -4255,10 +4258,10 @@ "type": "object", "properties": { "delete_message_seconds": { - "type": "string" + "type": "integer" }, "delete_message_days": { - "type": "string" + "type": "integer" }, "reason": { "type": "string" @@ -10774,6 +10777,14 @@ 3 ] }, + "InstanceUserDeleteSchemaContent": { + "type": "object", + "properties": { + "reason": { + "type": "string" + } + } + }, "Classification": { "type": "object", "properties": { @@ -15565,6 +15576,16 @@ "bearer": [] } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InstanceUserDeleteSchema" + } + } + } + }, "responses": { "204": { "description": "No description available" diff --git a/assets/schemas.json b/assets/schemas.json
index d5b4835e..4bf97dc4 100644 --- a/assets/schemas.json +++ b/assets/schemas.json
@@ -1063,6 +1063,10 @@ ], "$schema": "http://json-schema.org/draft-07/schema#" }, + "InstanceUserDeleteSchema": { + "$ref": "#/definitions/InstanceUserDeleteSchemaContent", + "$schema": "http://json-schema.org/draft-07/schema#" + }, "UserSettingsUpdateSchema": { "type": "object", "properties": { @@ -4497,10 +4501,10 @@ "type": "object", "properties": { "delete_message_seconds": { - "type": "string" + "type": "integer" }, "delete_message_days": { - "type": "string" + "type": "integer" }, "reason": { "type": "string" @@ -11400,6 +11404,16 @@ ], "$schema": "http://json-schema.org/draft-07/schema#" }, + "InstanceUserDeleteSchemaContent": { + "type": "object", + "properties": { + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, "Classification": { "type": "object", "properties": { diff --git a/extra/admin-api/.gitignore b/extra/admin-api/.gitignore
index f4e4961b..65497125 100644 --- a/extra/admin-api/.gitignore +++ b/extra/admin-api/.gitignore
@@ -6,4 +6,4 @@ appsettings.Local*.json /*.patch Spacebar.Db/**/*.orig -Spacebar.Db/**/*.rej +Spacebar.Db/**/*.rej \ No newline at end of file diff --git a/extra/admin-api/ConfigTest/ConfigTest.csproj b/extra/admin-api/ConfigTest/ConfigTest.csproj new file mode 100644
index 00000000..7244b673 --- /dev/null +++ b/extra/admin-api/ConfigTest/ConfigTest.csproj
@@ -0,0 +1,20 @@ +<Project Sdk="Microsoft.NET.Sdk.Worker"> + + <PropertyGroup> + <TargetFramework>net10.0</TargetFramework> + <Nullable>enable</Nullable> + <ImplicitUsings>enable</ImplicitUsings> + <UserSecretsId>dotnet-ConfigTest-18d89c0e-df5d-447b-8429-7d526a35ab13</UserSecretsId> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1" /> + <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" /> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Spacebar.ConfigModel\Spacebar.ConfigModel.csproj" /> + <ProjectReference Include="..\Spacebar.Db\Spacebar.Db.csproj" /> + </ItemGroup> +</Project> diff --git a/extra/admin-api/ConfigTest/Program.cs b/extra/admin-api/ConfigTest/Program.cs new file mode 100644
index 00000000..5945bcf6 --- /dev/null +++ b/extra/admin-api/ConfigTest/Program.cs
@@ -0,0 +1,14 @@ +using ConfigTest; +using Microsoft.EntityFrameworkCore; +using Spacebar.Db.Contexts; + +var builder = Host.CreateApplicationBuilder(args); +builder.Services.AddHostedService<Worker>(); +builder.Services.AddDbContext<SpacebarDbContext>(options => { + options + .UseNpgsql(builder.Configuration.GetConnectionString("Spacebar")) + .EnableDetailedErrors(); +}, ServiceLifetime.Singleton); + +var host = builder.Build(); +host.Run(); diff --git a/extra/admin-api/ConfigTest/Properties/launchSettings.json b/extra/admin-api/ConfigTest/Properties/launchSettings.json new file mode 100644
index 00000000..c355f360 --- /dev/null +++ b/extra/admin-api/ConfigTest/Properties/launchSettings.json
@@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "ConfigTest": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/extra/admin-api/ConfigTest/Worker.cs b/extra/admin-api/ConfigTest/Worker.cs new file mode 100644
index 00000000..dd3ef779 --- /dev/null +++ b/extra/admin-api/ConfigTest/Worker.cs
@@ -0,0 +1,43 @@ +using System.Text.Json.Nodes; +using Spacebar.ConfigModel.Extensions; +using Spacebar.Db.Contexts; + +namespace ConfigTest; + +public class Worker(ILogger<Worker> logger, SpacebarDbContext db) : BackgroundService +{ + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + var config = db.Configs + .OrderBy(x => x.Key) + .ToDictionary(x => x.Key, x => x.Value); + foreach (var (key, value) in config) + { + Console.WriteLine("Config Key: {0}, Value: {1}", key, value ?? "[NULL]"); + } + + var readConfig = config.ToNestedJsonObject(); + Console.WriteLine(readConfig); + var mapped = readConfig.ToFlatKv(); + foreach (var (key, value) in mapped) + { + Console.WriteLine("Mapped Key: {0}, Value: {1}", key, value ?? "[NULL]"); + } + + // check that they're equal + foreach (var (key, value) in config) + { + if (!mapped.ContainsKey(key)) + { + Console.WriteLine("Missing Key in Mapped: {0}", key); + continue; + } + + if (mapped[key] != value) + { + Console.WriteLine("Value Mismatch for Key: {0}, Original: {1}, Mapped: {2}", key, value ?? "[NULL]", mapped[key] ?? "[NULL]"); + } + } + Environment.Exit(0); + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.AdminAPI/appsettings.Development.json b/extra/admin-api/ConfigTest/appsettings.Development.json
index 03245773..03245773 100644 --- a/extra/admin-api/Spacebar.AdminAPI/appsettings.Development.json +++ b/extra/admin-api/ConfigTest/appsettings.Development.json
diff --git a/extra/admin-api/Spacebar.AdminAPI/appsettings.json b/extra/admin-api/ConfigTest/appsettings.json
index 10f68b8c..10f68b8c 100644 --- a/extra/admin-api/Spacebar.AdminAPI/appsettings.json +++ b/extra/admin-api/ConfigTest/appsettings.json
diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/ConfigController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/ConfigController.cs new file mode 100644
index 00000000..96465616 --- /dev/null +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/ConfigController.cs
@@ -0,0 +1,61 @@ +using System.Text.Json.Nodes; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.AdminApi.Extensions; +using Spacebar.AdminApi.Models; +using Spacebar.AdminApi.Services; +using Spacebar.Db.Contexts; +using Spacebar.Db.Models; +using Spacebar.RabbitMqUtilities; +using Spacebar.ConfigModel.Extensions; + +namespace Spacebar.AdminApi.Controllers; + +[ApiController] +[Route("/Configuration")] +public class ConfigController(ILogger<ConfigController> logger, SpacebarDbContext db, RabbitMQService mq, IServiceProvider sp, AuthenticationService auth) : ControllerBase { + private readonly ILogger<ConfigController> _logger = logger; + + [HttpGet] + public async Task<JsonObject> Get() { + (await auth.GetCurrentUser(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + + var config = (await db.Configs.AsNoTracking().ToDictionaryAsync(x => x.Key, x => x.Value)).ToNestedJsonObject(); + return config; + } + + [HttpPost] + public async Task<IActionResult> Post([FromBody] JsonObject newConfig) { + (await auth.GetCurrentUser(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + + var flatConfig = newConfig.ToFlatKv(); + var tasks = flatConfig.Select(async x => { + await using var scope = sp.CreateAsyncScope(); + var scopedDb = scope.ServiceProvider.GetRequiredService<SpacebarDbContext>(); + var existingConfig = await scopedDb.Configs.FindAsync(x.Key); + if (existingConfig != null) { + existingConfig.Value = x.Value; + scopedDb.Configs.Update(existingConfig); + } + else { + await scopedDb.Configs.AddAsync(new Config + { Key = x.Key, Value = x.Value }); + } + + await scopedDb.SaveChangesAsync(); + }); + await Task.WhenAll(tasks); + // TODO: rabbitmq + + return Ok(); + } + + [HttpPost] + public async Task<IActionResult> ReloadConfig() { + (await auth.GetCurrentUser(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + + // TODO: rabbitmq + + return Ok(); + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.AdminAPI/Controllers/GuildController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs
index 03b38e64..05bd604a 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Controllers/GuildController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs
@@ -2,14 +2,14 @@ using ArcaneLibs.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using RabbitMQ.Client; -using Spacebar.AdminAPI.Extensions; +using Spacebar.AdminApi.Extensions; using Spacebar.AdminApi.Models; -using Spacebar.AdminAPI.Services; +using Spacebar.AdminApi.Services; using Spacebar.Db.Contexts; using Spacebar.Db.Models; using Spacebar.RabbitMqUtilities; -namespace Spacebar.AdminAPI.Controllers; +namespace Spacebar.AdminApi.Controllers; [ApiController] [Route("/Guilds")] diff --git a/extra/admin-api/Spacebar.AdminAPI/Controllers/Media/UserMediaController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/Media/UserMediaController.cs
index a4d915e5..f3415731 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Controllers/Media/UserMediaController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/Media/UserMediaController.cs
@@ -1,13 +1,13 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -using Spacebar.AdminAPI.Extensions; +using Spacebar.AdminApi.Extensions; using Spacebar.AdminApi.Models; -using Spacebar.AdminAPI.Services; +using Spacebar.AdminApi.Services; using Spacebar.Db.Contexts; using Spacebar.Db.Models; using Spacebar.RabbitMqUtilities; -namespace Spacebar.AdminAPI.Controllers.Media; +namespace Spacebar.AdminApi.Controllers.Media; [ApiController] [Route("/media/user")] diff --git a/extra/admin-api/Spacebar.AdminAPI/Controllers/PingController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/PingController.cs
index a2aceb50..05b6d876 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Controllers/PingController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/PingController.cs
@@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; -using Spacebar.AdminAPI.Services; +using Spacebar.AdminApi.Services; -namespace Spacebar.AdminAPI.Controllers; +namespace Spacebar.AdminApi.Controllers; [ApiController] [Route("/")] diff --git a/extra/admin-api/Spacebar.AdminAPI/Controllers/UserController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs
index 436c0f91..505f789e 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Controllers/UserController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs
@@ -4,14 +4,14 @@ using ArcaneLibs.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using RabbitMQ.Client; -using Spacebar.AdminAPI.Extensions; +using Spacebar.AdminApi.Extensions; using Spacebar.AdminApi.Models; -using Spacebar.AdminAPI.Services; +using Spacebar.AdminApi.Services; using Spacebar.Db.Contexts; using Spacebar.Db.Models; using Spacebar.RabbitMqUtilities; -namespace Spacebar.AdminAPI.Controllers; +namespace Spacebar.AdminApi.Controllers; [ApiController] [Route("/users")] @@ -48,7 +48,7 @@ public class UserController(ILogger<UserController> logger, Configuration config Disabled = x.Disabled, Deleted = x.Deleted, Email = x.Email, - Flags = ulong.Parse(x.Flags), + Flags = x.Flags, PublicFlags = x.PublicFlags, Rights = x.Rights, ApplicationBotUser = x.ApplicationBotUser == null ? null : new(), diff --git a/extra/admin-api/Spacebar.AdminAPI/Extensions/DbExtensions.cs b/extra/admin-api/Spacebar.AdminApi/Extensions/DbExtensions.cs
index f6318e8f..1220de91 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Extensions/DbExtensions.cs +++ b/extra/admin-api/Spacebar.AdminApi/Extensions/DbExtensions.cs
@@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore; using Spacebar.AdminApi.Models; using Spacebar.Db.Models; -namespace Spacebar.AdminAPI.Extensions; +namespace Spacebar.AdminApi.Extensions; public static class DbExtensions { public static string? GetString(this DbSet<Config> config, string key) => config.Find(key)?.Value; diff --git a/extra/admin-api/Spacebar.AdminAPI/Middleware/AuthenticationMiddleware.cs b/extra/admin-api/Spacebar.AdminApi/Middleware/AuthenticationMiddleware.cs
index dea89586..a5144add 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Middleware/AuthenticationMiddleware.cs +++ b/extra/admin-api/Spacebar.AdminApi/Middleware/AuthenticationMiddleware.cs
@@ -1,11 +1,11 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Cryptography; using Microsoft.IdentityModel.Tokens; -using Spacebar.AdminAPI.Services; +using Spacebar.AdminApi.Services; using Spacebar.Db.Contexts; using Spacebar.Db.Models; -namespace Spacebar.AdminAPI.Middleware; +namespace Spacebar.AdminApi.Middleware; public class AuthenticationMiddleware(RequestDelegate next) { private static Dictionary<string, User> _userCache = new(); diff --git a/extra/admin-api/Spacebar.AdminAPI/Program.cs b/extra/admin-api/Spacebar.AdminApi/Program.cs
index 93b28b15..f3e977cb 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Program.cs +++ b/extra/admin-api/Spacebar.AdminApi/Program.cs
@@ -1,8 +1,8 @@ using System.Text.Json.Serialization; using Microsoft.AspNetCore.Http.Timeouts; using Microsoft.EntityFrameworkCore; -using Spacebar.AdminAPI.Middleware; -using Spacebar.AdminAPI.Services; +using Spacebar.AdminApi.Middleware; +using Spacebar.AdminApi.Services; using Spacebar.Db.Contexts; using Spacebar.RabbitMqUtilities; diff --git a/extra/admin-api/Spacebar.AdminAPI/Properties/launchSettings.json b/extra/admin-api/Spacebar.AdminApi/Properties/launchSettings.json
index d362517c..d362517c 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Properties/launchSettings.json +++ b/extra/admin-api/Spacebar.AdminApi/Properties/launchSettings.json
diff --git a/extra/admin-api/Spacebar.AdminAPI/Services/AuthenticationService.cs b/extra/admin-api/Spacebar.AdminApi/Services/AuthenticationService.cs
index d402c5b9..788c8eb9 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Services/AuthenticationService.cs +++ b/extra/admin-api/Spacebar.AdminApi/Services/AuthenticationService.cs
@@ -4,7 +4,7 @@ using Microsoft.IdentityModel.Tokens; using Spacebar.Db.Contexts; using Spacebar.Db.Models; -namespace Spacebar.AdminAPI.Services; +namespace Spacebar.AdminApi.Services; public class AuthenticationService(SpacebarDbContext db, Configuration config) { private static Dictionary<string, User> _userCache = new(); diff --git a/extra/admin-api/Spacebar.AdminAPI/Services/Configuration.cs b/extra/admin-api/Spacebar.AdminApi/Services/Configuration.cs
index a58ef5c0..f6b2c029 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Services/Configuration.cs +++ b/extra/admin-api/Spacebar.AdminApi/Services/Configuration.cs
@@ -1,4 +1,4 @@ -namespace Spacebar.AdminAPI.Services; +namespace Spacebar.AdminApi.Services; public class Configuration { public Configuration(IConfiguration configuration) { diff --git a/extra/admin-api/Spacebar.AdminAPI/Spacebar.AdminAPI.csproj b/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.csproj
index 872e1188..950f8a1f 100644 --- a/extra/admin-api/Spacebar.AdminAPI/Spacebar.AdminAPI.csproj +++ b/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.csproj
@@ -16,6 +16,7 @@ <ItemGroup> <ProjectReference Include="..\Spacebar.AdminApi.Models\Spacebar.AdminApi.Models.csproj" /> + <ProjectReference Include="..\Spacebar.ConfigModel\Spacebar.ConfigModel.csproj" /> <ProjectReference Include="..\Spacebar.Db\Spacebar.Db.csproj" /> <ProjectReference Include="..\Utilities\Spacebar.RabbitMqUtilities\Spacebar.RabbitMqUtilities.csproj" /> </ItemGroup> diff --git a/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json b/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json new file mode 100644
index 00000000..03245773 --- /dev/null +++ b/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json
@@ -0,0 +1,27 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Trace", //Warning + "Microsoft.AspNetCore.Mvc": "Warning", //Warning + "Microsoft.AspNetCore.HostFiltering": "Warning", //Warning + "Microsoft.AspNetCore.Cors": "Warning", //Warning + // "Microsoft.EntityFrameworkCore": "Warning" + "Microsoft.EntityFrameworkCore.Database.Command": "Debug" + } + }, + "ConnectionStrings": { + "Spacebar": "Host=127.0.0.1; Username=postgres; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;", + }, + "RabbitMQ": { + "Host": "127.0.0.1", + "Port": 5673, + "Username": "guest", + "Password": "guest" + }, + "SpacebarAdminApi": { + "Enforce2FA": true, + "OverrideUid": null, + "DisableAuthentication": false + } +} diff --git a/extra/admin-api/Spacebar.AdminApi/appsettings.json b/extra/admin-api/Spacebar.AdminApi/appsettings.json new file mode 100644
index 00000000..10f68b8c --- /dev/null +++ b/extra/admin-api/Spacebar.AdminApi/appsettings.json
@@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/extra/admin-api/Spacebar.AdminApi/deps.json b/extra/admin-api/Spacebar.AdminApi/deps.json new file mode 100644
index 00000000..f1ddcb25 --- /dev/null +++ b/extra/admin-api/Spacebar.AdminApi/deps.json
@@ -0,0 +1,447 @@ +[ + { + "pname": "ArcaneLibs", + "version": "1.0.0-preview.20251005-232225", + "hash": "sha256-EsYLSiyX5Nj+ZpFb6FOcAYqDsQFSbvgm9NKaarJjK/0=" + }, + { + "pname": "ArcaneLibs.StringNormalisation", + "version": "1.0.0-preview.20251005-232225", + "hash": "sha256-CdftHRV6idRMojLMgnDRxgSOvfP1DU+9KTKyBGlwoSI=" + }, + { + "pname": "Humanizer.Core", + "version": "2.14.1", + "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" + }, + { + "pname": "Microsoft.AspNetCore.OpenApi", + "version": "10.0.0", + "hash": "sha256-mf3kOdkliLF4OD+CrmEhMNNDGOPKWGBOBWtX2EV5YEU=" + }, + { + "pname": "Microsoft.Build", + "version": "17.7.2", + "hash": "sha256-k35nFdPxC8t0zAltVSmAJtsepp/ubNIjPOsJ6k8jSqM=" + }, + { + "pname": "Microsoft.Build.Framework", + "version": "17.14.28", + "hash": "sha256-7RzEyIipumafwLW1xN1q23114NafG6PT0+RADElNsiM=" + }, + { + "pname": "Microsoft.Build.Framework", + "version": "17.7.2", + "hash": "sha256-fNWmVQYFTJDveAGmxEdNqJRAczV6+Ep8RA8clKBJFqw=" + }, + { + "pname": "Microsoft.Build.Tasks.Core", + "version": "17.14.28", + "hash": "sha256-M9zRXYijH2HtLlRXbrUK1a1LQ9zkT+DC9ZmMiiVZwv0=" + }, + { + "pname": "Microsoft.Build.Tasks.Core", + "version": "17.7.2", + "hash": "sha256-OrV/qWgZHzGlNUmaSfX5wDBcmg1aQeF3/OUHpSH+uZU=" + }, + { + "pname": "Microsoft.Build.Utilities.Core", + "version": "17.14.28", + "hash": "sha256-VFfO+UpyTpw2X/qiCCOCYzvMLuu7B+XVSSpJZQLkPzU=" + }, + { + "pname": "Microsoft.Build.Utilities.Core", + "version": "17.7.2", + "hash": "sha256-oatF0KfuP1nb4+OLNKg2/R/ZLO4EiACaO5leaxMEY4A=" + }, + { + "pname": "Microsoft.CodeAnalysis.Analyzers", + "version": "3.11.0", + "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" + }, + { + "pname": "Microsoft.CodeAnalysis.Common", + "version": "4.14.0", + "hash": "sha256-ne/zxH3GqoGB4OemnE8oJElG5mai+/67ASaKqwmL2BE=" + }, + { + "pname": "Microsoft.CodeAnalysis.CSharp", + "version": "4.14.0", + "hash": "sha256-5Mzj3XkYYLkwDWh17r1NEXSbXwwWYQPiOmkSMlgo1JY=" + }, + { + "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", + "version": "4.14.0", + "hash": "sha256-aNbV1a0yYBs0fpQawG6LXcbyoE8en+YFSpV5vcYE4J4=" + }, + { + "pname": "Microsoft.CodeAnalysis.Workspaces.Common", + "version": "4.14.0", + "hash": "sha256-0YfeaJe01WBUm9avy4a8FacQJXA1NkpnDpiXu4yz88I=" + }, + { + "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", + "version": "4.14.0", + "hash": "sha256-5SJfpRqzqCK0UbkmAaJpA/r1XJb0YAriMMeQHYC4d+o=" + }, + { + "pname": "Microsoft.EntityFrameworkCore", + "version": "10.0.0", + "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" + }, + { + "pname": "Microsoft.EntityFrameworkCore", + "version": "10.0.1", + "hash": "sha256-IxA+/nDA6hZkUm9bDT6bRu8+9qeHA3gbTvIhR9Ncg4w=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Abstractions", + "version": "10.0.0", + "hash": "sha256-UDgZbRQcGPaKsE53EH6bvJiv+Q4KSxAbnsVhTVFGG4Q=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Abstractions", + "version": "10.0.1", + "hash": "sha256-FMGXhAAgcVSedV0/GmUVqAwoiRzFJil9mQYr6eNgowg=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Analyzers", + "version": "10.0.0", + "hash": "sha256-7Q0jYJO50cqGI+u6gLpootbB8GZvgsgtg0F9FZI1jig=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Analyzers", + "version": "10.0.1", + "hash": "sha256-O949vr98WoSRXtiIo0ZI6dz2cibIBKbiuC+mQOe9bV4=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Design", + "version": "10.0.1", + "hash": "sha256-GGNZIGNEMhSGaMRFkRN4bOuCUBs5YVnX8klXarm319U=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Relational", + "version": "10.0.0", + "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Relational", + "version": "10.0.1", + "hash": "sha256-zLgxr/iW9HP8Fip1IDgr7X0Ar8OWKDvVmoEt65gG6VY=" + }, + { + "pname": "Microsoft.Extensions.Caching.Abstractions", + "version": "10.0.1", + "hash": "sha256-qVLAEqxPK/dNq+z1a6D9NqdcSg/18sfzZhlBWMkqV/A=" + }, + { + "pname": "Microsoft.Extensions.Caching.Memory", + "version": "10.0.0", + "hash": "sha256-AMgDSm1k6q0s17spGtyR5q8nAqUFDOxl/Fe38f9M+d4=" + }, + { + "pname": "Microsoft.Extensions.Caching.Memory", + "version": "10.0.1", + "hash": "sha256-Qb7xK6VEZDas0lJFaW1suKdFjtkSYwLHHxkQEfWIU2A=" + }, + { + "pname": "Microsoft.Extensions.Configuration", + "version": "10.0.0", + "hash": "sha256-MsLskVPpkCvov5+DWIaALCt1qfRRX4u228eHxvpE0dg=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.0", + "hash": "sha256-GcgrnTAieCV7AVT13zyOjfwwL86e99iiO/MiMOxPGG0=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.1", + "hash": "sha256-s4PDp+vtzdxKIxnOT3+dDRoTDopyl8kqmmw4KDnkOtQ=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Binder", + "version": "10.0.0", + "hash": "sha256-YSiWoA3VQR22k6+bSEAUqeG7UDzZlJfHWDTubUO5V8U=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "10.0.1", + "hash": "sha256-RKOB+zPrtQNUbJY/1jR54rKOM8KHPgynPExxugku3I8=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "9.0.0", + "hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.0", + "hash": "sha256-9iodXP39YqgxomnOPOxd/mzbG0JfOSXzFoNU3omT2Ps=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.1", + "hash": "sha256-zNUpau51ds7iQTaSUTFtyTHIUoinYc129W50CnufMdQ=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "9.0.0", + "hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c=" + }, + { + "pname": "Microsoft.Extensions.DependencyModel", + "version": "10.0.1", + "hash": "sha256-XIj2jEURe25YA4RhBSuCqQpic0YP+TZaO/dbBPCjad8=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "10.0.0", + "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "10.0.1", + "hash": "sha256-zuLP3SIpCToMOlIPOEv3Kq8y/minecd8k8GSkxFo13E=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "9.0.0", + "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "10.0.0", + "hash": "sha256-BnhgGZc01HwTSxogavq7Ueq4V7iMA3wPnbfRwQ4RhGk=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "10.0.1", + "hash": "sha256-NRk0feNE1fgi/hyO0AVDbSGJQRT+9yte6Lpm4Hz/2Bs=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "9.0.0", + "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "10.0.1", + "hash": "sha256-vBiSS1vqAC7eDrpJNT4H3A9qLikCSAepnNRbry0mKnk=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "9.0.0", + "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "10.0.0", + "hash": "sha256-Dup08KcptLjlnpN5t5//+p4n8FUTgRAq4n/w1s6us+I=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "10.0.1", + "hash": "sha256-EXmukq09erT4s+miQpBSYy3IY4HxxKlwEPL43/KoyEc=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "9.0.0", + "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" + }, + { + "pname": "Microsoft.IdentityModel.Abstractions", + "version": "8.15.0", + "hash": "sha256-LKTvERNUTMCEF7xs377tCMwOMRki93OS4kh6Yv0uXJ4=" + }, + { + "pname": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.15.0", + "hash": "sha256-LwzKiGjcnORvmQ9tim6lomXpfVlPpd/fE8FKTFWKlpM=" + }, + { + "pname": "Microsoft.IdentityModel.Logging", + "version": "8.15.0", + "hash": "sha256-mMXwsjGcrrmHR1mG7BLTKg/30mX+m93QVX17/ynOOd4=" + }, + { + "pname": "Microsoft.IdentityModel.Tokens", + "version": "8.15.0", + "hash": "sha256-7Lo/TsvqgNCEMyFssO3Om233521Pqgb9K9lUeHc5HMk=" + }, + { + "pname": "Microsoft.NET.StringTools", + "version": "17.14.28", + "hash": "sha256-UzREyvDxkiOQ4cEOQ5UCjkwXGrldIDCcbefECTPGjXI=" + }, + { + "pname": "Microsoft.NET.StringTools", + "version": "17.7.2", + "hash": "sha256-hQE07TCgcQuyu9ZHVq2gPDb0+xe8ECJUdrgh17bJP4o=" + }, + { + "pname": "Microsoft.OpenApi", + "version": "2.0.0", + "hash": "sha256-8eiM3Mx4Hx1etx52RlczoHG2z9XIpWgu2LQtWSt086k=" + }, + { + "pname": "Mono.TextTemplating", + "version": "3.0.0", + "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" + }, + { + "pname": "Newtonsoft.Json", + "version": "13.0.3", + "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + }, + { + "pname": "Npgsql", + "version": "10.0.0", + "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + }, + { + "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", + "version": "10.0.0", + "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" + }, + { + "pname": "RabbitMQ.Client", + "version": "7.2.0", + "hash": "sha256-NkEb2ey0jo/OAeaVOUwIeSbeplqkOsgv1+ys8ZQgemQ=" + }, + { + "pname": "System.CodeDom", + "version": "6.0.0", + "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" + }, + { + "pname": "System.CodeDom", + "version": "7.0.0", + "hash": "sha256-7IPt39cY+0j0ZcRr/J45xPtEjnSXdUJ/5ai3ebaYQiE=" + }, + { + "pname": "System.CodeDom", + "version": "9.0.0", + "hash": "sha256-578lcBgswW0eM16r0EnJzfGodPx86RxxFoZHc2PSzsw=" + }, + { + "pname": "System.Composition", + "version": "9.0.0", + "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" + }, + { + "pname": "System.Composition.AttributedModel", + "version": "9.0.0", + "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" + }, + { + "pname": "System.Composition.Convention", + "version": "9.0.0", + "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" + }, + { + "pname": "System.Composition.Hosting", + "version": "9.0.0", + "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" + }, + { + "pname": "System.Composition.Runtime", + "version": "9.0.0", + "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" + }, + { + "pname": "System.Composition.TypedParts", + "version": "9.0.0", + "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + }, + { + "pname": "System.Configuration.ConfigurationManager", + "version": "7.0.0", + "hash": "sha256-SgBexTTjRn23uuXvkzO0mz0qOfA23MiS4Wv+qepMLZE=" + }, + { + "pname": "System.Configuration.ConfigurationManager", + "version": "9.0.0", + "hash": "sha256-+pLnTC0YDP6Kjw5DVBiFrV/Q3x5is/+6N6vAtjvhVWk=" + }, + { + "pname": "System.Diagnostics.EventLog", + "version": "9.0.0", + "hash": "sha256-tPvt6yoAp56sK/fe+/ei8M65eavY2UUhRnbrREj/Ems=" + }, + { + "pname": "System.Formats.Nrbf", + "version": "9.0.0", + "hash": "sha256-c4qf6CocQUZB0ySGQd8s15PXY7xfrjQqMGXxkwytKyw=" + }, + { + "pname": "System.IdentityModel.Tokens.Jwt", + "version": "8.15.0", + "hash": "sha256-5O0wbGp0gWnukK+0mWBjMnP1bZc6N0xuNcO2qmFiUX8=" + }, + { + "pname": "System.Reflection.MetadataLoadContext", + "version": "7.0.0", + "hash": "sha256-VYl6SFD130K9Aw4eJH16ApJ9Sau4Xu0dcxEip2veuTI=" + }, + { + "pname": "System.Resources.Extensions", + "version": "9.0.0", + "hash": "sha256-y2gLEMuAy6QfEyNJxABC/ayMWGnwlpX735jsUQLktho=" + }, + { + "pname": "System.Security.Cryptography.Pkcs", + "version": "7.0.0", + "hash": "sha256-3J3vL9hcKSuZjT2GKappa2A9p2xJm1nH2asTNAl8ZCA=" + }, + { + "pname": "System.Security.Cryptography.Pkcs", + "version": "7.0.2", + "hash": "sha256-qS5Z/Yo8J+f3ExVX5Qkcpj1Z57oUZqz5rWa1h5bVpl8=" + }, + { + "pname": "System.Security.Cryptography.Pkcs", + "version": "9.0.0", + "hash": "sha256-AjG14mGeSc2Ka4QSelGBM1LrGBW3VJX60lnihKyJjGY=" + }, + { + "pname": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0", + "hash": "sha256-gPgPU7k/InTqmXoRzQfUMEKL3QuTnOKowFqmXTnWaBQ=" + }, + { + "pname": "System.Security.Cryptography.Xml", + "version": "7.0.1", + "hash": "sha256-CH8+JVC8LyCSW75/6ZQ7ecMbSOAE1c16z4dG8JTp01w=" + }, + { + "pname": "System.Security.Cryptography.Xml", + "version": "9.0.0", + "hash": "sha256-SQJWwAFrJUddEU6JiZB52FM9tGjRlJAYH8oYVzG5IJU=" + }, + { + "pname": "System.Security.Permissions", + "version": "7.0.0", + "hash": "sha256-DOFoX+AKRmrkllykHheR8FfUXYx/Ph+I/HYuReQydXI=" + }, + { + "pname": "System.Security.Permissions", + "version": "9.0.0", + "hash": "sha256-BFrA9ottmQtLIAiKiGRbfSUpzNJwuaOCeFRDN4Z0ku0=" + }, + { + "pname": "System.Threading.RateLimiting", + "version": "8.0.0", + "hash": "sha256-KOEWEt6ZthvZHJ2Wp70d9nBhBrPqobGQDi2twlKYh/w=" + }, + { + "pname": "System.Windows.Extensions", + "version": "9.0.0", + "hash": "sha256-RErD+Ju15qtnwdwB7E0SjjJGAnhXwJyC7UPcl24Z3Vs=" + }, + { + "pname": "Unidecode.NET", + "version": "2.1.0", + "hash": "sha256-nfxHxG/YwRftOP+wiSW8NL5hIVwt+wnfRb0lURjrZoc=" + } +] diff --git a/extra/admin-api/Spacebar.CleanSettingsRows/deps.json b/extra/admin-api/Spacebar.CleanSettingsRows/deps.json new file mode 100644
index 00000000..2938cdaa --- /dev/null +++ b/extra/admin-api/Spacebar.CleanSettingsRows/deps.json
@@ -0,0 +1,492 @@ +[ + { + "pname": "Humanizer.Core", + "version": "2.14.1", + "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" + }, + { + "pname": "Microsoft.Build", + "version": "17.7.2", + "hash": "sha256-k35nFdPxC8t0zAltVSmAJtsepp/ubNIjPOsJ6k8jSqM=" + }, + { + "pname": "Microsoft.Build.Framework", + "version": "17.14.28", + "hash": "sha256-7RzEyIipumafwLW1xN1q23114NafG6PT0+RADElNsiM=" + }, + { + "pname": "Microsoft.Build.Framework", + "version": "17.7.2", + "hash": "sha256-fNWmVQYFTJDveAGmxEdNqJRAczV6+Ep8RA8clKBJFqw=" + }, + { + "pname": "Microsoft.Build.Tasks.Core", + "version": "17.14.28", + "hash": "sha256-M9zRXYijH2HtLlRXbrUK1a1LQ9zkT+DC9ZmMiiVZwv0=" + }, + { + "pname": "Microsoft.Build.Tasks.Core", + "version": "17.7.2", + "hash": "sha256-OrV/qWgZHzGlNUmaSfX5wDBcmg1aQeF3/OUHpSH+uZU=" + }, + { + "pname": "Microsoft.Build.Utilities.Core", + "version": "17.14.28", + "hash": "sha256-VFfO+UpyTpw2X/qiCCOCYzvMLuu7B+XVSSpJZQLkPzU=" + }, + { + "pname": "Microsoft.Build.Utilities.Core", + "version": "17.7.2", + "hash": "sha256-oatF0KfuP1nb4+OLNKg2/R/ZLO4EiACaO5leaxMEY4A=" + }, + { + "pname": "Microsoft.CodeAnalysis.Analyzers", + "version": "3.11.0", + "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" + }, + { + "pname": "Microsoft.CodeAnalysis.Common", + "version": "4.14.0", + "hash": "sha256-ne/zxH3GqoGB4OemnE8oJElG5mai+/67ASaKqwmL2BE=" + }, + { + "pname": "Microsoft.CodeAnalysis.CSharp", + "version": "4.14.0", + "hash": "sha256-5Mzj3XkYYLkwDWh17r1NEXSbXwwWYQPiOmkSMlgo1JY=" + }, + { + "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", + "version": "4.14.0", + "hash": "sha256-aNbV1a0yYBs0fpQawG6LXcbyoE8en+YFSpV5vcYE4J4=" + }, + { + "pname": "Microsoft.CodeAnalysis.Workspaces.Common", + "version": "4.14.0", + "hash": "sha256-0YfeaJe01WBUm9avy4a8FacQJXA1NkpnDpiXu4yz88I=" + }, + { + "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", + "version": "4.14.0", + "hash": "sha256-5SJfpRqzqCK0UbkmAaJpA/r1XJb0YAriMMeQHYC4d+o=" + }, + { + "pname": "Microsoft.EntityFrameworkCore", + "version": "10.0.0", + "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" + }, + { + "pname": "Microsoft.EntityFrameworkCore", + "version": "10.0.1", + "hash": "sha256-IxA+/nDA6hZkUm9bDT6bRu8+9qeHA3gbTvIhR9Ncg4w=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Abstractions", + "version": "10.0.0", + "hash": "sha256-UDgZbRQcGPaKsE53EH6bvJiv+Q4KSxAbnsVhTVFGG4Q=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Abstractions", + "version": "10.0.1", + "hash": "sha256-FMGXhAAgcVSedV0/GmUVqAwoiRzFJil9mQYr6eNgowg=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Analyzers", + "version": "10.0.0", + "hash": "sha256-7Q0jYJO50cqGI+u6gLpootbB8GZvgsgtg0F9FZI1jig=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Analyzers", + "version": "10.0.1", + "hash": "sha256-O949vr98WoSRXtiIo0ZI6dz2cibIBKbiuC+mQOe9bV4=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Design", + "version": "10.0.1", + "hash": "sha256-GGNZIGNEMhSGaMRFkRN4bOuCUBs5YVnX8klXarm319U=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Relational", + "version": "10.0.0", + "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Relational", + "version": "10.0.1", + "hash": "sha256-zLgxr/iW9HP8Fip1IDgr7X0Ar8OWKDvVmoEt65gG6VY=" + }, + { + "pname": "Microsoft.Extensions.Caching.Abstractions", + "version": "10.0.1", + "hash": "sha256-qVLAEqxPK/dNq+z1a6D9NqdcSg/18sfzZhlBWMkqV/A=" + }, + { + "pname": "Microsoft.Extensions.Caching.Memory", + "version": "10.0.0", + "hash": "sha256-AMgDSm1k6q0s17spGtyR5q8nAqUFDOxl/Fe38f9M+d4=" + }, + { + "pname": "Microsoft.Extensions.Caching.Memory", + "version": "10.0.1", + "hash": "sha256-Qb7xK6VEZDas0lJFaW1suKdFjtkSYwLHHxkQEfWIU2A=" + }, + { + "pname": "Microsoft.Extensions.Configuration", + "version": "10.0.0", + "hash": "sha256-MsLskVPpkCvov5+DWIaALCt1qfRRX4u228eHxvpE0dg=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.0", + "hash": "sha256-GcgrnTAieCV7AVT13zyOjfwwL86e99iiO/MiMOxPGG0=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.1", + "hash": "sha256-s4PDp+vtzdxKIxnOT3+dDRoTDopyl8kqmmw4KDnkOtQ=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Binder", + "version": "10.0.0", + "hash": "sha256-YSiWoA3VQR22k6+bSEAUqeG7UDzZlJfHWDTubUO5V8U=" + }, + { + "pname": "Microsoft.Extensions.Configuration.CommandLine", + "version": "10.0.0", + "hash": "sha256-ldTiRFqnv8/pA0gl6UR+4DDGAIZOf9+MhaLWOuKOXOI=" + }, + { + "pname": "Microsoft.Extensions.Configuration.EnvironmentVariables", + "version": "10.0.0", + "hash": "sha256-UayfeqrAmNyfOkuhcBKfj8UpjQqV/ZMqWrDyxCSG1MA=" + }, + { + "pname": "Microsoft.Extensions.Configuration.FileExtensions", + "version": "10.0.0", + "hash": "sha256-rN+3rqrHiTaBfHgP+E4dA8Qm2cFJPfbEcd93yKLsqlQ=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Json", + "version": "10.0.0", + "hash": "sha256-VCFukgsxiQ2MFGE6RDMFTGopBHbcZL2t0ER7ENaFXRY=" + }, + { + "pname": "Microsoft.Extensions.Configuration.UserSecrets", + "version": "10.0.0", + "hash": "sha256-uIoIpbDPRMfFqT8Y6j/wHbFCAly6H1N9qpxnomRbHIo=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "10.0.0", + "hash": "sha256-LYm9hVlo/R9c2aAKHsDYJ5vY9U0+3Jvclme3ou3BtvQ=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "10.0.1", + "hash": "sha256-RKOB+zPrtQNUbJY/1jR54rKOM8KHPgynPExxugku3I8=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "9.0.0", + "hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.0", + "hash": "sha256-9iodXP39YqgxomnOPOxd/mzbG0JfOSXzFoNU3omT2Ps=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.1", + "hash": "sha256-zNUpau51ds7iQTaSUTFtyTHIUoinYc129W50CnufMdQ=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "9.0.0", + "hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c=" + }, + { + "pname": "Microsoft.Extensions.DependencyModel", + "version": "10.0.1", + "hash": "sha256-XIj2jEURe25YA4RhBSuCqQpic0YP+TZaO/dbBPCjad8=" + }, + { + "pname": "Microsoft.Extensions.Diagnostics", + "version": "10.0.0", + "hash": "sha256-o7QkCisEcFIh227qBUfWFci2ns4cgEpLqpX7YvHGToQ=" + }, + { + "pname": "Microsoft.Extensions.Diagnostics.Abstractions", + "version": "10.0.0", + "hash": "sha256-cix7QxQ/g3sj6reXu3jn0cRv2RijzceaLLkchEGTt5E=" + }, + { + "pname": "Microsoft.Extensions.FileProviders.Abstractions", + "version": "10.0.0", + "hash": "sha256-CHDs2HCN8QcfuYQpgNVszZ5dfXFe4yS9K2GoQXecc20=" + }, + { + "pname": "Microsoft.Extensions.FileProviders.Physical", + "version": "10.0.0", + "hash": "sha256-2Rw/cwBO+/A3QY2IjN/c8Y0LhtC1qTBL7VdJiD1J2UQ=" + }, + { + "pname": "Microsoft.Extensions.FileSystemGlobbing", + "version": "10.0.0", + "hash": "sha256-ETfVTdsdBtp69EggLg/AARTQW4lLQYVdVldXIQrsjZA=" + }, + { + "pname": "Microsoft.Extensions.Hosting", + "version": "10.0.0", + "hash": "sha256-tY0g6lCy2yFprE+NmriiU6FGmwpzxV8LqE0ZFNKIwuM=" + }, + { + "pname": "Microsoft.Extensions.Hosting.Abstractions", + "version": "10.0.0", + "hash": "sha256-Sub3Thay/+eR84cEODk/nPh1oYIYtawvDX6r0duReqo=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "10.0.0", + "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "10.0.1", + "hash": "sha256-zuLP3SIpCToMOlIPOEv3Kq8y/minecd8k8GSkxFo13E=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "9.0.0", + "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "10.0.0", + "hash": "sha256-BnhgGZc01HwTSxogavq7Ueq4V7iMA3wPnbfRwQ4RhGk=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "10.0.1", + "hash": "sha256-NRk0feNE1fgi/hyO0AVDbSGJQRT+9yte6Lpm4Hz/2Bs=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "9.0.0", + "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" + }, + { + "pname": "Microsoft.Extensions.Logging.Configuration", + "version": "10.0.0", + "hash": "sha256-7/TWO1aq8hdgbcTEKDBWIjgSC9KpFN3kRnMX+12bOkU=" + }, + { + "pname": "Microsoft.Extensions.Logging.Console", + "version": "10.0.0", + "hash": "sha256-Rsblo7GSMTOr43876KkdvqS6wU9Typ1yoFK3tL50CBk=" + }, + { + "pname": "Microsoft.Extensions.Logging.Debug", + "version": "10.0.0", + "hash": "sha256-n/+KRVlsgKm17cJImaoAPHAObHpApW/hf6mMsQFGrvY=" + }, + { + "pname": "Microsoft.Extensions.Logging.EventLog", + "version": "10.0.0", + "hash": "sha256-4RJ2r80RtI3QUAhCAYbGnA0YcTmouqtZvQU9o3CrB38=" + }, + { + "pname": "Microsoft.Extensions.Logging.EventSource", + "version": "10.0.0", + "hash": "sha256-tqC13Qwkf4x14iGxOYlXTyeoN8KPVX+mupv2LdpzGHo=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "10.0.0", + "hash": "sha256-j5MOqZSKeUtxxzmZjzZMGy0vELHdvPraqwTQQQNVsYA=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "10.0.1", + "hash": "sha256-vBiSS1vqAC7eDrpJNT4H3A9qLikCSAepnNRbry0mKnk=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "9.0.0", + "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" + }, + { + "pname": "Microsoft.Extensions.Options.ConfigurationExtensions", + "version": "10.0.0", + "hash": "sha256-XGAs5DxMvWnmjX8dqRwKY0vsuS40SHvsfJqB1rO4L7k=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "10.0.0", + "hash": "sha256-Dup08KcptLjlnpN5t5//+p4n8FUTgRAq4n/w1s6us+I=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "10.0.1", + "hash": "sha256-EXmukq09erT4s+miQpBSYy3IY4HxxKlwEPL43/KoyEc=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "9.0.0", + "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" + }, + { + "pname": "Microsoft.NET.StringTools", + "version": "17.14.28", + "hash": "sha256-UzREyvDxkiOQ4cEOQ5UCjkwXGrldIDCcbefECTPGjXI=" + }, + { + "pname": "Microsoft.NET.StringTools", + "version": "17.7.2", + "hash": "sha256-hQE07TCgcQuyu9ZHVq2gPDb0+xe8ECJUdrgh17bJP4o=" + }, + { + "pname": "Mono.TextTemplating", + "version": "3.0.0", + "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" + }, + { + "pname": "Newtonsoft.Json", + "version": "13.0.3", + "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + }, + { + "pname": "Npgsql", + "version": "10.0.0", + "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + }, + { + "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", + "version": "10.0.0", + "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" + }, + { + "pname": "System.CodeDom", + "version": "6.0.0", + "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" + }, + { + "pname": "System.CodeDom", + "version": "7.0.0", + "hash": "sha256-7IPt39cY+0j0ZcRr/J45xPtEjnSXdUJ/5ai3ebaYQiE=" + }, + { + "pname": "System.CodeDom", + "version": "9.0.0", + "hash": "sha256-578lcBgswW0eM16r0EnJzfGodPx86RxxFoZHc2PSzsw=" + }, + { + "pname": "System.Composition", + "version": "9.0.0", + "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" + }, + { + "pname": "System.Composition.AttributedModel", + "version": "9.0.0", + "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" + }, + { + "pname": "System.Composition.Convention", + "version": "9.0.0", + "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" + }, + { + "pname": "System.Composition.Hosting", + "version": "9.0.0", + "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" + }, + { + "pname": "System.Composition.Runtime", + "version": "9.0.0", + "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" + }, + { + "pname": "System.Composition.TypedParts", + "version": "9.0.0", + "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + }, + { + "pname": "System.Configuration.ConfigurationManager", + "version": "7.0.0", + "hash": "sha256-SgBexTTjRn23uuXvkzO0mz0qOfA23MiS4Wv+qepMLZE=" + }, + { + "pname": "System.Configuration.ConfigurationManager", + "version": "9.0.0", + "hash": "sha256-+pLnTC0YDP6Kjw5DVBiFrV/Q3x5is/+6N6vAtjvhVWk=" + }, + { + "pname": "System.Diagnostics.EventLog", + "version": "10.0.0", + "hash": "sha256-pN3tld926Fp0n5ZNjjzIJviUQrynlOAB0vhc1aoso6E=" + }, + { + "pname": "System.Diagnostics.EventLog", + "version": "9.0.0", + "hash": "sha256-tPvt6yoAp56sK/fe+/ei8M65eavY2UUhRnbrREj/Ems=" + }, + { + "pname": "System.Formats.Nrbf", + "version": "9.0.0", + "hash": "sha256-c4qf6CocQUZB0ySGQd8s15PXY7xfrjQqMGXxkwytKyw=" + }, + { + "pname": "System.Reflection.MetadataLoadContext", + "version": "7.0.0", + "hash": "sha256-VYl6SFD130K9Aw4eJH16ApJ9Sau4Xu0dcxEip2veuTI=" + }, + { + "pname": "System.Resources.Extensions", + "version": "9.0.0", + "hash": "sha256-y2gLEMuAy6QfEyNJxABC/ayMWGnwlpX735jsUQLktho=" + }, + { + "pname": "System.Security.Cryptography.Pkcs", + "version": "7.0.0", + "hash": "sha256-3J3vL9hcKSuZjT2GKappa2A9p2xJm1nH2asTNAl8ZCA=" + }, + { + "pname": "System.Security.Cryptography.Pkcs", + "version": "7.0.2", + "hash": "sha256-qS5Z/Yo8J+f3ExVX5Qkcpj1Z57oUZqz5rWa1h5bVpl8=" + }, + { + "pname": "System.Security.Cryptography.Pkcs", + "version": "9.0.0", + "hash": "sha256-AjG14mGeSc2Ka4QSelGBM1LrGBW3VJX60lnihKyJjGY=" + }, + { + "pname": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0", + "hash": "sha256-gPgPU7k/InTqmXoRzQfUMEKL3QuTnOKowFqmXTnWaBQ=" + }, + { + "pname": "System.Security.Cryptography.Xml", + "version": "7.0.1", + "hash": "sha256-CH8+JVC8LyCSW75/6ZQ7ecMbSOAE1c16z4dG8JTp01w=" + }, + { + "pname": "System.Security.Cryptography.Xml", + "version": "9.0.0", + "hash": "sha256-SQJWwAFrJUddEU6JiZB52FM9tGjRlJAYH8oYVzG5IJU=" + }, + { + "pname": "System.Security.Permissions", + "version": "7.0.0", + "hash": "sha256-DOFoX+AKRmrkllykHheR8FfUXYx/Ph+I/HYuReQydXI=" + }, + { + "pname": "System.Security.Permissions", + "version": "9.0.0", + "hash": "sha256-BFrA9ottmQtLIAiKiGRbfSUpzNJwuaOCeFRDN4Z0ku0=" + }, + { + "pname": "System.Windows.Extensions", + "version": "9.0.0", + "hash": "sha256-RErD+Ju15qtnwdwB7E0SjjJGAnhXwJyC7UPcl24Z3Vs=" + } +] diff --git a/extra/admin-api/Spacebar.ConfigModel/Class1.cs b/extra/admin-api/Spacebar.ConfigModel/Class1.cs new file mode 100644
index 00000000..948ca91a --- /dev/null +++ b/extra/admin-api/Spacebar.ConfigModel/Class1.cs
@@ -0,0 +1,64 @@ +using System.Text.Json.Serialization; + +namespace Spacebar.ConfigModel; + +public class Config +{ + [JsonPropertyName("admin")] public EndpointConfig Admin { get; set; } = null!; + [JsonPropertyName("api")] public EndpointConfig Api { get; set; } = null!; + [JsonPropertyName("gateway")] public EndpointConfig Gateway { get; set; } = null!; + [JsonPropertyName("cdn")] public EndpointConfig Cdn { get; set; } = null!; + + public Config ReadFromKv(Dictionary<string, object?> kv) + { + // to object + + + foreach (var (key, value) in kv) + { + switch (key.Split('_', 2)[0]) + { + default: + Console.WriteLine($"Unrecognized config key prefix: {key}"); + continue; + } + } + + return this; + } +} + +public class EndpointConfig +{ + [JsonPropertyName("endpointPrivate")] public string? EndpointPrivate { get; set; } + [JsonPropertyName("endpointPublic")] public string? EndpointPublic { get; set; } + + public EndpointConfig ReadFromKv(Dictionary<string, object?> kv, string prefix) + { + foreach (var (key, value) in kv) + { + if (!key.StartsWith(prefix + "_")) continue; + var subKey = key[(prefix + "_").Length..]; + switch (subKey) + { + case "ENDPOINT_PRIVATE": + EndpointPrivate = value?.ToString(); + break; + case "ENDPOINT_PUBLIC": + EndpointPublic = value?.ToString(); + break; + default: + Console.WriteLine($"Unrecognized config key: {key}"); + break; + } + } + + return this; + } +} + +public class ApiConfig : EndpointConfig +{ + [JsonPropertyName("activeVersions")] public List<string> ActiveVersions { get; set; } = null!; + [JsonPropertyName("defaultVersion")] public string DefaultVersion { get; set; } = null!; +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.ConfigModel/Extensions/JsonExtensions.cs b/extra/admin-api/Spacebar.ConfigModel/Extensions/JsonExtensions.cs new file mode 100644
index 00000000..20ae7331 --- /dev/null +++ b/extra/admin-api/Spacebar.ConfigModel/Extensions/JsonExtensions.cs
@@ -0,0 +1,119 @@ +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Spacebar.ConfigModel.Extensions; + +public static class JsonExtensions +{ + extension(Dictionary<string, string?> kv) + { + public JsonObject ToNestedJsonObject(string path = "$") + { + JsonObject root = new(); + // group by prefix + var groups = kv.GroupBy(kvItem => kvItem.Key.Split('_', 2)[0]); + foreach (var group in groups) + { + var prefix = group.Key; + + if (group.Count() == 1 && !group.First().Key.Contains('_')) + { + root[prefix] = group.First().Value == null ? null : JsonNode.Parse(group.First().Value!); + Console.WriteLine("[CONFIG] Single Key: {0}.{1}, Value: {2}", path, prefix, root[prefix]?.ToJsonString()); + continue; + } + + var nestedValues = group.Where(x => x.Key.Contains('_')).ToDictionary(kvItem => kvItem.Key[(prefix.Length + 1)..], kvItem => kvItem.Value); + + if (nestedValues.All(x => int.TryParse(x.Key.Split('_')[0], out _))) + { + Console.WriteLine("[CONFIG] Array Key Detected: {0}.{1}", path, prefix); + var arr = new JsonArray(); + if (nestedValues.All(x => x.Key.Contains('_'))) + { + var objs = nestedValues.GroupBy(x => x.Key.Split('_', 2)[0]); + foreach (var objGroup in objs.OrderBy(x => int.Parse(x.Key))) + { + var i = objGroup.Key; + var objValues = objGroup.ToDictionary(kvItem => kvItem.Key[(i.Length + 1)..], kvItem => kvItem.Value); + var obj = objValues.ToNestedJsonObject($"{path}.{prefix}[{i}]"); + arr.Add(obj); + Console.WriteLine($" - ${path}.{prefix}[{i}]: {obj.ToJsonString()}"); + } + } + else + foreach (var (i, arrayItem) in nestedValues.OrderBy(x => int.Parse(x.Key))) + { + arr.Add(arrayItem == null ? null : JsonNode.Parse(arrayItem)); + Console.WriteLine($" - {path}.{prefix}[{i}]: {arrayItem}"); + } + + root[prefix] = arr; + } + else + { + root[prefix] = nestedValues.ToNestedJsonObject($"{path}.{prefix}"); + } + } + + return root; + } + } + + extension(JsonObject jo) + { + public Dictionary<string, string?> ToFlatKv(string path = "$") + { + var kv = new Dictionary<string, string?>(); + var jso = new JsonSerializerOptions() + { + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + foreach (var (key, value) in jo) + { + var currentPath = path == "$" ? key : $"{path}_{key}"; + + switch (value) + { + case JsonObject nestedObj: + var nestedKv = nestedObj.ToFlatKv(currentPath); + foreach (var (nestedKey, nestedValue) in nestedKv) + { + kv[nestedKey] = nestedValue; + } + + break; + case JsonArray arr: + for (int i = 0; i < arr.Count; i++) + { + var item = arr[i]; + var itemPath = $"{currentPath}_{i}"; + switch (item) + { + case JsonObject arrObj: + var arrObjKv = arrObj.ToFlatKv(itemPath); + foreach (var (arrObjKey, arrObjValue) in arrObjKv) + { + kv[arrObjKey] = arrObjValue; + } + + break; + default: + kv[itemPath] = item?.ToJsonString(jso); + break; + } + } + + break; + default: + Console.WriteLine(value?.GetType()); + kv[currentPath] = value?.ToJsonString(jso); + break; + } + } + + return kv; + } + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.ConfigModel/Spacebar.ConfigModel.csproj b/extra/admin-api/Spacebar.ConfigModel/Spacebar.ConfigModel.csproj new file mode 100644
index 00000000..237d6616 --- /dev/null +++ b/extra/admin-api/Spacebar.ConfigModel/Spacebar.ConfigModel.csproj
@@ -0,0 +1,9 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net10.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + +</Project> diff --git a/extra/admin-api/Spacebar.Db/Contexts/SpacebarDbContext.cs b/extra/admin-api/Spacebar.Db/Contexts/SpacebarDbContext.cs
index 51699513..7d380b1c 100644 --- a/extra/admin-api/Spacebar.Db/Contexts/SpacebarDbContext.cs +++ b/extra/admin-api/Spacebar.Db/Contexts/SpacebarDbContext.cs
@@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Spacebar.Db.Models; using Stream = Spacebar.Db.Models.Stream; @@ -15,6 +13,8 @@ public partial class SpacebarDbContext : DbContext public virtual DbSet<Application> Applications { get; set; } + public virtual DbSet<ApplicationCommand> ApplicationCommands { get; set; } + public virtual DbSet<Attachment> Attachments { get; set; } public virtual DbSet<AuditLog> AuditLogs { get; set; } @@ -47,6 +47,8 @@ public partial class SpacebarDbContext : DbContext public virtual DbSet<Guild> Guilds { get; set; } + public virtual DbSet<InstanceBan> InstanceBans { get; set; } + public virtual DbSet<Invite> Invites { get; set; } public virtual DbSet<Member> Members { get; set; } @@ -120,6 +122,16 @@ public partial class SpacebarDbContext : DbContext .HasConstraintName("FK_a36ed02953077f408d0f3ebc424"); }); + modelBuilder.Entity<ApplicationCommand>(entity => + { + entity.HasKey(e => e.Id).HasName("PK_0f73c2f025989c407947e1f75fe"); + + entity.Property(e => e.DmPermission).HasDefaultValue(true); + entity.Property(e => e.Options).HasDefaultValueSql("'[]'::text"); + entity.Property(e => e.Type).HasDefaultValue(1); + entity.Property(e => e.Version).HasDefaultValueSql("'0'::character varying"); + }); + modelBuilder.Entity<Attachment>(entity => { entity.HasKey(e => e.Id).HasName("PK_5e1f050bcff31e3084a1d662412"); @@ -266,6 +278,17 @@ public partial class SpacebarDbContext : DbContext entity.HasOne(d => d.WidgetChannel).WithMany(p => p.GuildWidgetChannels).HasConstraintName("FK_9d1d665379eefde7876a17afa99"); }); + modelBuilder.Entity<InstanceBan>(entity => + { + entity.HasKey(e => e.Id).HasName("PK_3aa6e80a6d325601054892b1340"); + + entity.Property(e => e.CreatedAt).HasDefaultValueSql("now()"); + + entity.HasOne(d => d.OriginInstanceBan).WithOne(p => p.InverseOriginInstanceBan) + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_0b02d18d0d830f160c921192a30"); + }); + modelBuilder.Entity<Invite>(entity => { entity.HasKey(e => e.Code).HasName("PK_33fd8a248db1cd832baa8aa25bf"); @@ -321,7 +344,6 @@ public partial class SpacebarDbContext : DbContext { entity.HasKey(e => e.Id).HasName("PK_18325f38ae6de43878487eff986"); - entity.Property(e => e.Flags).HasDefaultValue(0); entity.Property(e => e.Timestamp).HasDefaultValueSql("now()"); entity.HasOne(d => d.Application).WithMany(p => p.Messages).HasConstraintName("FK_5d3ec1cb962de6488637fd779d6"); @@ -342,7 +364,9 @@ public partial class SpacebarDbContext : DbContext .OnDelete(DeleteBehavior.Cascade) .HasConstraintName("FK_b0525304f2262b7014245351c76"); - entity.HasOne(d => d.MessageReferenceNavigation).WithMany(p => p.InverseMessageReferenceNavigation).HasConstraintName("FK_61a92bb65b302a76d9c1fcd3174"); + entity.HasOne(d => d.MessageReferenceNavigation).WithMany(p => p.InverseMessageReferenceNavigation) + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_61a92bb65b302a76d9c1fcd3174"); entity.HasOne(d => d.Webhook).WithMany(p => p.Messages).HasConstraintName("FK_f83c04bcf1df4e5c0e7a52ed348"); @@ -475,8 +499,6 @@ public partial class SpacebarDbContext : DbContext { entity.HasKey(e => e.Id).HasName("PK_de8fc5a9c364568f294798fe1e9"); - entity.Property(e => e.Closed).HasDefaultValue(false); - entity.HasOne(d => d.Channel).WithMany(p => p.Recipients).HasConstraintName("FK_2f18ee1ba667f233ae86c0ea60e"); entity.HasOne(d => d.User).WithMany(p => p.Recipients).HasConstraintName("FK_6157e8b6ba4e6e3089616481fe2"); @@ -495,8 +517,6 @@ public partial class SpacebarDbContext : DbContext { entity.HasKey(e => e.Id).HasName("PK_c1433d71a4838793a49dcad46ab"); - entity.Property(e => e.Flags).HasDefaultValue(0); - entity.HasOne(d => d.Guild).WithMany(p => p.Roles).HasConstraintName("FK_c32c1ab1c4dc7dcb0278c4b1b8b"); }); @@ -518,6 +538,8 @@ public partial class SpacebarDbContext : DbContext { entity.HasKey(e => e.Id).HasName("PK_3238ef96f18b355b671619111bc"); + entity.Property(e => e.Activities).HasDefaultValueSql("'[]'::text"); + entity.HasOne(d => d.User).WithMany(p => p.Sessions) .OnDelete(DeleteBehavior.Cascade) .HasConstraintName("FK_085d540d9f418cfbdc7bd55bb19"); @@ -560,8 +582,6 @@ public partial class SpacebarDbContext : DbContext { entity.HasKey(e => e.Id).HasName("PK_49bdc3f66394c12478f8371c546"); - entity.Property(e => e.Used).HasDefaultValue(false); - entity.HasOne(d => d.Stream).WithMany(p => p.StreamSessions).HasConstraintName("FK_8b5a028a34dae9ee54af37c9c32"); entity.HasOne(d => d.User).WithMany(p => p.StreamSessions).HasConstraintName("FK_13ae5c29aff4d0890c54179511a"); @@ -602,8 +622,6 @@ public partial class SpacebarDbContext : DbContext { entity.HasKey(e => e.Id).HasName("PK_a3ffb1c0c8416b9fc6f907b7433"); - entity.Property(e => e.WebauthnEnabled).HasDefaultValue(false); - entity.HasOne(d => d.SettingsIndexNavigation).WithOne(p => p.User).HasConstraintName("FK_0c14beb78d8c5ccba66072adbc7"); }); diff --git a/extra/admin-api/Spacebar.Db/Models/Application.cs b/extra/admin-api/Spacebar.Db/Models/Application.cs
index 27d8a45b..a5dda6f8 100644 --- a/extra/admin-api/Spacebar.Db/Models/Application.cs +++ b/extra/admin-api/Spacebar.Db/Models/Application.cs
@@ -86,12 +86,6 @@ public partial class Application [Column("privacy_policy_url", TypeName = "character varying")] public string? PrivacyPolicyUrl { get; set; } - [Column("guild_id", TypeName = "character varying")] - public string? GuildId { get; set; } - - [Column("custom_install_url", TypeName = "character varying")] - public string? CustomInstallUrl { get; set; } - [Column("owner_id", TypeName = "character varying")] public string? OwnerId { get; set; } @@ -101,6 +95,12 @@ public partial class Application [Column("team_id", TypeName = "character varying")] public string? TeamId { get; set; } + [Column("guild_id", TypeName = "character varying")] + public string? GuildId { get; set; } + + [Column("custom_install_url", TypeName = "character varying")] + public string? CustomInstallUrl { get; set; } + [ForeignKey("BotUserId")] [InverseProperty("ApplicationBotUser")] public virtual User? BotUser { get; set; } diff --git a/extra/admin-api/Spacebar.Db/Models/ApplicationCommand.cs b/extra/admin-api/Spacebar.Db/Models/ApplicationCommand.cs new file mode 100644
index 00000000..96c6d2ae --- /dev/null +++ b/extra/admin-api/Spacebar.Db/Models/ApplicationCommand.cs
@@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Spacebar.Db.Models; + +[Table("application_commands")] +public partial class ApplicationCommand +{ + [Key] + [Column("id", TypeName = "character varying")] + public string Id { get; set; } = null!; + + [Column("type")] + public int Type { get; set; } + + [Column("application_id", TypeName = "character varying")] + public string ApplicationId { get; set; } = null!; + + [Column("guild_id", TypeName = "character varying")] + public string? GuildId { get; set; } + + [Column("name", TypeName = "character varying")] + public string Name { get; set; } = null!; + + [Column("name_localizations")] + public string? NameLocalizations { get; set; } + + [Column("description", TypeName = "character varying")] + public string Description { get; set; } = null!; + + [Column("description_localizations")] + public string? DescriptionLocalizations { get; set; } + + [Column("options")] + public string Options { get; set; } = null!; + + [Column("default_member_permissions", TypeName = "character varying")] + public string? DefaultMemberPermissions { get; set; } + + [Column("dm_permission")] + public bool DmPermission { get; set; } + + [Column("permissions")] + public string? Permissions { get; set; } + + [Column("nsfw")] + public bool Nsfw { get; set; } + + [Column("integration_types")] + public string? IntegrationTypes { get; set; } + + [Column("global_popularity_rank")] + public int GlobalPopularityRank { get; set; } + + [Column("contexts")] + public string? Contexts { get; set; } + + [Column("version", TypeName = "character varying")] + public string Version { get; set; } = null!; + + [Column("handler")] + public int Handler { get; set; } +} diff --git a/extra/admin-api/Spacebar.Db/Models/Ban.cs b/extra/admin-api/Spacebar.Db/Models/Ban.cs
index 1b6665d9..bea49842 100644 --- a/extra/admin-api/Spacebar.Db/Models/Ban.cs +++ b/extra/admin-api/Spacebar.Db/Models/Ban.cs
@@ -23,7 +23,7 @@ public partial class Ban public string? ExecutorId { get; set; } [Column("ip", TypeName = "character varying")] - public string Ip { get; set; } = null!; + public string? Ip { get; set; } [Column("reason", TypeName = "character varying")] public string? Reason { get; set; } diff --git a/extra/admin-api/Spacebar.Db/Models/InstanceBan.cs b/extra/admin-api/Spacebar.Db/Models/InstanceBan.cs new file mode 100644
index 00000000..92d510fb --- /dev/null +++ b/extra/admin-api/Spacebar.Db/Models/InstanceBan.cs
@@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Spacebar.Db.Models; + +[Table("instance_bans")] +[Index("OriginInstanceBanId", Name = "REL_0b02d18d0d830f160c921192a3", IsUnique = true)] +public partial class InstanceBan +{ + [Key] + [Column("id", TypeName = "character varying")] + public string Id { get; set; } = null!; + + [Column("created_at", TypeName = "timestamp without time zone")] + public DateTime CreatedAt { get; set; } + + [Column("reason", TypeName = "character varying")] + public string Reason { get; set; } = null!; + + [Column("user_id", TypeName = "character varying")] + public string? UserId { get; set; } + + [Column("fingerprint", TypeName = "character varying")] + public string? Fingerprint { get; set; } + + [Column("ip_address", TypeName = "character varying")] + public string? IpAddress { get; set; } + + [Column("is_from_other_instance_ban")] + public bool IsFromOtherInstanceBan { get; set; } + + [Column("origin_instance_ban_id", TypeName = "character varying")] + public string? OriginInstanceBanId { get; set; } + + [Column("is_allowlisted")] + public bool IsAllowlisted { get; set; } + + [InverseProperty("OriginInstanceBan")] + public virtual InstanceBan? InverseOriginInstanceBan { get; set; } + + [ForeignKey("OriginInstanceBanId")] + [InverseProperty("InverseOriginInstanceBan")] + public virtual InstanceBan? OriginInstanceBan { get; set; } +} diff --git a/extra/admin-api/Spacebar.Db/Models/Message.cs b/extra/admin-api/Spacebar.Db/Models/Message.cs
index 35de4819..11cee738 100644 --- a/extra/admin-api/Spacebar.Db/Models/Message.cs +++ b/extra/admin-api/Spacebar.Db/Models/Message.cs
@@ -58,18 +58,12 @@ public partial class Message [Column("nonce")] public string? Nonce { get; set; } - [Column("pinned_at", TypeName = "timestamp without time zone")] - public DateTime? PinnedAt { get; set; } - [Column("type")] public int Type { get; set; } [Column("activity")] public string? Activity { get; set; } - [Column("flags")] - public int Flags { get; set; } - [Column("message_reference")] public string? MessageReference { get; set; } @@ -79,6 +73,12 @@ public partial class Message [Column("components")] public string? Components { get; set; } + [Column("message_reference_id", TypeName = "character varying")] + public string? MessageReferenceId { get; set; } + + [Column("flags")] + public int Flags { get; set; } + [Column("poll")] public string? Poll { get; set; } @@ -88,8 +88,11 @@ public partial class Message [Column("avatar", TypeName = "character varying")] public string? Avatar { get; set; } - [Column("message_reference_id", TypeName = "character varying")] - public string? MessageReferenceId { get; set; } + [Column("pinned_at", TypeName = "timestamp without time zone")] + public DateTime? PinnedAt { get; set; } + + [Column("interaction_metadata")] + public string? InteractionMetadata { get; set; } [ForeignKey("ApplicationId")] [InverseProperty("Messages")] diff --git a/extra/admin-api/Spacebar.Db/Models/Session.cs b/extra/admin-api/Spacebar.Db/Models/Session.cs
index b27ad1f3..f7eda3cf 100644 --- a/extra/admin-api/Spacebar.Db/Models/Session.cs +++ b/extra/admin-api/Spacebar.Db/Models/Session.cs
@@ -20,17 +20,17 @@ public partial class Session public string SessionId { get; set; } = null!; [Column("activities")] - public string? Activities { get; set; } + public string Activities { get; set; } = null!; [Column("client_info")] public string ClientInfo { get; set; } = null!; - [Column("client_status")] - public string ClientStatus { get; set; } = null!; - [Column("status", TypeName = "character varying")] public string Status { get; set; } = null!; + [Column("client_status")] + public string ClientStatus { get; set; } = null!; + [ForeignKey("UserId")] [InverseProperty("Sessions")] public virtual User? User { get; set; } diff --git a/extra/admin-api/Spacebar.Db/Models/TeamMember.cs b/extra/admin-api/Spacebar.Db/Models/TeamMember.cs
index ec1cf646..0d5c453c 100644 --- a/extra/admin-api/Spacebar.Db/Models/TeamMember.cs +++ b/extra/admin-api/Spacebar.Db/Models/TeamMember.cs
@@ -19,15 +19,15 @@ public partial class TeamMember [Column("permissions")] public string Permissions { get; set; } = null!; - [Column("role", TypeName = "character varying")] - public string Role { get; set; } = null!; - [Column("team_id", TypeName = "character varying")] public string? TeamId { get; set; } [Column("user_id", TypeName = "character varying")] public string? UserId { get; set; } + [Column("role", TypeName = "character varying")] + public string Role { get; set; } = null!; + [ForeignKey("TeamId")] [InverseProperty("TeamMembers")] public virtual Team? Team { get; set; } diff --git a/extra/admin-api/Spacebar.Db/Models/User.cs b/extra/admin-api/Spacebar.Db/Models/User.cs
index b16d3bae..323846d4 100644 --- a/extra/admin-api/Spacebar.Db/Models/User.cs +++ b/extra/admin-api/Spacebar.Db/Models/User.cs
@@ -65,9 +65,6 @@ public partial class User [Column("mfa_enabled")] public bool MfaEnabled { get; set; } - [Column("webauthn_enabled")] - public bool WebauthnEnabled { get; set; } - [Column("totp_secret", TypeName = "character varying")] public string? TotpSecret { get; set; } @@ -92,14 +89,14 @@ public partial class User [Column("email", TypeName = "character varying")] public string? Email { get; set; } - [Column("flags", TypeName = "character varying")] - public string Flags { get; set; } + [Column("flags")] + public ulong Flags { get; set; } [Column("public_flags")] public ulong PublicFlags { get; set; } [Column("purchased_flags")] - public int PurchasedFlags { get; set; } + public ulong PurchasedFlags { get; set; } [Column("premium_usage_flags")] public int PremiumUsageFlags { get; set; } @@ -116,12 +113,15 @@ public partial class User [Column("extended_settings")] public string ExtendedSettings { get; set; } = null!; - [Column("badge_ids")] - public string? BadgeIds { get; set; } - [Column("settingsIndex")] public int? SettingsIndex { get; set; } + [Column("webauthn_enabled")] + public bool WebauthnEnabled { get; set; } + + [Column("badge_ids")] + public string? BadgeIds { get; set; } + [InverseProperty("BotUser")] public virtual Application? ApplicationBotUser { get; set; } diff --git a/extra/admin-api/Spacebar.Db/Models/UserSetting.cs b/extra/admin-api/Spacebar.Db/Models/UserSetting.cs
index c742b092..5bd3354f 100644 --- a/extra/admin-api/Spacebar.Db/Models/UserSetting.cs +++ b/extra/admin-api/Spacebar.Db/Models/UserSetting.cs
@@ -52,9 +52,6 @@ public partial class UserSetting [Column("explicit_content_filter")] public int? ExplicitContentFilter { get; set; } - [Column("friend_discovery_flags")] - public int? FriendDiscoveryFlags { get; set; } - [Column("friend_source_flags")] public string? FriendSourceFlags { get; set; } @@ -109,6 +106,9 @@ public partial class UserSetting [Column("timezone_offset")] public int? TimezoneOffset { get; set; } + [Column("friend_discovery_flags")] + public int? FriendDiscoveryFlags { get; set; } + [Column("view_nsfw_guilds")] public bool? ViewNsfwGuilds { get; set; } diff --git a/extra/admin-api/Spacebar.Db/Spacebar.Db.csproj b/extra/admin-api/Spacebar.Db/Spacebar.Db.csproj
index 350753bf..2d1dcaf5 100644 --- a/extra/admin-api/Spacebar.Db/Spacebar.Db.csproj +++ b/extra/admin-api/Spacebar.Db/Spacebar.Db.csproj
@@ -7,11 +7,8 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="*" /> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="*" /> </ItemGroup> </Project> diff --git a/extra/admin-api/Spacebar.Db/deps.json b/extra/admin-api/Spacebar.Db/deps.json new file mode 100644
index 00000000..c978dd1b --- /dev/null +++ b/extra/admin-api/Spacebar.Db/deps.json
@@ -0,0 +1,362 @@ +[ + { + "pname": "Humanizer.Core", + "version": "2.14.1", + "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" + }, + { + "pname": "Microsoft.Build", + "version": "17.7.2", + "hash": "sha256-k35nFdPxC8t0zAltVSmAJtsepp/ubNIjPOsJ6k8jSqM=" + }, + { + "pname": "Microsoft.Build.Framework", + "version": "17.14.28", + "hash": "sha256-7RzEyIipumafwLW1xN1q23114NafG6PT0+RADElNsiM=" + }, + { + "pname": "Microsoft.Build.Framework", + "version": "17.7.2", + "hash": "sha256-fNWmVQYFTJDveAGmxEdNqJRAczV6+Ep8RA8clKBJFqw=" + }, + { + "pname": "Microsoft.Build.Tasks.Core", + "version": "17.14.28", + "hash": "sha256-M9zRXYijH2HtLlRXbrUK1a1LQ9zkT+DC9ZmMiiVZwv0=" + }, + { + "pname": "Microsoft.Build.Tasks.Core", + "version": "17.7.2", + "hash": "sha256-OrV/qWgZHzGlNUmaSfX5wDBcmg1aQeF3/OUHpSH+uZU=" + }, + { + "pname": "Microsoft.Build.Utilities.Core", + "version": "17.14.28", + "hash": "sha256-VFfO+UpyTpw2X/qiCCOCYzvMLuu7B+XVSSpJZQLkPzU=" + }, + { + "pname": "Microsoft.Build.Utilities.Core", + "version": "17.7.2", + "hash": "sha256-oatF0KfuP1nb4+OLNKg2/R/ZLO4EiACaO5leaxMEY4A=" + }, + { + "pname": "Microsoft.CodeAnalysis.Analyzers", + "version": "3.11.0", + "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" + }, + { + "pname": "Microsoft.CodeAnalysis.Common", + "version": "4.14.0", + "hash": "sha256-ne/zxH3GqoGB4OemnE8oJElG5mai+/67ASaKqwmL2BE=" + }, + { + "pname": "Microsoft.CodeAnalysis.CSharp", + "version": "4.14.0", + "hash": "sha256-5Mzj3XkYYLkwDWh17r1NEXSbXwwWYQPiOmkSMlgo1JY=" + }, + { + "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", + "version": "4.14.0", + "hash": "sha256-aNbV1a0yYBs0fpQawG6LXcbyoE8en+YFSpV5vcYE4J4=" + }, + { + "pname": "Microsoft.CodeAnalysis.Workspaces.Common", + "version": "4.14.0", + "hash": "sha256-0YfeaJe01WBUm9avy4a8FacQJXA1NkpnDpiXu4yz88I=" + }, + { + "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", + "version": "4.14.0", + "hash": "sha256-5SJfpRqzqCK0UbkmAaJpA/r1XJb0YAriMMeQHYC4d+o=" + }, + { + "pname": "Microsoft.EntityFrameworkCore", + "version": "10.0.0", + "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" + }, + { + "pname": "Microsoft.EntityFrameworkCore", + "version": "10.0.1", + "hash": "sha256-IxA+/nDA6hZkUm9bDT6bRu8+9qeHA3gbTvIhR9Ncg4w=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Abstractions", + "version": "10.0.0", + "hash": "sha256-UDgZbRQcGPaKsE53EH6bvJiv+Q4KSxAbnsVhTVFGG4Q=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Abstractions", + "version": "10.0.1", + "hash": "sha256-FMGXhAAgcVSedV0/GmUVqAwoiRzFJil9mQYr6eNgowg=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Analyzers", + "version": "10.0.0", + "hash": "sha256-7Q0jYJO50cqGI+u6gLpootbB8GZvgsgtg0F9FZI1jig=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Analyzers", + "version": "10.0.1", + "hash": "sha256-O949vr98WoSRXtiIo0ZI6dz2cibIBKbiuC+mQOe9bV4=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Design", + "version": "10.0.1", + "hash": "sha256-GGNZIGNEMhSGaMRFkRN4bOuCUBs5YVnX8klXarm319U=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Relational", + "version": "10.0.0", + "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Relational", + "version": "10.0.1", + "hash": "sha256-zLgxr/iW9HP8Fip1IDgr7X0Ar8OWKDvVmoEt65gG6VY=" + }, + { + "pname": "Microsoft.Extensions.Caching.Abstractions", + "version": "10.0.1", + "hash": "sha256-qVLAEqxPK/dNq+z1a6D9NqdcSg/18sfzZhlBWMkqV/A=" + }, + { + "pname": "Microsoft.Extensions.Caching.Memory", + "version": "10.0.0", + "hash": "sha256-AMgDSm1k6q0s17spGtyR5q8nAqUFDOxl/Fe38f9M+d4=" + }, + { + "pname": "Microsoft.Extensions.Caching.Memory", + "version": "10.0.1", + "hash": "sha256-Qb7xK6VEZDas0lJFaW1suKdFjtkSYwLHHxkQEfWIU2A=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.1", + "hash": "sha256-s4PDp+vtzdxKIxnOT3+dDRoTDopyl8kqmmw4KDnkOtQ=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "10.0.1", + "hash": "sha256-RKOB+zPrtQNUbJY/1jR54rKOM8KHPgynPExxugku3I8=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "9.0.0", + "hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.1", + "hash": "sha256-zNUpau51ds7iQTaSUTFtyTHIUoinYc129W50CnufMdQ=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "9.0.0", + "hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c=" + }, + { + "pname": "Microsoft.Extensions.DependencyModel", + "version": "10.0.1", + "hash": "sha256-XIj2jEURe25YA4RhBSuCqQpic0YP+TZaO/dbBPCjad8=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "10.0.0", + "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "10.0.1", + "hash": "sha256-zuLP3SIpCToMOlIPOEv3Kq8y/minecd8k8GSkxFo13E=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "9.0.0", + "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "10.0.0", + "hash": "sha256-BnhgGZc01HwTSxogavq7Ueq4V7iMA3wPnbfRwQ4RhGk=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "10.0.1", + "hash": "sha256-NRk0feNE1fgi/hyO0AVDbSGJQRT+9yte6Lpm4Hz/2Bs=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "9.0.0", + "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "10.0.1", + "hash": "sha256-vBiSS1vqAC7eDrpJNT4H3A9qLikCSAepnNRbry0mKnk=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "9.0.0", + "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "10.0.1", + "hash": "sha256-EXmukq09erT4s+miQpBSYy3IY4HxxKlwEPL43/KoyEc=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "9.0.0", + "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" + }, + { + "pname": "Microsoft.NET.StringTools", + "version": "17.14.28", + "hash": "sha256-UzREyvDxkiOQ4cEOQ5UCjkwXGrldIDCcbefECTPGjXI=" + }, + { + "pname": "Microsoft.NET.StringTools", + "version": "17.7.2", + "hash": "sha256-hQE07TCgcQuyu9ZHVq2gPDb0+xe8ECJUdrgh17bJP4o=" + }, + { + "pname": "Mono.TextTemplating", + "version": "3.0.0", + "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" + }, + { + "pname": "Newtonsoft.Json", + "version": "13.0.3", + "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + }, + { + "pname": "Npgsql", + "version": "10.0.0", + "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + }, + { + "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", + "version": "10.0.0", + "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" + }, + { + "pname": "System.CodeDom", + "version": "6.0.0", + "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" + }, + { + "pname": "System.CodeDom", + "version": "7.0.0", + "hash": "sha256-7IPt39cY+0j0ZcRr/J45xPtEjnSXdUJ/5ai3ebaYQiE=" + }, + { + "pname": "System.CodeDom", + "version": "9.0.0", + "hash": "sha256-578lcBgswW0eM16r0EnJzfGodPx86RxxFoZHc2PSzsw=" + }, + { + "pname": "System.Composition", + "version": "9.0.0", + "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" + }, + { + "pname": "System.Composition.AttributedModel", + "version": "9.0.0", + "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" + }, + { + "pname": "System.Composition.Convention", + "version": "9.0.0", + "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" + }, + { + "pname": "System.Composition.Hosting", + "version": "9.0.0", + "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" + }, + { + "pname": "System.Composition.Runtime", + "version": "9.0.0", + "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" + }, + { + "pname": "System.Composition.TypedParts", + "version": "9.0.0", + "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + }, + { + "pname": "System.Configuration.ConfigurationManager", + "version": "7.0.0", + "hash": "sha256-SgBexTTjRn23uuXvkzO0mz0qOfA23MiS4Wv+qepMLZE=" + }, + { + "pname": "System.Configuration.ConfigurationManager", + "version": "9.0.0", + "hash": "sha256-+pLnTC0YDP6Kjw5DVBiFrV/Q3x5is/+6N6vAtjvhVWk=" + }, + { + "pname": "System.Diagnostics.EventLog", + "version": "9.0.0", + "hash": "sha256-tPvt6yoAp56sK/fe+/ei8M65eavY2UUhRnbrREj/Ems=" + }, + { + "pname": "System.Formats.Nrbf", + "version": "9.0.0", + "hash": "sha256-c4qf6CocQUZB0ySGQd8s15PXY7xfrjQqMGXxkwytKyw=" + }, + { + "pname": "System.Reflection.MetadataLoadContext", + "version": "7.0.0", + "hash": "sha256-VYl6SFD130K9Aw4eJH16ApJ9Sau4Xu0dcxEip2veuTI=" + }, + { + "pname": "System.Resources.Extensions", + "version": "9.0.0", + "hash": "sha256-y2gLEMuAy6QfEyNJxABC/ayMWGnwlpX735jsUQLktho=" + }, + { + "pname": "System.Security.Cryptography.Pkcs", + "version": "7.0.0", + "hash": "sha256-3J3vL9hcKSuZjT2GKappa2A9p2xJm1nH2asTNAl8ZCA=" + }, + { + "pname": "System.Security.Cryptography.Pkcs", + "version": "7.0.2", + "hash": "sha256-qS5Z/Yo8J+f3ExVX5Qkcpj1Z57oUZqz5rWa1h5bVpl8=" + }, + { + "pname": "System.Security.Cryptography.Pkcs", + "version": "9.0.0", + "hash": "sha256-AjG14mGeSc2Ka4QSelGBM1LrGBW3VJX60lnihKyJjGY=" + }, + { + "pname": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0", + "hash": "sha256-gPgPU7k/InTqmXoRzQfUMEKL3QuTnOKowFqmXTnWaBQ=" + }, + { + "pname": "System.Security.Cryptography.Xml", + "version": "7.0.1", + "hash": "sha256-CH8+JVC8LyCSW75/6ZQ7ecMbSOAE1c16z4dG8JTp01w=" + }, + { + "pname": "System.Security.Cryptography.Xml", + "version": "9.0.0", + "hash": "sha256-SQJWwAFrJUddEU6JiZB52FM9tGjRlJAYH8oYVzG5IJU=" + }, + { + "pname": "System.Security.Permissions", + "version": "7.0.0", + "hash": "sha256-DOFoX+AKRmrkllykHheR8FfUXYx/Ph+I/HYuReQydXI=" + }, + { + "pname": "System.Security.Permissions", + "version": "9.0.0", + "hash": "sha256-BFrA9ottmQtLIAiKiGRbfSUpzNJwuaOCeFRDN4Z0ku0=" + }, + { + "pname": "System.Windows.Extensions", + "version": "9.0.0", + "hash": "sha256-RErD+Ju15qtnwdwB7E0SjjJGAnhXwJyC7UPcl24Z3Vs=" + } +] diff --git a/extra/admin-api/SpacebarAdminAPI.sln b/extra/admin-api/SpacebarAdminAPI.sln
index 09e3a696..723939d0 100644 --- a/extra/admin-api/SpacebarAdminAPI.sln +++ b/extra/admin-api/SpacebarAdminAPI.sln
@@ -5,15 +5,15 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.Db", "Spacebar.Db\Spacebar.Db.csproj", "{524849DF-93BC-4632-B6C2-D05552C13887}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.AdminAPI", "Spacebar.AdminAPI\Spacebar.AdminAPI.csproj", "{00E58C53-0AC1-4113-8CCF-D299861EA8D3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.AdminApi", "Spacebar.AdminApi\Spacebar.AdminApi.csproj", "{00E58C53-0AC1-4113-8CCF-D299861EA8D3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{04787943-EBB6-4DE4-96D5-4CFB4A2CEE99}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.RabbitMqUtilities", "Utilities\Spacebar.RabbitMqUtilities\Spacebar.RabbitMqUtilities.csproj", "{F4E179D1-A3EB-4A1D-9C11-E7019F4B1EE2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.AdminAPITest", "Utilities\Spacebar.AdminAPITest\Spacebar.AdminAPITest.csproj", "{374314B2-7149-4316-8750-4E1E7BF6C3B4}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.AdminApiTest", "Utilities\Spacebar.AdminApiTest\Spacebar.AdminApiTest.csproj", "{374314B2-7149-4316-8750-4E1E7BF6C3B4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.AdminAPI.TestClient", "Utilities\Spacebar.AdminAPI.TestClient\Spacebar.AdminAPI.TestClient.csproj", "{498DAD05-336E-4851-ABD8-4E7CCA8312B0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.AdminApi.TestClient", "Utilities\Spacebar.AdminApi.TestClient\Spacebar.AdminApi.TestClient.csproj", "{498DAD05-336E-4851-ABD8-4E7CCA8312B0}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.AdminApi.Models", "Spacebar.AdminApi.Models\Spacebar.AdminApi.Models.csproj", "{5F138C70-EAFA-4C7C-8B90-EFB9624B235C}" EndProject @@ -21,6 +21,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.AdminApi.PrepareTe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.CleanSettingsRows", "Spacebar.CleanSettingsRows\Spacebar.CleanSettingsRows.csproj", "{9B41FAC1-4427-487C-BF3F-69554848DEBF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spacebar.ConfigModel", "Spacebar.ConfigModel\Spacebar.ConfigModel.csproj", "{EC5C6A71-458A-4A1D-BE96-CCB84FF4AF1B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigTest", "ConfigTest\ConfigTest.csproj", "{3BB51E17-8D3E-42FE-AAE6-C33DA5D8D323}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -62,6 +66,14 @@ Global {9B41FAC1-4427-487C-BF3F-69554848DEBF}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B41FAC1-4427-487C-BF3F-69554848DEBF}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B41FAC1-4427-487C-BF3F-69554848DEBF}.Release|Any CPU.Build.0 = Release|Any CPU + {EC5C6A71-458A-4A1D-BE96-CCB84FF4AF1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC5C6A71-458A-4A1D-BE96-CCB84FF4AF1B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC5C6A71-458A-4A1D-BE96-CCB84FF4AF1B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC5C6A71-458A-4A1D-BE96-CCB84FF4AF1B}.Release|Any CPU.Build.0 = Release|Any CPU + {3BB51E17-8D3E-42FE-AAE6-C33DA5D8D323}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BB51E17-8D3E-42FE-AAE6-C33DA5D8D323}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BB51E17-8D3E-42FE-AAE6-C33DA5D8D323}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BB51E17-8D3E-42FE-AAE6-C33DA5D8D323}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {F4E179D1-A3EB-4A1D-9C11-E7019F4B1EE2} = {04787943-EBB6-4DE4-96D5-4CFB4A2CEE99} diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/App.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/App.razor
index c7730d1c..c7730d1c 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/App.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/App.razor
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Classes/OpenAPI/OpenAPISchema.cs b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Classes/OpenAPI/OpenAPISchema.cs
index 8e399302..8995dee5 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Classes/OpenAPI/OpenAPISchema.cs +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Classes/OpenAPI/OpenAPISchema.cs
@@ -2,7 +2,7 @@ using System.Collections.Frozen; using System.Text.Json; using System.Text.Json.Serialization; -namespace Spacebar.AdminAPI.TestClient.Classes.OpenAPI; +namespace Spacebar.AdminApi.TestClient.Classes.OpenAPI; public class OpenApiSchema { [JsonPropertyName("openapi")] diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/MainLayout.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/MainLayout.razor
index e7554be8..e7554be8 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/MainLayout.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/MainLayout.razor
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/MainLayout.razor.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/MainLayout.razor.css
index c538ac3b..c538ac3b 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/MainLayout.razor.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/MainLayout.razor.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/NavMenu.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/NavMenu.razor
index c96fa30c..96deb070 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/NavMenu.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/NavMenu.razor
@@ -1,6 +1,6 @@ <div class="top-row ps-3 navbar navbar-dark"> <div class="container-fluid"> - <a class="navbar-brand" href="">Spacebar.AdminAPI.TestClient</a> + <a class="navbar-brand" href="">Spacebar.AdminApi.TestClient</a> <button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu"> <span class="navbar-toggler-icon"></span> </button> diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/NavMenu.razor.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/NavMenu.razor.css
index 617b89cc..617b89cc 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Layout/NavMenu.razor.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Layout/NavMenu.razor.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Guilds.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Guilds.razor
index 454808b4..9a376d24 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Guilds.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Guilds.razor
@@ -2,7 +2,7 @@ @using System.Net.Http.Headers @using System.Reflection @using Spacebar.AdminApi.Models -@using Spacebar.AdminAPI.TestClient.Services +@using Spacebar.AdminApi.TestClient.Services @using ArcaneLibs.Blazor.Components @using ArcaneLibs.Extensions @inject Config Config diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Home.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Home.razor
index 812a6152..275ca146 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Home.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Home.razor
@@ -1,6 +1,6 @@ @page "/" @using System.Net.Http.Headers -@using Spacebar.AdminAPI.TestClient.Services +@using Spacebar.AdminApi.TestClient.Services @inject Config Config @inject ILocalStorageService LocalStorage diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/HttpTestClient.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/HttpTestClient.razor
index 0be77a99..b70f4efb 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/HttpTestClient.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/HttpTestClient.razor
@@ -1,11 +1,10 @@ @page "/HttpTestClient" -@using System.Collections.Immutable @using System.Text.Json @using ArcaneLibs.Blazor.Components @using ArcaneLibs.Extensions -@using Spacebar.AdminAPI.TestClient.Classes.OpenAPI -@using Spacebar.AdminAPI.TestClient.Pages.HttpTestClientParts -@using Spacebar.AdminAPI.TestClient.Services +@using Spacebar.AdminApi.TestClient.Classes.OpenAPI +@using Spacebar.AdminApi.TestClient.Pages.HttpTestClientParts +@using Spacebar.AdminApi.TestClient.Services @inject Config Config <h3>HttpTestClient</h3> diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/HttpTestClientParts/OpenAPIParameterDescription.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/HttpTestClientParts/OpenAPIParameterDescription.razor
index 89222453..8f0667d0 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/HttpTestClientParts/OpenAPIParameterDescription.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/HttpTestClientParts/OpenAPIParameterDescription.razor
@@ -1,5 +1,5 @@ @using ArcaneLibs.Extensions -@using Spacebar.AdminAPI.TestClient.Classes.OpenAPI +@using Spacebar.AdminApi.TestClient.Classes.OpenAPI <span title="@Parameter.ToJson()">@Summary</span> @if (Parameter.Name != Parameter.Description && !string.IsNullOrWhiteSpace(Parameter.Description)) { <i> - @Parameter.Description</i> diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Login.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Login.razor
index ca1205ed..196d7336 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Login.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Login.razor
@@ -1,6 +1,6 @@ @page "/Login" @using System.Text.Json.Nodes -@using Spacebar.AdminAPI.TestClient.Services +@using Spacebar.AdminApi.TestClient.Services @inject ILocalStorageService LocalStorage @inject Config Config @inject NavigationManager Navigation diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Media/Index.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Media/Index.razor
index 76621e74..76621e74 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Media/Index.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Media/Index.razor
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Media/Users.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Media/Users.razor
index 81008a25..f9b43942 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Media/Users.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Media/Users.razor
@@ -2,7 +2,7 @@ @using System.Net.Http.Headers @using System.Reflection @using Spacebar.AdminApi.Models -@using Spacebar.AdminAPI.TestClient.Services +@using Spacebar.AdminApi.TestClient.Services @using ArcaneLibs.Blazor.Components @inject Config Config @inject ILocalStorageService LocalStorage diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Users.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Users.razor
index c0e67867..b12a5c16 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/Users.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/Users.razor
@@ -2,7 +2,7 @@ @using System.Net.Http.Headers @using System.Reflection @using Spacebar.AdminApi.Models -@using Spacebar.AdminAPI.TestClient.Services +@using Spacebar.AdminApi.TestClient.Services @using ArcaneLibs.Blazor.Components @using ArcaneLibs.Extensions @inject Config Config diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/UsersDelete.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/UsersDelete.razor
index 98a3e0fc..9d527621 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Pages/UsersDelete.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/UsersDelete.razor
@@ -4,7 +4,7 @@ @using System.Text.Json.Nodes @using ArcaneLibs.Extensions @using Spacebar.AdminApi.Models -@using Spacebar.AdminAPI.TestClient.Services +@using Spacebar.AdminApi.TestClient.Services @inject Config Config <h3>UsersDelete - @Id</h3> diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Program.cs b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Program.cs
index 0e3035a2..d28066da 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Program.cs +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Program.cs
@@ -5,8 +5,8 @@ using System.Text.Json.Serialization; using Blazored.LocalStorage; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Spacebar.AdminAPI.TestClient; -using Spacebar.AdminAPI.TestClient.Services; +using Spacebar.AdminApi.TestClient; +using Spacebar.AdminApi.TestClient.Services; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Properties/launchSettings.json b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Properties/launchSettings.json
index c346464e..c346464e 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Properties/launchSettings.json +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Properties/launchSettings.json
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Services/Config.cs b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Services/Config.cs
index 08fc1fb0..3b045a49 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Services/Config.cs +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Services/Config.cs
@@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Spacebar.AdminAPI.TestClient.Services; +namespace Spacebar.AdminApi.TestClient.Services; public class Config { [JsonPropertyName("api_url")] diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Services/StreamingHttpClient.cs b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Services/StreamingHttpClient.cs
index 67dc673e..0f2ade71 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Services/StreamingHttpClient.cs +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Services/StreamingHttpClient.cs
@@ -11,7 +11,7 @@ using System.Text.Json.Serialization; using ArcaneLibs; using ArcaneLibs.Extensions; -namespace Spacebar.AdminAPI.TestClient.Services; +namespace Spacebar.AdminApi.TestClient.Services; #if SINGLE_HTTPCLIENT // TODO: Add URI wrapper for @@ -83,7 +83,7 @@ public class StreamingHttpClient { request.RequestUri = new Uri(BaseAddress ?? throw new InvalidOperationException("Relative URI passed, but no BaseAddress is specified!"), request.RequestUri); swWait.Stop(); var swExec = Stopwatch.StartNew(); - + foreach (var (key, value) in AdditionalQueryParameters) request.RequestUri = request.RequestUri.AddQuery(key, value); foreach (var (key, value) in DefaultRequestHeaders) { if (request.Headers.Contains(key)) continue; @@ -110,6 +110,7 @@ public class StreamingHttpClient { else if (!e.ToString().StartsWith("TypeError: NetworkError")) Console.WriteLine( $"Failed to send request {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)}):\n{e}"); + throw; } #if SYNC_HTTPCLIENT @@ -148,19 +149,19 @@ public class StreamingHttpClient { //retry on gateway timeout // if (responseMessage.StatusCode == HttpStatusCode.GatewayTimeout) { - // request.ResetSendStatus(); - // return await SendAsync(request, cancellationToken); + // request.ResetSendStatus(); + // return await SendAsync(request, cancellationToken); // } //error handling var content = await responseMessage.Content.ReadAsStringAsync(cancellationToken); if (content.Length == 0) throw new DataException("Content was empty"); - // throw new MatrixException() { - // ErrorCode = "M_UNKNOWN", - // Error = "Unknown error, server returned no content" - // }; - + // throw new MatrixException() { + // ErrorCode = "M_UNKNOWN", + // Error = "Unknown error, server returned no content" + // }; + // if (!content.StartsWith('{')) throw new InvalidDataException("Encountered invalid data:\n" + content); if (!content.TrimStart().StartsWith('{')) { responseMessage.EnsureSuccessStatusCode(); @@ -293,4 +294,4 @@ public class StreamingHttpClient { return await SendAsync(request); } } -#endif +#endif \ No newline at end of file diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Spacebar.AdminAPI.TestClient.csproj b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Spacebar.AdminApi.TestClient.csproj
index fcbd9d61..bc29a828 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/Spacebar.AdminAPI.TestClient.csproj +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Spacebar.AdminApi.TestClient.csproj
@@ -14,6 +14,7 @@ <!-- <RunAOTCompilation>true</RunAOTCompilation>--> <BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport> <InvariantGlobalization>true</InvariantGlobalization> +<!-- <RuntimeFrameworkVersion>10.0.2-servicing.25570.103</RuntimeFrameworkVersion>--> </PropertyGroup> <ItemGroup> @@ -24,8 +25,7 @@ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="10.0.0" /> </ItemGroup> - - + <ItemGroup> <Content Update="appsettings.Development.json"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/_Imports.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/_Imports.razor
index 6cfe3191..17be595b 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/_Imports.razor +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/_Imports.razor
@@ -7,5 +7,5 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.JSInterop -@using Spacebar.AdminAPI.TestClient -@using Spacebar.AdminAPI.TestClient.Layout \ No newline at end of file +@using Spacebar.AdminApi.TestClient +@using Spacebar.AdminApi.TestClient.Layout \ No newline at end of file diff --git a/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/deps.json b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/deps.json new file mode 100644
index 00000000..c48eef78 --- /dev/null +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/deps.json
@@ -0,0 +1,217 @@ +[ + { + "pname": "ArcaneLibs", + "version": "1.0.0-preview.20251005-232225", + "hash": "sha256-EsYLSiyX5Nj+ZpFb6FOcAYqDsQFSbvgm9NKaarJjK/0=" + }, + { + "pname": "ArcaneLibs.Blazor.Components", + "version": "1.0.0-preview.20251005-232225", + "hash": "sha256-9rgq/bFNwZf+mpTldzhr7VPwlseLR21nDlJWGgIaGIQ=" + }, + { + "pname": "Blazored.LocalStorage", + "version": "4.5.0", + "hash": "sha256-0vklTFHEGgNG8V6ivQictuooyiXS2nMn/qLpfYhEBlE=" + }, + { + "pname": "Microsoft.AspNetCore.App.Internal.Assets", + "version": "10.0.0", + "hash": "sha256-IyY5Ymdkmf9S9qRwYXX9rWpzcU3fuDR+ITeaaeJQ/Dk=" + }, + { + "pname": "Microsoft.AspNetCore.Authorization", + "version": "10.0.0", + "hash": "sha256-g1MagKFkZF0LttdK5GLdHCXe4d1qOXv57ngz7XnhRrk=" + }, + { + "pname": "Microsoft.AspNetCore.Components", + "version": "10.0.0", + "hash": "sha256-nrCuCDRbvD5XQyn3ySW/CD4yKYD6coC71JH2ke6xtXI=" + }, + { + "pname": "Microsoft.AspNetCore.Components", + "version": "9.0.9", + "hash": "sha256-1+lIIRfIwHC3XWt2tMuQ3NxoqIsVRgAR+/l9vttQLd8=" + }, + { + "pname": "Microsoft.AspNetCore.Components.Analyzers", + "version": "10.0.0", + "hash": "sha256-2mbSRBB/2eT0fYouhDKM5OFRZGQ0Jv8HgcQLvufoHq4=" + }, + { + "pname": "Microsoft.AspNetCore.Components.Forms", + "version": "10.0.0", + "hash": "sha256-el2T9pvjNexq5lfJhp+7xZYa/1CS6RchIWKmtiKg+vI=" + }, + { + "pname": "Microsoft.AspNetCore.Components.Forms", + "version": "9.0.9", + "hash": "sha256-oUwcqvDLtychAdga+fAJp70wSEdVheYsV6l5Qnfdq6M=" + }, + { + "pname": "Microsoft.AspNetCore.Components.Web", + "version": "10.0.0", + "hash": "sha256-xDf1DBNBceAFKu4er2inn6gUKa1T1L8T3ewlFcuxddE=" + }, + { + "pname": "Microsoft.AspNetCore.Components.Web", + "version": "8.0.0", + "hash": "sha256-dsCb4B6r5iHPbEp8+uFzAfD1txGI5dEIWgwtT9+GgU8=" + }, + { + "pname": "Microsoft.AspNetCore.Components.Web", + "version": "9.0.9", + "hash": "sha256-mgohB7RwOzE5XPB0Lg3h9pUIBKPXaul2F39uxfU7GD8=" + }, + { + "pname": "Microsoft.AspNetCore.Components.WebAssembly", + "version": "10.0.0", + "hash": "sha256-YeHralkfEzLmlrBj0jER+ta9hsbXv5skP81178V1ppw=" + }, + { + "pname": "Microsoft.AspNetCore.Components.WebAssembly.DevServer", + "version": "10.0.0", + "hash": "sha256-akCQI6iHuKIyDmZh5du+hifzPzQoGeMX3Y7ST/TFxRU=" + }, + { + "pname": "Microsoft.AspNetCore.Metadata", + "version": "10.0.0", + "hash": "sha256-TxyiXUx8sWdWFWacBTWFaPeMa3z2+Zmc4VK/Qgq0YRw=" + }, + { + "pname": "Microsoft.DotNet.HotReload.WebAssembly.Browser", + "version": "10.0.100", + "hash": "sha256-ppwVl5tBHFFmMZ0EjpKn4OSBYNExf/S6ojeYdefX1+k=" + }, + { + "pname": "Microsoft.Extensions.Configuration", + "version": "10.0.0", + "hash": "sha256-MsLskVPpkCvov5+DWIaALCt1qfRRX4u228eHxvpE0dg=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.0", + "hash": "sha256-GcgrnTAieCV7AVT13zyOjfwwL86e99iiO/MiMOxPGG0=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Binder", + "version": "10.0.0", + "hash": "sha256-YSiWoA3VQR22k6+bSEAUqeG7UDzZlJfHWDTubUO5V8U=" + }, + { + "pname": "Microsoft.Extensions.Configuration.FileExtensions", + "version": "10.0.0", + "hash": "sha256-rN+3rqrHiTaBfHgP+E4dA8Qm2cFJPfbEcd93yKLsqlQ=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Json", + "version": "10.0.0", + "hash": "sha256-VCFukgsxiQ2MFGE6RDMFTGopBHbcZL2t0ER7ENaFXRY=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "10.0.0", + "hash": "sha256-LYm9hVlo/R9c2aAKHsDYJ5vY9U0+3Jvclme3ou3BtvQ=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection", + "version": "9.0.9", + "hash": "sha256-UHG/uj9hjCRWmz2LZ4wR721ooZYGtBy4TT+lNeVzyrU=" + }, + { + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.0", + "hash": "sha256-9iodXP39YqgxomnOPOxd/mzbG0JfOSXzFoNU3omT2Ps=" + }, + { + "pname": "Microsoft.Extensions.Diagnostics", + "version": "10.0.0", + "hash": "sha256-o7QkCisEcFIh227qBUfWFci2ns4cgEpLqpX7YvHGToQ=" + }, + { + "pname": "Microsoft.Extensions.Diagnostics.Abstractions", + "version": "10.0.0", + "hash": "sha256-cix7QxQ/g3sj6reXu3jn0cRv2RijzceaLLkchEGTt5E=" + }, + { + "pname": "Microsoft.Extensions.FileProviders.Abstractions", + "version": "10.0.0", + "hash": "sha256-CHDs2HCN8QcfuYQpgNVszZ5dfXFe4yS9K2GoQXecc20=" + }, + { + "pname": "Microsoft.Extensions.FileProviders.Physical", + "version": "10.0.0", + "hash": "sha256-2Rw/cwBO+/A3QY2IjN/c8Y0LhtC1qTBL7VdJiD1J2UQ=" + }, + { + "pname": "Microsoft.Extensions.FileSystemGlobbing", + "version": "10.0.0", + "hash": "sha256-ETfVTdsdBtp69EggLg/AARTQW4lLQYVdVldXIQrsjZA=" + }, + { + "pname": "Microsoft.Extensions.Logging", + "version": "10.0.0", + "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" + }, + { + "pname": "Microsoft.Extensions.Logging.Abstractions", + "version": "10.0.0", + "hash": "sha256-BnhgGZc01HwTSxogavq7Ueq4V7iMA3wPnbfRwQ4RhGk=" + }, + { + "pname": "Microsoft.Extensions.Logging.Configuration", + "version": "10.0.0", + "hash": "sha256-7/TWO1aq8hdgbcTEKDBWIjgSC9KpFN3kRnMX+12bOkU=" + }, + { + "pname": "Microsoft.Extensions.Options", + "version": "10.0.0", + "hash": "sha256-j5MOqZSKeUtxxzmZjzZMGy0vELHdvPraqwTQQQNVsYA=" + }, + { + "pname": "Microsoft.Extensions.Options.ConfigurationExtensions", + "version": "10.0.0", + "hash": "sha256-XGAs5DxMvWnmjX8dqRwKY0vsuS40SHvsfJqB1rO4L7k=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "10.0.0", + "hash": "sha256-Dup08KcptLjlnpN5t5//+p4n8FUTgRAq4n/w1s6us+I=" + }, + { + "pname": "Microsoft.Extensions.Primitives", + "version": "9.0.9", + "hash": "sha256-bCd4Bj5uP4kT0hCvs0LZS8IVqEtpOIyhSiay5ijJbBA=" + }, + { + "pname": "Microsoft.Extensions.Validation", + "version": "10.0.0", + "hash": "sha256-BbmNqKlqNd/37IU5X1wjc3VCxfrBTA1bkVqk1myU+H0=" + }, + { + "pname": "Microsoft.JSInterop", + "version": "10.0.0", + "hash": "sha256-/y6la0IzcE5N/thRGLDZYsvNEeWPlW2IwEJtIu3sdaE=" + }, + { + "pname": "Microsoft.JSInterop", + "version": "9.0.9", + "hash": "sha256-fCUkGYS6HKZ0NW1xfuI5aCD2yGqpeIjaWFcS0dBZMWE=" + }, + { + "pname": "Microsoft.JSInterop.WebAssembly", + "version": "10.0.0", + "hash": "sha256-dJdTHFh4lZOmXI+88mLdJOf6UsKq/80N59+COq/KPpg=" + }, + { + "pname": "Microsoft.NET.Sdk.WebAssembly.Pack", + "version": "10.0.0", + "hash": "sha256-AWqn+WUvMjdec4KX4RpTW1ZgT4K+fYhpkrcf706Zt/w=" + }, + { + "pname": "Microsoft.NETCore.App.Runtime.Mono.browser-wasm", + "version": "10.0.0", + "hash": "sha256-1CpAq/TBpFaZncLb+Z3xaq/J7/O8ZTEl3qBidCpQj9M=" + } +] diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/appsettings.json b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/appsettings.json
index 1ca99ed9..1ca99ed9 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/appsettings.json +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/appsettings.json
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/css/app.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/css/app.css
index 7b3eb5d9..7b3eb5d9 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/css/app.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/css/app.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/favicon.png b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/favicon.png
index 8422b596..8422b596 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/favicon.png +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/favicon.png
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/icon-192.png b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/icon-192.png
index 166f56da..166f56da 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/icon-192.png +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/icon-192.png
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/index.html b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/index.html
index 43c7e50b..5f8af7c9 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/index.html +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/index.html
@@ -9,7 +9,7 @@ <link rel="stylesheet" href="lib/jetbrains-mono/jetbrains-mono.css"/> <link rel="stylesheet" href="css/app.css"/> <link rel="icon" type="image/png" href="favicon.png"/> - <link href="Spacebar.AdminAPI.TestClient.styles.css" rel="stylesheet"/> + <link href="Spacebar.AdminApi.TestClient.styles.css" rel="stylesheet"/> </head> <body> diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
index 3882a819..3882a819 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map
index ce99ec19..ce99ec19 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
index 49b843b1..49b843b1 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map
index a0db8b57..a0db8b57 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css
index 1a5d6563..1a5d6563 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map
index 8df43cfc..8df43cfc 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css
index 672cbc2e..672cbc2e 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map
index 1c926af5..1c926af5 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
index 63054109..63054109 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map
index 5fe522b6..5fe522b6 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
index aa803594..aa803594 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map
index e4774804..e4774804 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css
index 2103919f..2103919f 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map
index 31c8d254..31c8d254 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css
index b1da1a5a..b1da1a5a 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map
index 689b5b6b..689b5b6b 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css
index aaa63792..aaa63792 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map
index 5a3e2bae..5a3e2bae 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css
index 50d34056..50d34056 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map
index 41d4357a..41d4357a 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css
index 2d714223..2d714223 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map
index 3e4a515e..3e4a515e 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css
index 7641ef68..7641ef68 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map
index 9fd3e127..9fd3e127 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css
index b7ab57f2..b7ab57f2 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map
index a1f9a189..a1f9a189 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css
index 39934146..39934146 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map
index 90ce7987..90ce7987 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css
index 753823c2..753823c2 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map
index 34a6d2a1..34a6d2a1 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css
index 802b60d0..802b60d0 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map
index a77bdfb0..a77bdfb0 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
index 1876be09..1876be09 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map
index c88b707f..c88b707f 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js
index 04e9185b..04e9185b 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map
index 69af4203..69af4203 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js
index 45745c85..45745c85 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map
index a3a69516..a3a69516 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js
index c573a344..c573a344 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map
index 03527afe..03527afe 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js
index a8f49edd..a8f49edd 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map
index d0ae645b..d0ae645b 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js
index d5dc5ea1..d5dc5ea1 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map
index 901fbef5..901fbef5 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/jetbrains-mono.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/jetbrains-mono.css
index 78aedd2b..78aedd2b 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/jetbrains-mono.css +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/jetbrains-mono.css
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Bold.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Bold.woff2
index 4917f434..4917f434 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Bold.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Bold.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-BoldItalic.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-BoldItalic.woff2
index 536d3f71..536d3f71 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-BoldItalic.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-BoldItalic.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBold.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBold.woff2
index 8f88c546..8f88c546 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBold.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBold.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBoldItalic.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBoldItalic.woff2
index d1478bac..d1478bac 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBoldItalic.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraBoldItalic.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLight.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLight.woff2
index b97239f3..b97239f3 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLight.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLight.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLightItalic.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLightItalic.woff2
index be01aaca..be01aaca 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLightItalic.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ExtraLightItalic.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Italic.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Italic.woff2
index d60c270e..d60c270e 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Italic.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Italic.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Light.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Light.woff2
index 65384987..65384987 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Light.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Light.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-LightItalic.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-LightItalic.woff2
index 66ca3d2b..66ca3d2b 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-LightItalic.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-LightItalic.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Medium.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Medium.woff2
index 669d04cd..669d04cd 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Medium.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Medium.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-MediumItalic.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-MediumItalic.woff2
index 80cfd15e..80cfd15e 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-MediumItalic.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-MediumItalic.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Regular.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Regular.woff2
index 40da4276..40da4276 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Regular.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Regular.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBold.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBold.woff2
index 5ead7b0d..5ead7b0d 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBold.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBold.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBoldItalic.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBoldItalic.woff2
index c5dd294b..c5dd294b 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBoldItalic.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-SemiBoldItalic.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Thin.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Thin.woff2
index 17270e45..17270e45 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Thin.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-Thin.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ThinItalic.woff2 b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ThinItalic.woff2
index a6432151..a6432151 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPI.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ThinItalic.woff2 +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/wwwroot/lib/jetbrains-mono/webfonts/JetBrainsMono-ThinItalic.woff2
Binary files differdiff --git a/extra/admin-api/Utilities/Spacebar.AdminAPITest/Program.cs b/extra/admin-api/Utilities/Spacebar.AdminApiTest/Program.cs
index 00900e0a..00900e0a 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPITest/Program.cs +++ b/extra/admin-api/Utilities/Spacebar.AdminApiTest/Program.cs
diff --git a/extra/admin-api/Utilities/Spacebar.AdminAPITest/Spacebar.AdminAPITest.csproj b/extra/admin-api/Utilities/Spacebar.AdminApiTest/Spacebar.AdminApiTest.csproj
index ec27f0b6..5daabdd0 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminAPITest/Spacebar.AdminAPITest.csproj +++ b/extra/admin-api/Utilities/Spacebar.AdminApiTest/Spacebar.AdminApiTest.csproj
@@ -10,9 +10,7 @@ </PropertyGroup> <ItemGroup> - <Reference Include="ArcaneLibs"> - <HintPath>..\..\..\..\..\..\..\.nuget\packages\arcanelibs\1.0.0-preview.20241210-161342\lib\net10.0\ArcaneLibs.dll</HintPath> - </Reference> + <PackageReference Include="ArcaneLibs" Version="1.0.0-preview.20251207-164820" /> </ItemGroup> </Project> diff --git a/extra/admin-api/db-patches/00-use-stream-class.patch b/extra/admin-api/db-patches/00-use-stream-class.patch new file mode 100644
index 00000000..1636f120 --- /dev/null +++ b/extra/admin-api/db-patches/00-use-stream-class.patch
@@ -0,0 +1,14 @@ +diff --git a/Spacebar.Db/Contexts/SpacebarDbContext.cs b/Spacebar.Db/Contexts/SpacebarDbContext.cs +index 0a707c75..7d380b1c 100644 +--- a/Spacebar.Db/Contexts/SpacebarDbContext.cs ++++ b/Spacebar.Db/Contexts/SpacebarDbContext.cs +@@ -1,7 +1,6 @@ +-using System; +-using System.Collections.Generic; +-using Microsoft.EntityFrameworkCore; ++using Microsoft.EntityFrameworkCore; + using Spacebar.Db.Models; ++using Stream = Spacebar.Db.Models.Stream; + + namespace Spacebar.Db.Contexts; + diff --git a/extra/admin-api/db-patches/01-ulong-user-rights.patch b/extra/admin-api/db-patches/01-ulong-user-rights.patch new file mode 100644
index 00000000..362b67d7 --- /dev/null +++ b/extra/admin-api/db-patches/01-ulong-user-rights.patch
@@ -0,0 +1,28 @@ +diff --git a/Spacebar.Db/Models/User.cs b/Spacebar.Db/Models/User.cs +index 7af60bf4..323846d4 100644 +--- a/Spacebar.Db/Models/User.cs ++++ b/Spacebar.Db/Models/User.cs +@@ -90,19 +90,19 @@ public partial class User + public string? Email { get; set; } + + [Column("flags")] +- public long Flags { get; set; } ++ public ulong Flags { get; set; } + + [Column("public_flags")] +- public long PublicFlags { get; set; } ++ public ulong PublicFlags { get; set; } + + [Column("purchased_flags")] +- public long PurchasedFlags { get; set; } ++ public ulong PurchasedFlags { get; set; } + + [Column("premium_usage_flags")] + public int PremiumUsageFlags { get; set; } + + [Column("rights")] +- public long Rights { get; set; } ++ public ulong Rights { get; set; } + + [Column("data")] + public string Data { get; set; } = null!; diff --git a/extra/admin-api/db-patches/db-00-fix-flags.patch b/extra/admin-api/db-patches/db-00-fix-flags.patch deleted file mode 100644
index 437368f4..00000000 --- a/extra/admin-api/db-patches/db-00-fix-flags.patch +++ /dev/null
@@ -1,13 +0,0 @@ ---- Spacebar.Db/Models/User.cs.orig 2025-10-05 22:04:37.168566856 +0200 -+++ Spacebar.Db/Models/User.cs 2025-10-05 22:07:11.519980808 +0200 -@@ -92,8 +92,8 @@ - [Column("email", TypeName = "character varying")] - public string? Email { get; set; } - -- [Column("flags")] -- public int Flags { get; set; } -+ [Column("flags", TypeName = "character varying")] -+ public string Flags { get; set; } - - [Column("public_flags")] - public int PublicFlags { get; set; } diff --git a/extra/admin-api/flake.lock b/extra/admin-api/flake.lock
index 664ccdaa..10a74143 100644 --- a/extra/admin-api/flake.lock +++ b/extra/admin-api/flake.lock
@@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1759381078, - "narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=", + "lastModified": 1765472234, + "narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee", + "rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b", "type": "github" }, "original": { diff --git a/extra/admin-api/flake.nix b/extra/admin-api/flake.nix
index 6cf35682..7626bf6c 100644 --- a/extra/admin-api/flake.nix +++ b/extra/admin-api/flake.nix
@@ -1,5 +1,5 @@ { - description = "Spacebar Admin API, written in C#."; + description = "Spacebar server, written in Typescript."; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -12,81 +12,7 @@ nixpkgs, flake-utils, }: - flake-utils.lib.eachSystem flake-utils.lib.allSystems ( - system: - let - pkgs = import nixpkgs { - inherit system; - }; - hashesFile = builtins.fromJSON (builtins.readFile ./hashes.json); - lib = pkgs.lib; - in - { - packages = { - default = pkgs.buildNpmPackage { - pname = "spacebar-server-ts"; - name = "spacebar-server-ts"; - - meta = with lib; { - description = "Spacebar server, a FOSS reimplementation of the Discord backend."; - homepage = "https://github.com/spacebarchat/server"; - license = licenses.agpl3Plus; - platforms = platforms.all; - mainProgram = "start-bundle"; - }; - - src = ./.; - nativeBuildInputs = with pkgs; [ python3 ]; - npmDepsHash = hashesFile.npmDepsHash; - makeCacheWritable = true; - postPatch = '' - substituteInPlace package.json --replace 'npx patch-package' '${pkgs.nodePackages.patch-package}/bin/patch-package' - ''; - installPhase = '' - runHook preInstall - set -x - #remove packages not needed for production, or at least try to... - npm prune --omit dev --no-save $npmInstallFlags "''${npmInstallFlagsArray[@]}" $npmFlags "''${npmFlagsArray[@]}" - find node_modules -maxdepth 1 -type d -empty -delete - - mkdir -p $out - cp -r assets dist node_modules package.json $out/ - for i in dist/**/start.js - do - makeWrapper ${pkgs.nodejs}/bin/node $out/bin/start-`dirname ''${i/dist\//}` --prefix NODE_PATH : $out/node_modules --add-flags $out/$i - done - - set +x - runHook postInstall - ''; - }; - - update-nix = pkgs.writeShellApplication { - name = "update-nix"; - runtimeInputs = with pkgs; [ - prefetch-npm-deps - nix - jq - ]; - text = '' - nix flake update --extra-experimental-features 'nix-command flakes' - DEPS_HASH=$(prefetch-npm-deps package-lock.json) - TMPFILE=$(mktemp) - jq '.npmDepsHash = "'"$DEPS_HASH"'"' hashes.json > "$TMPFILE" - mv -- "$TMPFILE" hashes.json - ''; - }; - }; - - devShell = pkgs.mkShell { - buildInputs = with pkgs; [ - nodejs - nodePackages.typescript - nodePackages.ts-node - nodePackages.patch-package - nodePackages.prettier - ]; - }; - } - ); + import ./outputs.nix { + inherit self nixpkgs flake-utils; + }; } diff --git a/extra/admin-api/nuget.config b/extra/admin-api/nuget.config new file mode 100644
index 00000000..aa87aca2 --- /dev/null +++ b/extra/admin-api/nuget.config
@@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <packageSources> + <!--To inherit the global NuGet package sources remove the <clear/> line below --> + <clear /> + <add key="nuget" value="https://api.nuget.org/v3/index.json" /> + <add key="dotnet10-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10-transport/nuget/v3/index.json" /> + </packageSources> +</configuration> diff --git a/extra/admin-api/outputs.nix b/extra/admin-api/outputs.nix new file mode 100644
index 00000000..ccbed17d --- /dev/null +++ b/extra/admin-api/outputs.nix
@@ -0,0 +1,128 @@ +{ + self, + nixpkgs, + flake-utils, +}: +let + rVersion = + let + rev = self.sourceInfo.shortRev or self.sourceInfo.dirtyShortRev; + date = builtins.substring 0 8 self.sourceInfo.lastModifiedDate; + time = builtins.substring 8 6 self.sourceInfo.lastModifiedDate; + in + "preview.${date}-${time}"; #+${rev}"; +in +flake-utils.lib.eachSystem flake-utils.lib.allSystems ( + system: + let + pkgs = import nixpkgs { + inherit system; + }; + lib = pkgs.lib; + makeNupkg = + { + name, + nugetDeps ? null, + projectReferences ? [ ], + projectFile ? "${name}/${name}.csproj", + runtimeId ? null, + useAppHost ? null, + packNupkg ? true, + }@args: + pkgs.buildDotnetModule rec { + inherit + projectReferences + nugetDeps + projectFile + runtimeId + useAppHost + ; + + pname = "${name}"; + version = "1.0.0-" + rVersion; + dotnetPackFlags = [ + "--include-symbols" + "--include-source" + "--version-suffix ${rVersion}" + ]; + # dotnetFlags = [ "-v:diag" ]; + dotnet-sdk = pkgs.dotnet-sdk_10; + dotnet-runtime = pkgs.dotnet-aspnetcore_10; + src = pkgs.lib.cleanSource ./.; + packNupkg = true; + meta = with pkgs.lib; { + description = "Spacebar Server, Typescript Edition (C# extensions)"; + homepage = "https://github.com/spacebarchat/server"; + license = licenses.agpl3Plus; + maintainers = with maintainers; [ RorySys ]; + }; + }; + in + { + packages = + let + proj = self.packages.${system}; + in + { + Spacebar-Db = makeNupkg { + name = "Spacebar.Db"; + nugetDeps = Spacebar.Db/deps.json; + }; + Spacebar-AdminApi-Models = makeNupkg { + name = "Spacebar.AdminApi.Models"; + }; + Spacebar-ConfigModel = makeNupkg { + name = "Spacebar.ConfigModel"; + }; + Spacebar-CleanSettingsRows = makeNupkg { + name = "Spacebar.CleanSettingsRows"; + nugetDeps = Spacebar.CleanSettingsRows/deps.json; + packNupkg = false; + projectReferences = [ proj.Spacebar-Db ]; + }; + Spacebar-AdminApi = makeNupkg { + name = "Spacebar.AdminApi"; + nugetDeps = Spacebar.AdminApi/deps.json; + packNupkg = false; + projectReferences = [ + proj.Spacebar-AdminApi-Models + proj.Spacebar-Db + ]; + + }; +# Spacebar-AdminApi-TestClient = makeNupkg { +# name = "Spacebar.AdminApi.TestClient"; +# projectFile = "Utilities/Spacebar.AdminApi.TestClient/Spacebar.AdminApi.TestClient.csproj"; +# nugetDeps = Utilities/Spacebar.AdminApi.TestClient/deps.json; +# projectReferences = [ +# proj.Spacebar-AdminApi-Models +# ]; +## runtimeId = "browser-wasm"; +## useAppHost = false; +# }; + }; + + # containers.docker = pkgs.dockerTools.buildLayeredImage { + # name = "spacebar-server-ts"; + # tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.default.version; + # contents = [ self.packages.${system}.default ]; + # config = { + # Cmd = [ "${self.outputs.packages.${system}.default}/bin/start-bundle" ]; + # Expose = [ "3001" ]; + # }; + # }; + } +) +// { + # nixosModules.default = import ./nix/modules/default self; + checks = + let + pkgs = import nixpkgs { system = "x86_64-linux"; }; + in + pkgs.lib.recursiveUpdate (pkgs.lib.attrsets.unionOfDisjoint { } self.packages) { + x86_64-linux = { + # spacebar-server-tests = self.packages.x86_64-linux.default.passthru.tests; + # docker-image = self.containers.x86_64-linux.docker; + }; + }; +} \ No newline at end of file diff --git a/extra/admin-api/scaffold-db b/extra/admin-api/scaffold-db
index 0f332e00..19d32130 100755 --- a/extra/admin-api/scaffold-db +++ b/extra/admin-api/scaffold-db
@@ -19,6 +19,8 @@ rm Class1.cs dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL -n -f net9.0 dotnet add package Microsoft.EntityFrameworkCore.Design -n -f net9.0 +dotnet restore + dotnet-ef dbcontext scaffold "Host=127.0.0.1; Username=postgres; Database=sb-server-scaffold" \ Npgsql.EntityFrameworkCore.PostgreSQL \ -o Models \ @@ -28,6 +30,8 @@ dotnet-ef dbcontext scaffold "Host=127.0.0.1; Username=postgres; Database=sb-ser --no-onconfiguring \ --data-annotations -for patch in db-patches/*.patch; do - patch -p3 < $patch -done \ No newline at end of file +for patch in ../db-patches/*.patch; do + patch -p1 < $patch +done + +echo 'Scaffolded database and applied patches. Dont forget to generate new patches with `git diff --relative Spacebar.Db/path/xyz > db-patches/001-your-patch-name.patch!`' \ No newline at end of file diff --git a/flake.nix b/flake.nix
index dd947375..805ac788 100644 --- a/flake.nix +++ b/flake.nix
@@ -12,143 +12,154 @@ nixpkgs, flake-utils, }: - let - hashesFile = builtins.fromJSON (builtins.readFile ./hashes.json); - rVersion = + nixpkgs.lib.recursiveUpdate + ( let - rev = self.sourceInfo.shortRev or self.sourceInfo.dirtyShortRev; - date = builtins.substring 0 8 self.sourceInfo.lastModifiedDate; - time = builtins.substring 8 6 self.sourceInfo.lastModifiedDate; + hashesFile = builtins.fromJSON (builtins.readFile ./hashes.json); + rVersion = + let + rev = self.sourceInfo.shortRev or self.sourceInfo.dirtyShortRev; + date = builtins.substring 0 8 self.sourceInfo.lastModifiedDate; + time = builtins.substring 8 6 self.sourceInfo.lastModifiedDate; + in + "preview.${date}-${time}"; # +${rev}"; in - "preview.${date}-${time}+${rev}"; - in - flake-utils.lib.eachSystem flake-utils.lib.allSystems ( - system: - let - pkgs = import nixpkgs { - inherit system; - }; - lib = pkgs.lib; - in - { - packages = { - default = pkgs.buildNpmPackage { - pname = "spacebar-server-ts"; - nodejs = pkgs.nodejs_24; - version = "1.0.0-" + rVersion; - - meta = with lib; { - description = "Spacebar server, a FOSS reimplementation of the Discord backend."; - homepage = "https://github.com/spacebarchat/server"; - license = licenses.agpl3Plus; - platforms = platforms.all; - mainProgram = "start-bundle"; - maintainers = with maintainers; [ RorySys ]; # lol. + flake-utils.lib.eachSystem flake-utils.lib.allSystems ( + system: + let + pkgs = import nixpkgs { + inherit system; }; + lib = pkgs.lib; + in + { + packages = { + default = pkgs.buildNpmPackage { + pname = "spacebar-server-ts"; + nodejs = pkgs.nodejs_24; + version = "1.0.0-" + rVersion; - src = ./.; - npmDepsHash = hashesFile.npmDepsHash; - npmBuildScript = "build:src"; - makeCacheWritable = true; - nativeBuildInputs = with pkgs; [ - python3 - ]; - installPhase = - let - revsFile = pkgs.writeText "spacebar-server-rev.json" (builtins.toJSON { - rev = self.sourceInfo.rev or self.sourceInfo.dirtyRev; - shortRev = self.sourceInfo.shortRev or self.sourceInfo.dirtyShortRev; - lastModified = self.sourceInfo.lastModified; - }); - in '' - runHook preInstall - # set -x + meta = with lib; { + description = "Spacebar server, a FOSS reimplementation of the Discord backend."; + homepage = "https://github.com/spacebarchat/server"; + license = licenses.agpl3Plus; + platforms = platforms.all; + mainProgram = "start-bundle"; + maintainers = with maintainers; [ RorySys ]; # lol. + }; - # remove packages not needed for production, or at least try to... - npm prune --omit dev --no-save $npmInstallFlags "''${npmInstallFlagsArray[@]}" $npmFlags "''${npmFlagsArray[@]}" - ${./nix/trimNodeModules.sh} + src = ./.; + npmDepsHash = hashesFile.npmDepsHash; + npmBuildScript = "build:src"; + makeCacheWritable = true; + nativeBuildInputs = with pkgs; [ + python3 + ]; + installPhase = + let + revsFile = pkgs.writeText "spacebar-server-rev.json" ( + builtins.toJSON { + rev = self.sourceInfo.rev or self.sourceInfo.dirtyRev; + shortRev = self.sourceInfo.shortRev or self.sourceInfo.dirtyShortRev; + lastModified = self.sourceInfo.lastModified; + } + ); + in + '' + runHook preInstall + # set -x - # Copy outputs - echo "Installing package into $out" - mkdir -p $out - cp -r assets dist node_modules package.json $out/ - cp ${revsFile} $out/.rev + # remove packages not needed for production, or at least try to... + npm prune --omit dev --no-save $npmInstallFlags "''${npmInstallFlagsArray[@]}" $npmFlags "''${npmFlagsArray[@]}" + ${./nix/trimNodeModules.sh} - # Create wrappers for start scripts - echo "Creating wrappers for start scripts" - for i in dist/**/start.js - do - makeWrapper ${pkgs.nodejs_24}/bin/node $out/bin/start-`dirname ''${i/dist\//}` --prefix NODE_PATH : $out/node_modules --add-flags --enable-source-maps --add-flags $out/$i - done + # Copy outputs + echo "Installing package into $out" + mkdir -p $out + cp -r assets dist node_modules package.json $out/ + cp ${revsFile} $out/.rev - # set +x - runHook postInstall - ''; + # Create wrappers for start scripts + echo "Creating wrappers for start scripts" + for i in dist/**/start.js + do + makeWrapper ${pkgs.nodejs_24}/bin/node $out/bin/start-`dirname ''${i/dist\//}` --prefix NODE_PATH : $out/node_modules --add-flags --enable-source-maps --add-flags $out/$i + done - passthru.tests = pkgs.testers.runNixOSTest (import ./nix/tests/test-bundle-starts.nix self); - }; + # set +x + runHook postInstall + ''; - update-nix-hashes = pkgs.writeShellApplication { - name = "update-nix"; - runtimeInputs = with pkgs; [ - prefetch-npm-deps - nix - jq - ]; - text = '' - rm -rf node_modules - ${pkgs.nodejs_24}/bin/npm install --save --no-audit --no-fund --prefer-offline - DEPS_HASH=$(prefetch-npm-deps package-lock.json) - TMPFILE=$(mktemp) - jq '.npmDepsHash = "'"$DEPS_HASH"'"' hashes.json > "$TMPFILE" - mv -- "$TMPFILE" hashes.json - ''; - }; + passthru.tests = pkgs.testers.runNixOSTest (import ./nix/tests/test-bundle-starts.nix self); + }; - update-nix-flake = pkgs.writeShellApplication { - name = "update-nix"; - runtimeInputs = with pkgs; [ - prefetch-npm-deps - nix - jq - ]; - text = '' - nix flake update --extra-experimental-features 'nix-command flakes' - ''; - }; - }; + update-nix-hashes = pkgs.writeShellApplication { + name = "update-nix"; + runtimeInputs = with pkgs; [ + prefetch-npm-deps + nix + jq + ]; + text = '' + rm -rf node_modules + ${pkgs.nodejs_24}/bin/npm install --save --no-audit --no-fund --prefer-offline + DEPS_HASH=$(prefetch-npm-deps package-lock.json) + TMPFILE=$(mktemp) + jq '.npmDepsHash = "'"$DEPS_HASH"'"' hashes.json > "$TMPFILE" + mv -- "$TMPFILE" hashes.json + ''; + }; - containers.docker = pkgs.dockerTools.buildLayeredImage { - name = "spacebar-server-ts"; - tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.default.version; - contents = [ self.packages.${system}.default ]; - config = { - Cmd = [ "${self.outputs.packages.${system}.default}/bin/start-bundle" ]; - Expose = [ "3001" ]; - }; - }; + update-nix-flake = pkgs.writeShellApplication { + name = "update-nix"; + runtimeInputs = with pkgs; [ + prefetch-npm-deps + nix + jq + ]; + text = '' + nix flake update --extra-experimental-features 'nix-command flakes' + ''; + }; + }; - devShells.default = pkgs.mkShell { - buildInputs = with pkgs; [ - nodejs_24 - nodePackages.typescript - nodePackages.patch-package - nodePackages.prettier - ]; - }; - } - ) - // { - nixosModules.default = import ./nix/modules/default self; - checks = - let - pkgs = import nixpkgs { system = "x86_64-linux"; }; - in - pkgs.lib.recursiveUpdate (pkgs.lib.attrsets.unionOfDisjoint { } self.packages) { - x86_64-linux = { - spacebar-server-tests = self.packages.x86_64-linux.default.passthru.tests; - docker-image = self.containers.x86_64-linux.docker; - }; - }; - }; -} + containers.docker = pkgs.dockerTools.buildLayeredImage { + name = "spacebar-server-ts"; + tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.default.version; + contents = [ self.packages.${system}.default ]; + config = { + Cmd = [ "${self.outputs.packages.${system}.default}/bin/start-bundle" ]; + Expose = [ "3001" ]; + }; + }; + + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + nodejs_24 + nodePackages.typescript + nodePackages.patch-package + nodePackages.prettier + ]; + }; + } + ) + // { + nixosModules.default = import ./nix/modules/default self; + checks = + let + pkgs = import nixpkgs { system = "x86_64-linux"; }; + in + pkgs.lib.recursiveUpdate (pkgs.lib.attrsets.unionOfDisjoint { } self.packages) { + x86_64-linux = { + spacebar-server-tests = self.packages.x86_64-linux.default.passthru.tests; + docker-image = self.containers.x86_64-linux.docker; + }; + }; + } + ) + ( + import ./extra/admin-api/outputs.nix { + inherit self nixpkgs flake-utils; + } + ); +} \ No newline at end of file diff --git a/nix/tests/test-bundle-starts.nix b/nix/tests/test-bundle-starts.nix
index 4378369c..14108a41 100644 --- a/nix/tests/test-bundle-starts.nix +++ b/nix/tests/test-bundle-starts.nix
@@ -7,7 +7,14 @@ self: nodes.machine = { imports = [ self.nixosModules.default ]; - services.spacebarchat-server.enable = true; + services.spacebarchat-server = { + enable = true; + settings = { + api = { endpointPublic = "http://localhost:3001/api/v9/"; }; + cdn = { endpointPublic = "http://localhost:3001/"; endpointPrivate = "http://localhost:3001/"; }; + gateway = { endpointPublic = "ws://localhost:3001/"; }; + }; + }; }; testScript = '' diff --git a/scripts/test.js b/scripts/test.js
index 69e9fdd6..c79e336f 100644 --- a/scripts/test.js +++ b/scripts/test.js
@@ -23,10 +23,21 @@ const { spawn } = require("child_process"); const path = require("path"); +const fs = require("fs"); -const server = spawn("node", [ - path.join(__dirname, "..", "dist", "bundle", "start.js"), -]); +const cfgFile = path.join(__dirname, "test_config.json"); +process.env.CONFIG_PATH = cfgFile; + +fs.writeFileSync( + cfgFile, + JSON.stringify({ + api: { endpointPublic: "http://localhost:3001/api/v9/" }, + cdn: { endpointPublic: "http://localhost:3001/", endpointPrivate: "http://localhost:3001/" }, + gateway: { endpointPublic: "ws://localhost:3001/" }, + }), +); + +const server = spawn("node", [path.join(__dirname, "..", "dist", "bundle", "start.js")]); server.stdout.on("data", (data) => { process.stdout.write(data); diff --git a/scripts/util/getRouteDescriptions.js b/scripts/util/getRouteDescriptions.js
index 9f329f32..0372aae7 100644 --- a/scripts/util/getRouteDescriptions.js +++ b/scripts/util/getRouteDescriptions.js
@@ -49,7 +49,7 @@ function proxy(file, apiMethod, apiPathPrefix, apiPath, ...args) { const opts = args.find((x) => x?.prototype?.OPTS_MARKER == true); if (!opts) return console.error( - ` \x1b[5m${bgRedBright("ERROR")}\x1b[25m ${file.replace(path.resolve(__dirname, "..", "..", "dist"), "/src/")} has route without route() description middleware: ${colorizeMethod(apiMethod)} ${formatPath(apiPath)}`, + ` \x1b[5m${bgRedBright("ERROR")}\x1b[25m ${file.replace(path.resolve(__dirname, "..", "..", "dist"), "/src")} has route without route() description middleware: ${colorizeMethod(apiMethod)} ${formatPath(apiPath)}`, ); console.log(`${colorizeMethod(apiMethod).padStart("DELETE".length + 10)} ${formatPath(apiPathPrefix + apiPath)}`); diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts
index 4bb809a6..2dd992ee 100644 --- a/src/api/middlewares/Authentication.ts +++ b/src/api/middlewares/Authentication.ts
@@ -70,6 +70,7 @@ declare global { user_bot: boolean; token: { id: string; iat: number }; rights: Rights; + fingerprint?: string; } } } @@ -77,6 +78,15 @@ declare global { export async function Authentication(req: Request, res: Response, next: NextFunction) { if (req.method === "OPTIONS") return res.sendStatus(204); const url = req.url.replace(API_PREFIX, ""); + + if (req.headers.cookie?.split("; ").find((x) => x.startsWith("__sb_sessid="))) + req.fingerprint = req.headers.cookie + .split("; ") + .find((x) => x.startsWith("__sb_sessid="))! + .split("=")[1]; + // for some reason we need to require here, else the openapi generator fails with "route is not a function" + else res.setHeader("Set-Cookie", `__sb_sessid=${req.fingerprint = (await require("../util")).randomString(32)}; Secure; HttpOnly; SameSite=None; Path=/`); + if ( NO_AUTHORIZATION_ROUTES.some((x) => { if (typeof x !== "string") { @@ -102,10 +112,14 @@ export async function Authentication(req: Request, res: Response, next: NextFunc }) ) return next(); + if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401)); try { - const { decoded, user } = await checkToken(req.headers.authorization); + const { decoded, user } = await checkToken(req.headers.authorization, { + ipAddress: req.ip, + fingerprint: req.fingerprint, + }); req.token = decoded; req.user_id = decoded.id; diff --git a/src/api/middlewares/CORS.ts b/src/api/middlewares/CORS.ts
index ca6abb82..34532d3f 100644 --- a/src/api/middlewares/CORS.ts +++ b/src/api/middlewares/CORS.ts
@@ -21,20 +21,16 @@ import { NextFunction, Request, Response } from "express"; // TODO: config settings export function CORS(req: Request, res: Response, next: NextFunction) { - res.set("Access-Control-Allow-Origin", "*"); + res.set("Access-Control-Allow-Credentials", "true"); + res.set("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers") || "*"); + res.set("Access-Control-Allow-Methods", req.header("Access-Control-Request-Method") || "*"); + res.set("Access-Control-Allow-Origin", req.header("Origin") ?? "*"); + res.set("Access-Control-Max-Age", "5"); // dont make it too long so we can change it dynamically // TODO: use better CSP res.set( "Content-security-policy", "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';", ); - res.set( - "Access-Control-Allow-Headers", - req.header("Access-Control-Request-Headers") || "*", - ); - res.set( - "Access-Control-Allow-Methods", - req.header("Access-Control-Request-Methods") || "*", - ); if (req.method === "OPTIONS") { res.status(204).end(); diff --git a/src/api/middlewares/RateLimit.ts b/src/api/middlewares/RateLimit.ts
index 81ccbc51..1a41ad4f 100644 --- a/src/api/middlewares/RateLimit.ts +++ b/src/api/middlewares/RateLimit.ts
@@ -16,7 +16,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress } from "@spacebar/api"; import { Config, getRights, listenEvent } from "@spacebar/util"; import { NextFunction, Request, Response, Router } from "express"; import { API_PREFIX_TRAILING_SLASH } from "./Authentication"; @@ -65,21 +64,14 @@ export default function rateLimit(opts: { if (rights.has("BYPASS_RATE_LIMITS")) return next(); } - const bucket_id = - opts.bucket || - req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, ""); - let executor_id = getIpAdress(req); + const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, ""); + let executor_id = req.ip || "127.0.0.1"; if (!opts.onlyIp && req.user_id) executor_id = req.user_id; let max_hits = opts.count; if (opts.bot && req.user_bot) max_hits = opts.bot; - if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) - max_hits = opts.GET; - else if ( - opts.MODIFY && - ["POST", "DELETE", "PATCH", "PUT"].includes(req.method) - ) - max_hits = opts.MODIFY; + if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) max_hits = opts.GET; + else if (opts.MODIFY && ["POST", "DELETE", "PATCH", "PUT"].includes(req.method)) max_hits = opts.MODIFY; const offender = Cache.get(executor_id + bucket_id); @@ -104,18 +96,13 @@ export default function rateLimit(opts: { } res.set("X-RateLimit-Reset", `${reset}`); - res.set( - "X-RateLimit-Reset-After", - `${Math.max(0, Math.ceil(resetAfterSec))}`, - ); + res.set("X-RateLimit-Reset-After", `${Math.max(0, Math.ceil(resetAfterSec))}`); if (offender.blocked) { const global = bucket_id === "global"; // each block violation pushes the expiry one full window further reset += opts.window * 1000; - offender.expires_at = new Date( - offender.expires_at.getTime() + opts.window * 1000, - ); + offender.expires_at = new Date(offender.expires_at.getTime() + opts.window * 1000); resetAfterMs = reset - Date.now(); resetAfterSec = Math.ceil(resetAfterMs / 1000); @@ -129,10 +116,7 @@ export default function rateLimit(opts: { res .status(429) .set("X-RateLimit-Remaining", "0") - .set( - "Retry-After", - `${Math.max(0, Math.ceil(resetAfterSec))}`, - ) + .set("Retry-After", `${Math.max(0, Math.ceil(resetAfterSec))}`) // TODO: error rate limit message translation .send({ message: "You are being rate limited.", @@ -156,11 +140,7 @@ export default function rateLimit(opts: { // check if error and increment error rate limit if (res.statusCode >= 400 && opts.error) { return hitRoute(hitRouteOpts); - } else if ( - res.statusCode >= 200 && - res.statusCode < 300 && - opts.success - ) { + } else if (res.statusCode >= 200 && res.statusCode < 300 && opts.success) { return hitRoute(hitRouteOpts); } }); @@ -213,18 +193,10 @@ export async function initRateLimits(app: Router) { app.use("/webhooks/:webhook_id", rateLimit(routes.webhook)); app.use("/channels/:channel_id", rateLimit(routes.channel)); app.use("/auth/login", rateLimit(routes.auth.login)); - app.use( - "/auth/register", - rateLimit({ onlyIp: true, success: true, ...routes.auth.register }), - ); + app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register })); } -async function hitRoute(opts: { - executor_id: string; - bucket_id: string; - max_hits: number; - window: number; -}) { +async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number }) { const id = opts.executor_id + opts.bucket_id; let limit = Cache.get(id); if (!limit) { diff --git a/src/api/routes/auth/forgot.ts b/src/api/routes/auth/forgot.ts
index 12bc9cb0..6e613820 100644 --- a/src/api/routes/auth/forgot.ts +++ b/src/api/routes/auth/forgot.ts
@@ -16,10 +16,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, route, verifyCaptcha } from "@spacebar/api"; +import { route, verifyCaptcha } from "@spacebar/api"; import { Config, Email, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; -import { ForgotPasswordSchema } from "@spacebar/schemas" +import { ForgotPasswordSchema } from "@spacebar/schemas"; const router = Router({ mergeParams: true }); router.post( @@ -38,10 +38,7 @@ router.post( const config = Config.get(); - if ( - config.passwordReset.requireCaptcha && - config.security.captcha.enabled - ) { + if (config.passwordReset.requireCaptcha && config.security.captcha.enabled) { const { sitekey, service } = config.security.captcha; if (!captcha_key) { return res.status(400).json({ @@ -51,7 +48,7 @@ router.post( }); } - const ip = getIpAdress(req); + const ip = req.ip; const verify = await verifyCaptcha(captcha_key, ip); if (!verify.success) { return res.status(400).json({ @@ -71,9 +68,7 @@ router.post( if (user && user.email) { Email.sendResetPassword(user, user.email).catch((e) => { - console.error( - `Failed to send password reset email to ${user.username}#${user.discriminator} (${user.id}): ${e}`, - ); + console.error(`Failed to send password reset email to ${user.username}#${user.discriminator} (${user.id}): ${e}`); }); } }, diff --git a/src/api/routes/auth/location-metadata.ts b/src/api/routes/auth/location-metadata.ts
index a08c98ab..92f47909 100644 --- a/src/api/routes/auth/location-metadata.ts +++ b/src/api/routes/auth/location-metadata.ts
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, route } from "@spacebar/api"; +import { route } from "@spacebar/api"; import { IpDataClient } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router({ mergeParams: true }); @@ -33,7 +33,7 @@ router.get( async (req: Request, res: Response) => { //TODO //Note: It's most likely related to legal. At the moment Discord hasn't finished this too - const country_code = (await IpDataClient.getIpInfo(getIpAdress(req)))?.country_code; + const country_code = (await IpDataClient.getIpInfo(req.ip!))?.country_code; res.json({ consent_required: false, country_code: country_code, diff --git a/src/api/routes/auth/login.ts b/src/api/routes/auth/login.ts
index 06e3fe43..c8ef6273 100644 --- a/src/api/routes/auth/login.ts +++ b/src/api/routes/auth/login.ts
@@ -16,19 +16,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, route, verifyCaptcha } from "@spacebar/api"; -import { - Config, - FieldErrors, - User, - WebAuthn, - generateToken, - generateWebAuthnTicket, -} from "@spacebar/util"; +import { route, verifyCaptcha } from "@spacebar/api"; +import { Config, FieldErrors, User, WebAuthn, generateToken, generateWebAuthnTicket } from "@spacebar/util"; import bcrypt from "bcrypt"; import crypto from "crypto"; import { Request, Response, Router } from "express"; -import { LoginSchema } from "@spacebar/schemas" +import { LoginSchema } from "@spacebar/schemas"; const router: Router = Router({ mergeParams: true }); export default router; @@ -61,7 +54,7 @@ router.post( }); } - const ip = getIpAdress(req); + const ip = req.ip; const verify = await verifyCaptcha(captcha_key, ip); if (!verify.success) { return res.status(400).json({ @@ -74,17 +67,7 @@ router.post( const user = await User.findOneOrFail({ where: [{ phone: login }, { email: login }], - select: [ - "data", - "id", - "disabled", - "deleted", - "totp_secret", - "mfa_enabled", - "webauthn_enabled", - "security_keys", - "verified", - ], + select: ["data", "id", "disabled", "deleted", "totp_secret", "mfa_enabled", "webauthn_enabled", "security_keys", "verified"], relations: ["security_keys", "settings"], }).catch(() => { throw FieldErrors({ @@ -100,10 +83,7 @@ router.post( }); // the salt is saved in the password refer to bcrypt docs - const same_password = await bcrypt.compare( - password, - user.data.hash || "", - ); + const same_password = await bcrypt.compare(password, user.data.hash || ""); if (!same_password) { throw FieldErrors({ login: { @@ -122,8 +102,7 @@ router.post( throw FieldErrors({ login: { code: "ACCOUNT_LOGIN_VERIFICATION_EMAIL", - message: - "Email verification is required, please check your email.", + message: "Email verification is required, please check your email.", }, }); } @@ -152,9 +131,7 @@ router.post( const challenge = JSON.stringify({ publicKey: { ...options, - challenge: Buffer.from(options.challenge).toString( - "base64", - ), + challenge: Buffer.from(options.challenge).toString("base64"), allowCredentials: user.security_keys.map((x) => ({ id: x.key_id, type: "public-key", @@ -178,10 +155,8 @@ router.post( if (undelete) { // undelete refers to un'disable' here - if (user.disabled) - await User.update({ id: user.id }, { disabled: false }); - if (user.deleted) - await User.update({ id: user.id }, { deleted: false }); + if (user.disabled) await User.update({ id: user.id }, { disabled: false }); + if (user.deleted) await User.update({ id: user.id }, { deleted: false }); } else { if (user.deleted) return res.status(400).json({ diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts
index 418f0a52..d4b061be 100644 --- a/src/api/routes/auth/register.ts +++ b/src/api/routes/auth/register.ts
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, route, verifyCaptcha } from "@spacebar/api"; +import { route, verifyCaptcha } from "@spacebar/api"; import { Config, FieldErrors, Invite, User, ValidRegistrationToken, generateToken, IpDataClient, AbuseIpDbClient } from "@spacebar/util"; import bcrypt from "bcrypt"; import { Request, Response, Router } from "express"; @@ -38,7 +38,7 @@ router.post( async (req: Request, res: Response) => { const body = req.body as RegisterSchema; const { register, security, limits } = Config.get(); - const ip = getIpAdress(req); + const ip = req.ip!; // Reg tokens // They're a one time use token that bypasses registration limits ( rates, disabled reg, etc ) @@ -143,7 +143,7 @@ router.post( const ipData = await IpDataClient.getIpInfo(ip); if (ipData) { - if(!ipData.threat) { + if (!ipData.threat) { console.log("Invalid IPData.co response, missing threat field", ipData); } const categories = Object.entries(ipData.threat) @@ -287,7 +287,7 @@ router.post( }, })) >= limits.absoluteRate.register.limit ) { - console.log(`Global register ratelimit exceeded for ${getIpAdress(req)}, ${req.body.username}, ${req.body.invite || "No invite given"}`); + console.log(`Global register ratelimit exceeded for ${req.ip}, ${req.body.username}, ${req.body.invite || "No invite given"}`); throw FieldErrors({ email: { code: "TOO_MANY_REGISTRATIONS", diff --git a/src/api/routes/auth/reset.ts b/src/api/routes/auth/reset.ts
index b14eb379..5cb5690d 100644 --- a/src/api/routes/auth/reset.ts +++ b/src/api/routes/auth/reset.ts
@@ -51,6 +51,8 @@ router.post( try { const userTokenData = await checkToken(token, { select: ["email"], + fingerprint: req.fingerprint, + ipAddress: req.ip }); user = userTokenData.user; } catch { diff --git a/src/api/routes/auth/verify/index.ts b/src/api/routes/auth/verify/index.ts
index b85120d8..8c636196 100644 --- a/src/api/routes/auth/verify/index.ts +++ b/src/api/routes/auth/verify/index.ts
@@ -16,14 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, route, verifyCaptcha } from "@spacebar/api"; -import { - checkToken, - Config, - FieldErrors, - generateToken, - User, -} from "@spacebar/util"; +import { route, verifyCaptcha } from "@spacebar/api"; +import { checkToken, Config, FieldErrors, generateToken, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router({ mergeParams: true }); @@ -67,7 +61,7 @@ router.post( }); } - const ip = getIpAdress(req); + const ip = req.ip; const verify = await verifyCaptcha(captcha_key, ip); if (!verify.success) { return res.status(400).json({ @@ -81,7 +75,10 @@ router.post( let user; try { - const userTokenData = await checkToken(token); + const userTokenData = await checkToken(token, { + fingerprint: req.fingerprint, + ipAddress: req.ip + }); user = userTokenData.user; } catch { throw FieldErrors({ diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts
index 8790241d..43c4984e 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts
@@ -548,7 +548,7 @@ router.delete( // TODO: handle other read state types if (body.read_state_type != ReadStateType.CHANNEL) return res.status(204).send(); - const readState = await ReadState.findOne({where: {channel_id}}); + const readState = await ReadState.findOne({ where: { channel_id, user_id: req.user_id } }); if (readState) { await readState.remove(); } diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts
index 1274c195..39eee34a 100644 --- a/src/api/routes/guilds/#guild_id/bans.ts +++ b/src/api/routes/guilds/#guild_id/bans.ts
@@ -16,11 +16,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, route } from "@spacebar/api"; +import { route } from "@spacebar/api"; import { Ban, DiscordApiErrors, GuildBanAddEvent, GuildBanRemoveEvent, Member, User, emitEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; -import { APIBansArray, BanRegistrySchema, GuildBansResponse } from "@spacebar/schemas"; +import { APIBansArray, BanCreateSchema, BanRegistrySchema, GuildBansResponse } from "@spacebar/schemas"; const router: Router = Router({ mergeParams: true }); @@ -198,6 +198,15 @@ router.put( async (req: Request, res: Response) => { const { guild_id } = req.params; const banned_user_id = req.params.user_id; + const opts = req.body as BanCreateSchema; + + let deleteMessagesMs = opts.delete_message_days + ? (opts.delete_message_days as number) * 86400000 + : opts.delete_message_seconds + ? (opts.delete_message_seconds as number) * 1000 + : 0; + + if (deleteMessagesMs < 0) deleteMessagesMs = 0; if (req.user_id === banned_user_id && banned_user_id === req.permission?.cache.guild?.owner_id) throw new HTTPError("You are the guild owner, hence can't ban yourself", 403); @@ -207,6 +216,7 @@ router.put( const existingBan = await Ban.findOne({ where: { guild_id: guild_id, user_id: banned_user_id }, }); + // Bans on already banned users are silently ignored if (existingBan) return res.status(204).send(); @@ -227,6 +237,7 @@ router.put( data: { guild_id: guild_id, user: banned_user.toPublicUser(), + delete_message_secs: Math.floor(deleteMessagesMs / 1000), }, guild_id: guild_id, } as GuildBanAddEvent), @@ -279,4 +290,4 @@ router.delete( }, ); -export default router; +export default router; \ No newline at end of file diff --git a/src/api/routes/guilds/#guild_id/bulk-ban.ts b/src/api/routes/guilds/#guild_id/bulk-ban.ts
index 3d3ed1c3..a564e2d4 100644 --- a/src/api/routes/guilds/#guild_id/bulk-ban.ts +++ b/src/api/routes/guilds/#guild_id/bulk-ban.ts
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, route } from "@spacebar/api"; +import { route } from "@spacebar/api"; import { Ban, DiscordApiErrors, GuildBanAddEvent, Member, User, emitEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; @@ -81,7 +81,7 @@ router.post( const ban = Ban.create({ user_id: banned_user_id, guild_id: guild_id, - ip: getIpAdress(req), + ip: req.ip, executor_id: req.user_id, reason: req.body.reason, // || otherwise empty }); diff --git a/src/api/routes/guilds/#guild_id/regions.ts b/src/api/routes/guilds/#guild_id/regions.ts
index a7fad818..a3c5a81b 100644 --- a/src/api/routes/guilds/#guild_id/regions.ts +++ b/src/api/routes/guilds/#guild_id/regions.ts
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, getVoiceRegions, route } from "@spacebar/api"; +import { getVoiceRegions, route } from "@spacebar/api"; import { Guild } from "@spacebar/util"; import { Request, Response, Router } from "express"; @@ -38,12 +38,7 @@ router.get( const { guild_id } = req.params; const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); //TODO we should use an enum for guild's features and not hardcoded strings - return res.json( - await getVoiceRegions( - getIpAdress(req), - guild.features.includes("VIP_REGIONS"), - ), - ); + return res.json(await getVoiceRegions(req.ip!, guild.features.includes("VIP_REGIONS"))); }, ); diff --git a/src/api/routes/invites/index.ts b/src/api/routes/invites/index.ts
index c77d36ea..87715f35 100644 --- a/src/api/routes/invites/index.ts +++ b/src/api/routes/invites/index.ts
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, route } from "@spacebar/api"; +import { route } from "@spacebar/api"; import { Ban, DiscordApiErrors, emitEvent, getPermission, Guild, Invite, InviteDeleteEvent, PublicInviteRelation, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; @@ -83,7 +83,7 @@ router.post( const ban = await Ban.findOne({ where: [ { guild_id: guild_id, user_id: req.user_id }, - { guild_id: guild_id, ip: getIpAdress(req) }, + { guild_id: guild_id, ip: req.ip }, ], }); diff --git a/src/api/routes/oauth2/authorize.ts b/src/api/routes/oauth2/authorize.ts
index a14d5705..4084a059 100644 --- a/src/api/routes/oauth2/authorize.ts +++ b/src/api/routes/oauth2/authorize.ts
@@ -17,18 +17,9 @@ */ import { route } from "@spacebar/api"; -import { - ApiError, - Application, - DiscordApiErrors, - FieldErrors, - Member, - Permissions, - User, - getPermission, -} from "@spacebar/util"; +import { ApiError, Application, DiscordApiErrors, FieldErrors, Member, Permissions, User, getPermission } from "@spacebar/util"; import { Request, Response, Router } from "express"; -import { ApplicationAuthorizeSchema } from "@spacebar/schemas" +import { ApplicationAuthorizeSchema } from "@spacebar/schemas"; const router = Router({ mergeParams: true }); // TODO: scopes, other oauth types @@ -84,26 +75,18 @@ router.get( id: req.user_id, bot: false, }, - select: [ - "id", - "username", - "avatar", - "discriminator", - "public_flags", - ], + select: ["id", "username", "avatar", "discriminator", "public_flags"], }); const guilds = await Member.find({ where: { - user: { - id: req.user_id, - }, + id: req.user_id, }, relations: ["guild", "roles", "user"], // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore // prettier-ignore - select: ["guild.id", "guild.name", "guild.icon", "guild.mfa_level", "guild.owner_id", "roles.id", "communication_disabled_until", "user.flags"], + select: ["guild.id", "guild.name", "guild.icon", "guild.mfa_level", "guild.owner_id", "roles.id", "user.flags"], }); const guildsWithPermissions = guilds.map((x) => { @@ -112,7 +95,7 @@ router.get( id: user.id, roles: x.roles?.map((x) => x.id) || [], communication_disabled_until: x.communication_disabled_until, - flags: x.user.flags + flags: x.user.flags, }, guild: { roles: x?.roles || [], @@ -211,18 +194,9 @@ router.post( // TODO: captcha verification // TODO: MFA verification - const perms = await getPermission( - req.user_id, - body.guild_id, - undefined, - { member_relations: ["user"] }, - ); + const perms = await getPermission(req.user_id, body.guild_id, undefined, { member_relations: ["user"] }); // getPermission cache won't exist if we're owner - if ( - Object.keys(perms.cache || {}).length > 0 && - perms.cache.member?.user.bot - ) - throw DiscordApiErrors.UNAUTHORIZED; + if (Object.keys(perms.cache || {}).length > 0 && perms.cache.member?.user.bot) throw DiscordApiErrors.UNAUTHORIZED; perms.hasThrow("MANAGE_GUILD"); const app = await Application.findOne({ @@ -235,12 +209,7 @@ router.post( // TODO: use DiscordApiErrors // findOneOrFail throws code 404 if (!app) throw new ApiError("Unknown Application", 10002, 404); - if (!app.bot) - throw new ApiError( - "OAuth2 application does not have a bot", - 50010, - 400, - ); + if (!app.bot) throw new ApiError("OAuth2 application does not have a bot", 50010, 400); await Member.addToGuild(app.id, body.guild_id); diff --git a/src/api/routes/users/#user_id/delete.ts b/src/api/routes/users/#user_id/delete.ts
index dbb18e29..50494a23 100644 --- a/src/api/routes/users/#user_id/delete.ts +++ b/src/api/routes/users/#user_id/delete.ts
@@ -17,9 +17,25 @@ */ import { route } from "@spacebar/api"; -import { emitEvent, Member, User, UserDeleteEvent } from "@spacebar/util"; +import { + Channel, + ChannelDeleteEvent, + ChannelRecipientRemoveEvent, + emitEvent, + Emoji, + Guild, + InstanceBan, + Member, + Recipient, + Sticker, + Stopwatch, + User, + UserDeleteEvent, + UserSettingsProtos, +} from "@spacebar/util"; import { Request, Response, Router } from "express"; -import { PrivateUserProjection } from "@spacebar/schemas"; +import { ChannelType, InstanceUserDeleteSchema, PrivateUserProjection } from "@spacebar/schemas"; +import { Not } from "typeorm"; const router = Router({ mergeParams: true }); @@ -27,6 +43,7 @@ router.post( "/", route({ right: "MANAGE_USERS", + requestBody: "InstanceUserDeleteSchema", responses: { 204: {}, 403: { @@ -38,12 +55,132 @@ router.post( }, }), async (req: Request, res: Response) => { - await User.findOneOrFail({ + const sw = Stopwatch.startNew(); + const body = req.body as InstanceUserDeleteSchema | undefined; + const user = await User.findOneOrFail({ where: { id: req.params.user_id }, select: [...PrivateUserProjection, "data"], }); + + if (!(await InstanceBan.findOne({ where: { user_id: user.id } }))) + await InstanceBan.create({ user_id: user.id, reason: body?.reason ?? "<legacy instance ban API - no reason specified>" }).save(); + + // prevent bugginess with clients - delete all DMs, only having half of the conversation is quite useless anyhow + const dmChannels = await user.getDmChannels(); + for (const channel of dmChannels) { + console.log(`[Instance ban] Deleting DM channel ${channel.id} for user ${user.id}`); + await emitEvent({ + event: "CHANNEL_DELETE", + data: channel.toJSON(), + channel_id: channel.id, + } as ChannelDeleteEvent); + await Recipient.delete({ channel_id: channel.id }); + await Channel.deleteChannel(channel); + } + + //leave all group channels + const groupChannels = await Channel.find({ + where: { type: ChannelType.GROUP_DM }, + relations: ["recipients"], + select: { + id: true, + owner_id: true, + recipients: { + id: true, + user_id: true, + }, + }, + }); + + await Promise.all( + groupChannels.map(async (channel) => { + const recipient = channel.recipients!.find((r) => r.user_id === user.id); + if (recipient) { + await Recipient.delete({ id: recipient.id }); + await emitEvent({ + event: "CHANNEL_RECIPIENT_REMOVE", + data: { + user: user.toPublicUser(), + channel_id: channel.id, + }, + channel_id: channel.id, + } as ChannelRecipientRemoveEvent); + console.log(`[Instance ban] Removed user ${user.id} from group channel ${channel.id}`); + } + + // if no recipients remain, delete the channel + const remainingRecipients = await Recipient.find({ where: { channel_id: channel.id } }); + if (remainingRecipients.length === 0) { + await emitEvent({ + event: "CHANNEL_DELETE", + data: channel.toJSON(), + channel_id: channel.id, + } as ChannelDeleteEvent); + await Channel.deleteChannel(channel); + console.log(`[Instance ban] Deleted empty group channel ${channel.id}`); + } else { + // otherwise, if the banned user was the owner, reassign ownership + if (channel.owner_id === user.id) { + channel.owner_id = remainingRecipients[0].user_id; + await channel.save(); + console.log(`[Instance ban] Reassigned ownership of group channel ${channel.id} to user ${channel.owner_id}`); + } + } + }), + ); + + // change ownership on guilds + const guilds = await Guild.find({ where: { owner_id: req.params.user_id } }); + await Promise.all( + guilds.map(async (guild) => { + const members = await Member.find({ + where: { guild_id: guild.id, id: Not(req.params.user_id) }, + relations: { roles: true }, + select: { id: true, roles: { id: true, position: true } }, + }); + const sortedMembers = members + .filter((m) => m.id !== req.params.user_id) + .sort((a, b) => { + const aHighestRole = a.roles.reduce((prev, curr) => (curr.position > prev.position ? curr : prev), { position: -1 } as { position: number }); + const bHighestRole = b.roles.reduce((prev, curr) => (curr.position > prev.position ? curr : prev), { position: -1 } as { position: number }); + return bHighestRole.position - aHighestRole.position; + }); + if (sortedMembers.length === 0) { + // no members left, delete guild + await guild.remove(); + console.log(`[Instance ban] Deleted guild ${guild.id} as user ${user.id} was the last member`); + } else { + // assign new owner + guild.owner_id = sortedMembers[0].id; + await guild.save(); + console.log(`[Instance ban] Transferred ownership of guild ${guild.id} to user ${guild.owner_id}`); + + // safety - reassign emojis/stickers owned by the old owner + const stickers = await Sticker.find({ where: { guild_id: guild.id, user_id: req.params.user_id } }); + await Promise.all( + stickers.map(async (sticker) => { + sticker.user_id = guild.owner_id; + await sticker.save(); + console.log(`[Instance ban] Reassigned sticker ${sticker.id} ownership to user ${guild.owner_id}`); + }), + ); + + const emojis = await Emoji.find({ where: { guild_id: guild.id, user_id: req.params.user_id } }); + await Promise.all( + emojis.map(async (emoji) => { + emoji.user_id = guild.owner_id!; + await emoji.save(); + console.log(`[Instance ban] Reassigned emoji ${emoji.id} ownership to user ${guild.owner_id}`); + }), + ); + } + }), + ); + const members = await Member.find({ where: { id: req.params.user_id } }); - await Promise.all([...members.map((member) => Member.removeFromGuild(member.id, member.guild_id)), User.delete({ id: req.params.user_id })]); + await Promise.all([...members.map((member) => Member.removeFromGuild(member.id, member.guild_id))]); + await UserSettingsProtos.delete({ user_id: req.params.user_id }); + await User.delete({ id: req.params.user_id }); // TODO: respect intents as USER_DELETE has potential to cause privacy issues await emitEvent({ @@ -52,6 +189,7 @@ router.post( data: { user_id: req.params.user_id }, } as UserDeleteEvent); + console.log(`[Instance ban] Deleted user ${user.id} from instance in ${sw.elapsed().toString()}`); res.sendStatus(204); }, ); diff --git a/src/api/routes/voice/regions.ts b/src/api/routes/voice/regions.ts
index 9fdd6fde..ba261231 100644 --- a/src/api/routes/voice/regions.ts +++ b/src/api/routes/voice/regions.ts
@@ -16,7 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { getIpAdress, getVoiceRegions, route } from "@spacebar/api"; +import { getVoiceRegions, route } from "@spacebar/api"; import { Request, Response, Router } from "express"; const router: Router = Router({ mergeParams: true }); @@ -31,7 +31,7 @@ router.get( }, }), async (req: Request, res: Response) => { - res.json(await getVoiceRegions(getIpAdress(req), true)); //vip true? + res.json(await getVoiceRegions(req.ip!, true)); //vip true? }, ); diff --git a/src/api/util/utility/ipAddress.ts b/src/api/util/utility/ipAddress.ts
index 914999a7..19408253 100644 --- a/src/api/util/utility/ipAddress.ts +++ b/src/api/util/utility/ipAddress.ts
@@ -16,41 +16,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { Config } from "@spacebar/util"; -import { Request } from "express"; - -export function getIpAdress(req: Request): string { - // TODO: express can do this (trustProxies: true)? - - return req.ip!; -} - type Location = { latitude: number; longitude: number }; -export function distanceBetweenLocations( - loc1: Location, - loc2: Location, -): number { - return distanceBetweenCoords( - loc1.latitude, - loc1.longitude, - loc2.latitude, - loc2.longitude, - ); +export function distanceBetweenLocations(loc1: Location, loc2: Location): number { + return distanceBetweenCoords(loc1.latitude, loc1.longitude, loc2.latitude, loc2.longitude); } //Haversine function -function distanceBetweenCoords( - lat1: number, - lon1: number, - lat2: number, - lon2: number, -) { +function distanceBetweenCoords(lat1: number, lon1: number, lat2: number, lon2: number) { const p = 0.017453292519943295; // Math.PI / 180 const c = Math.cos; - const a = - 0.5 - - c((lat2 - lat1) * p) / 2 + - (c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p))) / 2; + const a = 0.5 - c((lat2 - lat1) * p) / 2 + (c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p))) / 2; return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km } diff --git a/src/gateway/Server.ts b/src/gateway/Server.ts
index 269cc2ed..2ad3e413 100644 --- a/src/gateway/Server.ts +++ b/src/gateway/Server.ts
@@ -28,6 +28,7 @@ import ws from "ws"; import { Connection } from "./events/Connection"; import http from "http"; import { cleanupOnStartup } from "./util/Utils"; +import { randomString } from "@spacebar/api"; export class Server { public ws: ws.Server; @@ -50,6 +51,10 @@ export class Server { if (server) this.server = server; else { this.server = http.createServer(function (req, res) { + if(!req.headers.cookie?.split("; ").find(x => x.startsWith("__sb_sessid="))) { + res.setHeader("Set-Cookie", `__sb_sessid=${randomString(32)}; Secure; HttpOnly; SameSite=None; Path=/`); + } + res.writeHead(200).end("Online"); }); } diff --git a/src/gateway/events/Connection.ts b/src/gateway/events/Connection.ts
index bc74dee6..9b132d4b 100644 --- a/src/gateway/events/Connection.ts +++ b/src/gateway/events/Connection.ts
@@ -72,6 +72,13 @@ export async function Connection( ); } + if (request.headers.cookie?.split("; ").find(x => x.startsWith("__sb_sessid="))) { + socket.fingerprint = request.headers.cookie + .split("; ") + .find((x) => x.startsWith("__sb_sessid=")) + ?.split("=")[1]; + } + //Create session ID when the connection is opened. This allows gateway dump to group the initial websocket messages with the rest of the conversation. const session_id = genSessionId(); socket.session_id = session_id; //Set the session of the WebSocket object diff --git a/src/gateway/listener/listener.ts b/src/gateway/listener/listener.ts
index d7c3d23e..f76adfb2 100644 --- a/src/gateway/listener/listener.ts +++ b/src/gateway/listener/listener.ts
@@ -29,6 +29,7 @@ import { Message, NewUrlUserSignatureData, GuildMemberAddEvent, + Ban, } from "@spacebar/util"; import { OPCODES } from "../util/Constants"; import { Send } from "../util/Send"; @@ -36,7 +37,7 @@ import { WebSocket } from "@spacebar/gateway"; import { Channel as AMQChannel } from "amqplib"; import { Recipient } from "@spacebar/util"; import * as console from "node:console"; -import { PublicMember, RelationshipType } from "@spacebar/schemas" +import { PublicMember, RelationshipType } from "@spacebar/schemas"; import { bgRedBright } from "picocolors"; // TODO: close connection on Invalidated Token @@ -46,10 +47,7 @@ import { bgRedBright } from "picocolors"; // Sharding: calculate if the current shard id matches the formula: shard_id = (guild_id >> 22) % num_shards // https://discord.com/developers/docs/topics/gateway#sharding -export function handlePresenceUpdate( - this: WebSocket, - { event, acknowledge, data }: EventOpts, -) { +export function handlePresenceUpdate(this: WebSocket, { event, acknowledge, data }: EventOpts) { acknowledge?.(); if (event === EVENTEnum.PresenceUpdate) { return Send(this, { @@ -92,32 +90,24 @@ export async function setupListener(this: WebSocket) { this.listen_options = opts; const consumer = consume.bind(this); + const handleChannelError = (err: unknown) => { + console.error(`[RabbitMQ] [user-${this.user_id}] Channel Error (Handled):`, err); + }; + console.log("[RabbitMQ] setupListener: open for ", this.user_id); if (RabbitMQ.connection) { - console.log( - "[RabbitMQ] setupListener: opts.channel = ", - typeof opts.channel, - "with channel id", - opts.channel?.ch, - ); + console.log("[RabbitMQ] setupListener: opts.channel = ", typeof opts.channel, "with channel id", opts.channel?.ch); opts.channel = await RabbitMQ.connection.createChannel(); + + opts.channel.on("error", handleChannelError); opts.channel.queues = {}; - console.log( - "[RabbitMQ] channel created: ", - typeof opts.channel, - "with channel id", - opts.channel?.ch, - ); + console.log("[RabbitMQ] channel created: ", typeof opts.channel, "with channel id", opts.channel?.ch); } this.events[this.user_id] = await listenEvent(this.user_id, consumer, opts); relationships.forEach(async (relationship) => { - this.events[relationship.to_id] = await listenEvent( - relationship.to_id, - handlePresenceUpdate.bind(this), - opts, - ); + this.events[relationship.to_id] = await listenEvent(relationship.to_id, handlePresenceUpdate.bind(this), opts); }); dm_channels.forEach(async (channel) => { @@ -130,33 +120,27 @@ export async function setupListener(this: WebSocket) { this.events[guild.id] = await listenEvent(guild.id, consumer, opts); guild.channels.forEach(async (channel) => { - if ( - permission - .overwriteChannel(channel.permission_overwrites ?? []) - .has("VIEW_CHANNEL") - ) { - this.events[channel.id] = await listenEvent( - channel.id, - consumer, - opts, - ); + if (permission.overwriteChannel(channel.permission_overwrites ?? []).has("VIEW_CHANNEL")) { + this.events[channel.id] = await listenEvent(channel.id, consumer, opts); } }); }); - this.once("close", () => { - console.log( - "[RabbitMQ] setupListener: close for", - this.user_id, - "=", - typeof opts.channel, - "with channel id", - opts.channel?.ch, + this.once("close", async () => { + console.log("[RabbitMQ] setupListener: close for", this.user_id, "=", typeof opts.channel, "with channel id", opts.channel?.ch); + + // wait for event consumer cancellation + await Promise.all( + Object.values(this.events).map((x) => { + if (x) return x(); + else return Promise.resolve(); + }), ); - if (opts.channel) opts.channel.close(); - else { - Object.values(this.events).forEach((x) => x?.()); - Object.values(this.member_events).forEach((x) => x()); + await Promise.all(Object.values(this.member_events).map((x) => x())); + + if (opts.channel) { + await opts.channel.close(); + opts.channel.off("error", handleChannelError); } }); } @@ -180,11 +164,7 @@ async function consume(this: WebSocket, opts: EventOpts) { break; case "GUILD_MEMBER_ADD": if (this.member_events[data.user.id]) break; // already subscribed - this.member_events[data.user.id] = await listenEvent( - data.user.id, - handlePresenceUpdate.bind(this), - this.listen_options, - ); + this.member_events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); break; case "GUILD_MEMBER_UPDATE": if (!this.member_events[data.user.id]) break; @@ -195,34 +175,32 @@ async function consume(this: WebSocket, opts: EventOpts) { case "GUILD_DELETE": this.events[id]?.(); delete this.events[id]; + if (event === "GUILD_DELETE" && this.ipAddress) { + const ban = await Ban.findOne({ + where: { guild_id: id, user_id: this.user_id }, + }); + + if (ban) { + ban.ip = this.ipAddress || undefined; + await ban.save(); + } + } break; case "CHANNEL_CREATE": - if ( - !permission - .overwriteChannel(data.permission_overwrites) - .has("VIEW_CHANNEL") - ) { + if (!permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { return; } this.events[id] = await listenEvent(id, consumer, listenOpts); break; case "RELATIONSHIP_ADD": - this.events[data.user.id] = await listenEvent( - data.user.id, - handlePresenceUpdate.bind(this), - this.listen_options, - ); + this.events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); break; case "GUILD_CREATE": this.events[id] = await listenEvent(id, consumer, listenOpts); break; case "CHANNEL_UPDATE": { const exists = this.events[id]; - if ( - permission - .overwriteChannel(data.permission_overwrites) - .has("VIEW_CHANNEL") - ) { + if (permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { if (exists) break; this.events[id] = await listenEvent(id, consumer, listenOpts); } else { @@ -294,20 +272,19 @@ async function consume(this: WebSocket, opts: EventOpts) { case "MESSAGE_UPDATE": // console.log(this.request) if (data["attachments"]) - data["attachments"] = - Message.prototype.withSignedAttachments.call( - data, - new NewUrlUserSignatureData({ - ip: this.ipAddress, - userAgent: this.userAgent, - }), - ).attachments; + data["attachments"] = Message.prototype.withSignedAttachments.call( + data, + new NewUrlUserSignatureData({ + ip: this.ipAddress, + userAgent: this.userAgent, + }), + ).attachments; break; default: break; } - if(event === "GUILD_MEMBER_ADD") { + if (event === "GUILD_MEMBER_ADD") { if ((data as PublicMember).roles === undefined || (data as PublicMember).roles === null) { console.log(bgRedBright("[Gateway]"), "[GUILD_MEMBER_ADD] roles is undefined, setting to empty array!", opts.origin ?? "(Event origin not defined)", data); (data as PublicMember).roles = []; diff --git a/src/gateway/util/WebSocket.ts b/src/gateway/util/WebSocket.ts
index ea6f0701..d0a42eaf 100644 --- a/src/gateway/util/WebSocket.ts +++ b/src/gateway/util/WebSocket.ts
@@ -33,6 +33,7 @@ export interface WebSocket extends WS { compress?: "zlib-stream" | "zstd-stream"; ipAddress?: string; userAgent?: string; // for cdn request signing + fingerprint?: string; shard_count?: bigint; shard_id?: bigint; deflate?: Deflate; @@ -44,8 +45,8 @@ export interface WebSocket extends WS { intents: Intents; sequence: number; permissions: Record<string, Permissions>; - events: Record<string, undefined | (() => unknown)>; - member_events: Record<string, () => unknown>; + events: Record<string, undefined | (() => Promise<unknown>)>; + member_events: Record<string, () => Promise<unknown>>; listen_options: ListenEventOpts; capabilities?: Capabilities; large_threshold: number; diff --git a/src/schemas/api/users/InstanceUserDeleteSchema.ts b/src/schemas/api/users/InstanceUserDeleteSchema.ts new file mode 100644
index 00000000..48c9fde8 --- /dev/null +++ b/src/schemas/api/users/InstanceUserDeleteSchema.ts
@@ -0,0 +1,6 @@ +import { ConnectedAccount } from "@spacebar/util"; + +export type InstanceUserDeleteSchema = InstanceUserDeleteSchemaContent | undefined; //unsure if this a correct way to make the body optional +export interface InstanceUserDeleteSchemaContent { + reason?: string; +} diff --git a/src/schemas/api/users/index.ts b/src/schemas/api/users/index.ts
index a95a3282..5c507121 100644 --- a/src/schemas/api/users/index.ts +++ b/src/schemas/api/users/index.ts
@@ -16,6 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ export * from "./ConnectedAccount"; +export * from "./InstanceUserDeleteSchema"; export * from "./Member"; export * from "./User"; export * from "./UserSettings"; \ No newline at end of file diff --git a/src/schemas/uncategorised/BanCreateSchema.ts b/src/schemas/uncategorised/BanCreateSchema.ts
index 6d30ca22..8f349f4b 100644 --- a/src/schemas/uncategorised/BanCreateSchema.ts +++ b/src/schemas/uncategorised/BanCreateSchema.ts
@@ -17,7 +17,7 @@ */ export interface BanCreateSchema { - delete_message_seconds?: string; - delete_message_days?: string; + delete_message_seconds?: number; + delete_message_days?: number; reason?: string; } diff --git a/src/util/config/types/SecurityConfiguration.ts b/src/util/config/types/SecurityConfiguration.ts
index ae1c6cb8..1f3a1918 100644 --- a/src/util/config/types/SecurityConfiguration.ts +++ b/src/util/config/types/SecurityConfiguration.ts
@@ -24,7 +24,7 @@ export class SecurityConfiguration { twoFactor: TwoFactorConfiguration = new TwoFactorConfiguration(); autoUpdate: boolean | number = true; requestSignature: string = crypto.randomBytes(32).toString("base64"); - jwtSecret: string = crypto.randomBytes(256).toString("base64"); + jwtSecret: string | null = null; // header to get the real user ip address // X-Forwarded-For for nginx/reverse proxies // CF-Connecting-IP for cloudflare @@ -36,7 +36,7 @@ export class SecurityConfiguration { // https://docs.abuseipdb.com/#api-daily-rate-limits abuseipdbBlacklistRatelimit: number = 5; abuseipdbConfidenceScoreTreshold: number = 50; - ipdataApiKey: string | null = "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9"; // isnt even valid anymore it seems? + ipdataApiKey: string | null = null; mfaBackupCodeCount: number = 10; statsWorldReadable: boolean = true; defaultRegistrationTokenExpiration: number = 1000 * 60 * 60 * 24 * 7; //1 week diff --git a/src/util/entities/BackupCodes.ts b/src/util/entities/BackupCodes.ts
index 31c76343..a0c9ba4b 100644 --- a/src/util/entities/BackupCodes.ts +++ b/src/util/entities/BackupCodes.ts
@@ -20,6 +20,7 @@ import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; import crypto from "crypto"; +import { Config } from "../util"; @Entity({ name: "backup_codes", @@ -41,7 +42,7 @@ export class BackupCode extends BaseClass { export function generateMfaBackupCodes(user_id: string) { const backup_codes: BackupCode[] = []; - for (let i = 0; i < 10; i++) { + for (let i = 0; i < Config.get().security.mfaBackupCodeCount; i++) { const code = BackupCode.create({ user: { id: user_id }, code: crypto.randomBytes(4).toString("hex"), // 8 characters diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts
index 78242044..e21669ea 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts
@@ -379,13 +379,15 @@ export class Channel extends BaseClass { // TODO Delete attachments from the CDN for messages in the channel await Channel.delete({ id: channel.id }); - const guild = await Guild.findOneOrFail({ - where: { id: channel.guild_id }, - select: { channel_ordering: true }, - }); + if (channel.guild_id) { + const guild = await Guild.findOneOrFail({ + where: { id: channel.guild_id }, + select: { channel_ordering: true }, + }); - const updatedOrdering = guild.channel_ordering.filter((id) => id != channel.id); - await Guild.update({ id: channel.guild_id }, { channel_ordering: updatedOrdering }); + const updatedOrdering = guild.channel_ordering.filter((id) => id != channel.id); + await Guild.update({ id: channel.guild_id }, { channel_ordering: updatedOrdering }); + } } static async calculatePosition(channel_id: string, guild_id: string, guild?: Guild) { diff --git a/src/util/entities/InstanceBan.ts b/src/util/entities/InstanceBan.ts new file mode 100644
index 00000000..08100a9f --- /dev/null +++ b/src/util/entities/InstanceBan.ts
@@ -0,0 +1,111 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors + + 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/>. +*/ + +import { Column, CreateDateColumn, Entity, FindOptionsWhere, JoinColumn, ManyToOne, OneToOne, RelationId } from "typeorm"; +import { BaseClass } from "./BaseClass"; +import { Team } from "./Team"; +import { User } from "./User"; +import { Guild } from "./Guild"; + +@Entity({ + name: "instance_bans", +}) +export class InstanceBan extends BaseClass { + @Column({ type: "bigint" }) + @CreateDateColumn() + created_at: Date = new Date(); + + @Column() + reason: string; + + @Column({ nullable: true }) + user_id?: string; + + @Column({ nullable: true }) + fingerprint?: string; + + @Column({ nullable: true }) + ip_address?: string; + + // chain of trust type tracking + + @Column({ default: false }) + is_allowlisted: boolean = false; + + @Column({ default: false }) + is_from_other_instance_ban: boolean = false; + + @Column({ nullable: true }) + @RelationId((instance_ban: InstanceBan) => instance_ban.origin_instance_ban) + origin_instance_ban_id?: string; + + @JoinColumn({ name: "origin_instance_ban_id" }) + @OneToOne(() => InstanceBan, { nullable: true, onDelete: "SET NULL" }) + origin_instance_ban?: InstanceBan; + + static async findInstanceBans(opts: { userId?: string; ipAddress?: string; fingerprint?: string; propagateBan?: boolean }) { + const optionalChecks: FindOptionsWhere<InstanceBan>[] = [{ user_id: opts.userId }]; + if (opts?.ipAddress) optionalChecks.push({ ip_address: opts.ipAddress }); + if (opts?.fingerprint) optionalChecks.push({ fingerprint: opts.fingerprint }); + const instanceBans = await InstanceBan.find({ where: optionalChecks }); + + const banReasons = []; + for (const ban of instanceBans) { + if (ban.is_allowlisted) continue; + if (opts?.fingerprint && ban.fingerprint === opts.fingerprint) banReasons.push("fingerprint"); + if (opts?.ipAddress && ban.ip_address === opts.ipAddress) banReasons.push("ipAddress"); + if (opts?.userId && ban.user_id === opts?.userId) banReasons.push("userId"); + } + + const banViralityPromises: Promise<InstanceBan>[] = []; + if (opts.propagateBan && banReasons.length > 0) { + if (opts?.ipAddress && !instanceBans.find((b) => b.ip_address === opts.ipAddress)) + banViralityPromises.push( + InstanceBan.create({ + user_id: opts.userId, + ip_address: opts.ipAddress, + reason: "Propagated from other instance ban", + is_from_other_instance_ban: true, + origin_instance_ban: instanceBans[0], + }).save(), + ); + if (opts?.fingerprint && !instanceBans.find((b) => b.fingerprint === opts.fingerprint)) + banViralityPromises.push( + InstanceBan.create({ + user_id: opts.userId, + fingerprint: opts.fingerprint, + reason: "Propagated from other instance ban", + is_from_other_instance_ban: true, + origin_instance_ban: instanceBans[0], + }).save(), + ); + if (opts?.userId && !instanceBans.find((b) => b.user_id === opts.userId)) + banViralityPromises.push( + InstanceBan.create({ + user_id: opts.userId, + reason: "Propagated from other instance ban", + is_from_other_instance_ban: true, + origin_instance_ban: instanceBans[0], + }).save(), + ); + } + + await Promise.all(banViralityPromises); + return banReasons; + } +} diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts
index afa7c00e..375aaba4 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts
@@ -359,4 +359,18 @@ export class User extends BaseClass { return qry[0]; } + + async getDmChannels() { + const qry = await Channel.getRepository() + .createQueryBuilder("channel") + .leftJoinAndSelect("channel.recipients", "rcp") + .where("channel.type = :type", { type: ChannelType.DM }) + .andWhere("rcp.user_id = :user_id", { user_id: this.id }) + .groupBy("channel.id") + .addGroupBy("rcp.id") + .having("COUNT(rcp.id) = 2") + .getMany(); + + return qry; + } } diff --git a/src/util/entities/index.ts b/src/util/entities/index.ts
index 11494d00..0e67675a 100644 --- a/src/util/entities/index.ts +++ b/src/util/entities/index.ts
@@ -36,6 +36,7 @@ export * from "./EmbedCache"; export * from "./Emoji"; export * from "./Encryption"; export * from "./Guild"; +export * from "./InstanceBan"; export * from "./Invite"; export * from "./Member"; export * from "./Message"; diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts
index 24157efd..d0a60493 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts
@@ -219,6 +219,7 @@ export interface GuildBanAddEvent extends Event { data: { guild_id: string; user: User; + delete_message_secs?: number }; } diff --git a/src/util/migration/postgres-initial.ts b/src/util/migration/postgres-initial.ts new file mode 100644
index 00000000..76979417 --- /dev/null +++ b/src/util/migration/postgres-initial.ts
@@ -0,0 +1,727 @@ +// @prettier-ignore +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 Spacebar and Spacebar Contributors + + 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/>. +*/ + +import { MigrationInterface, QueryRunner } from "typeorm"; +export class initial0 implements MigrationInterface { + name = "initial0"; + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`CREATE TABLE public.applications ( + id character varying NOT NULL, + name character varying NOT NULL, + icon character varying, + description character varying, + summary character varying, + type text, + hook boolean NOT NULL, + bot_public boolean NOT NULL, + bot_require_code_grant boolean NOT NULL, + verify_key character varying NOT NULL, + flags integer NOT NULL, + redirect_uris text, + rpc_application_state integer, + store_application_state integer, + verification_state integer, + interactions_endpoint_url character varying, + integration_public boolean, + integration_require_code_grant boolean, + discoverability_state integer, + discovery_eligibility_flags integer, + tags text, + cover_image character varying, + install_params text, + terms_of_service_url character varying, + privacy_policy_url character varying, + owner_id character varying, + bot_user_id character varying, + team_id character varying + );`); + + await queryRunner.query(`CREATE TABLE public.attachments ( + id character varying NOT NULL, + filename character varying NOT NULL, + size integer NOT NULL, + url character varying NOT NULL, + proxy_url character varying NOT NULL, + height integer, + width integer, + content_type character varying, + message_id character varying + );`); + + await queryRunner.query(`CREATE TABLE public.audit_logs ( + id character varying NOT NULL, + user_id character varying, + action_type integer NOT NULL, + options text, + changes text NOT NULL, + reason character varying, + target_id character varying + );`); + + await queryRunner.query(`CREATE TABLE public.backup_codes ( + id character varying NOT NULL, + code character varying NOT NULL, + consumed boolean NOT NULL, + expired boolean NOT NULL, + user_id character varying + );`); + + await queryRunner.query(`CREATE TABLE public.bans ( + id character varying NOT NULL, + user_id character varying, + guild_id character varying, + executor_id character varying, + ip character varying NOT NULL, + reason character varying + );`); + + await queryRunner.query(`CREATE TABLE public.categories ( + id integer NOT NULL, + name character varying, + localizations text NOT NULL, + is_primary boolean + );`); + + await queryRunner.query(`CREATE TABLE public.channels ( + id character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + name character varying, + icon text, + type integer NOT NULL, + last_message_id character varying, + guild_id character varying, + parent_id character varying, + owner_id character varying, + last_pin_timestamp integer, + default_auto_archive_duration integer, + permission_overwrites text, + video_quality_mode integer, + bitrate integer, + user_limit integer, + nsfw boolean NOT NULL, + rate_limit_per_user integer, + topic character varying, + retention_policy_id character varying, + flags integer NOT NULL, + default_thread_rate_limit_per_user integer NOT NULL, + position integer NOT NULL DEFAULT 0 + );`); + + await queryRunner.query(`CREATE TABLE public.client_release ( + id character varying NOT NULL, + name character varying NOT NULL, + pub_date timestamp without time zone NOT NULL, + url character varying NOT NULL, + platform character varying NOT NULL, + enabled boolean NOT NULL, + notes character varying + );`); + + await queryRunner.query(`CREATE TABLE public.config ( + key character varying NOT NULL, + value text + );`); + + await queryRunner.query(`CREATE TABLE public.connected_accounts ( + id character varying NOT NULL, + external_id character varying NOT NULL, + user_id character varying, + friend_sync boolean NOT NULL, + name character varying NOT NULL, + revoked boolean NOT NULL, + show_activity integer NOT NULL, + type character varying NOT NULL, + verified boolean NOT NULL, + visibility integer NOT NULL, + integrations text NOT NULL, + metadata text, + metadata_visibility integer NOT NULL, + two_way_link boolean NOT NULL, + token_data text + );`); + + await queryRunner.query(`CREATE TABLE public.connection_config ( + key character varying NOT NULL, + value text + );`); + + await queryRunner.query(`CREATE TABLE public.embed_cache ( + id character varying NOT NULL, + url character varying NOT NULL, + embed text NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.emojis ( + id character varying NOT NULL, + animated boolean NOT NULL, + available boolean NOT NULL, + guild_id character varying NOT NULL, + user_id character varying, + managed boolean NOT NULL, + name character varying NOT NULL, + require_colons boolean NOT NULL, + roles text NOT NULL, + groups text + );`); + + await queryRunner.query(`CREATE TABLE public.guilds ( + id character varying NOT NULL, + afk_channel_id character varying, + afk_timeout integer, + banner character varying, + default_message_notifications integer, + description character varying, + discovery_splash character varying, + explicit_content_filter integer, + features text NOT NULL, + primary_category_id character varying, + icon character varying, + large boolean NOT NULL, + max_members integer, + max_presences integer, + max_video_channel_users integer, + member_count integer, + presence_count integer, + template_id character varying, + mfa_level integer, + name character varying NOT NULL, + owner_id character varying, + preferred_locale character varying, + premium_subscription_count integer, + premium_tier integer NOT NULL, + public_updates_channel_id character varying, + rules_channel_id character varying, + region character varying, + splash character varying, + system_channel_id character varying, + system_channel_flags integer, + unavailable boolean NOT NULL, + verification_level integer, + welcome_screen text NOT NULL, + widget_channel_id character varying, + widget_enabled boolean NOT NULL, + nsfw_level integer, + nsfw boolean NOT NULL, + parent character varying, + premium_progress_bar_enabled boolean + );`); + + await queryRunner.query(`CREATE TABLE public.invites ( + code character varying NOT NULL, + temporary boolean NOT NULL, + uses integer NOT NULL, + max_uses integer NOT NULL, + max_age integer NOT NULL, + created_at timestamp without time zone NOT NULL, + expires_at timestamp without time zone, + guild_id character varying, + channel_id character varying, + inviter_id character varying, + target_user_id character varying, + target_user_type integer, + vanity_url boolean, + flags integer NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.member_roles ( + index integer NOT NULL, + role_id character varying NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.members ( + index integer NOT NULL, + id character varying NOT NULL, + guild_id character varying NOT NULL, + nick character varying, + joined_at timestamp without time zone NOT NULL, + premium_since bigint, + deaf boolean NOT NULL, + mute boolean NOT NULL, + pending boolean NOT NULL, + settings text NOT NULL, + last_message_id character varying, + joined_by character varying, + avatar character varying, + banner character varying, + bio character varying NOT NULL, + theme_colors text, + pronouns character varying, + communication_disabled_until timestamp without time zone + );`); + + await queryRunner.query(`CREATE SEQUENCE public.members_index_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1;`); + + await queryRunner.query(`ALTER SEQUENCE public.members_index_seq OWNED BY public.members.index;`); + await queryRunner.query(`CREATE TABLE public.message_channel_mentions ( + "messagesId" character varying NOT NULL, + "channelsId" character varying NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.message_role_mentions ( + "messagesId" character varying NOT NULL, + "rolesId" character varying NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.message_stickers ( + "messagesId" character varying NOT NULL, + "stickersId" character varying NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.message_user_mentions ( + "messagesId" character varying NOT NULL, + "usersId" character varying NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.messages ( + id character varying NOT NULL, + channel_id character varying, + guild_id character varying, + author_id character varying, + member_id character varying, + webhook_id character varying, + application_id character varying, + content character varying, + "timestamp" timestamp without time zone DEFAULT now() NOT NULL, + edited_timestamp timestamp without time zone, + tts boolean, + mention_everyone boolean, + embeds text NOT NULL, + reactions text NOT NULL, + nonce text, + type integer NOT NULL, + activity text, + message_reference text, + interaction text, + components text, + message_reference_id character varying, + pinned boolean, + flags integer + );`); + + await queryRunner.query(`CREATE TABLE public.migrations ( + id integer NOT NULL, + "timestamp" bigint NOT NULL, + name character varying NOT NULL + );`); + + await queryRunner.query(`CREATE SEQUENCE public.migrations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1;`); + + await queryRunner.query(`ALTER SEQUENCE public.migrations_id_seq OWNED BY public.migrations.id;`); + await queryRunner.query(`CREATE TABLE public.notes ( + id character varying NOT NULL, + content character varying NOT NULL, + owner_id character varying, + target_id character varying + );`); + + await queryRunner.query(`CREATE TABLE public.rate_limits ( + id character varying NOT NULL, + executor_id character varying NOT NULL, + hits integer NOT NULL, + blocked boolean NOT NULL, + expires_at timestamp without time zone NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.read_states ( + id character varying NOT NULL, + channel_id character varying NOT NULL, + user_id character varying NOT NULL, + last_message_id character varying, + public_ack character varying, + notifications_cursor character varying, + last_pin_timestamp timestamp without time zone, + mention_count integer + );`); + + await queryRunner.query(`CREATE TABLE public.recipients ( + id character varying NOT NULL, + channel_id character varying NOT NULL, + user_id character varying NOT NULL, + closed boolean DEFAULT false NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.relationships ( + id character varying NOT NULL, + from_id character varying NOT NULL, + to_id character varying NOT NULL, + nickname character varying, + type integer NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.roles ( + id character varying NOT NULL, + guild_id character varying NOT NULL, + color integer NOT NULL, + hoist boolean NOT NULL, + managed boolean NOT NULL, + mentionable boolean NOT NULL, + name character varying NOT NULL, + permissions character varying NOT NULL, + "position" integer NOT NULL, + icon character varying, + unicode_emoji character varying, + tags text, + flags integer DEFAULT 0 NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.security_settings ( + id character varying NOT NULL, + guild_id character varying, + channel_id character varying, + encryption_permission_mask integer NOT NULL, + allowed_algorithms text NOT NULL, + current_algorithm character varying NOT NULL, + used_since_message character varying + );`); + + await queryRunner.query(`CREATE TABLE public.sessions ( + id character varying NOT NULL, + user_id character varying, + session_id character varying NOT NULL, + activities text, + client_info text NOT NULL, + status character varying NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.sticker_packs ( + id character varying NOT NULL, + name character varying NOT NULL, + description character varying, + banner_asset_id character varying, + cover_sticker_id character varying, + "coverStickerId" character varying + );`); + + await queryRunner.query(`CREATE TABLE public.stickers ( + id character varying NOT NULL, + name character varying NOT NULL, + description character varying, + available boolean, + tags character varying, + pack_id character varying, + guild_id character varying, + user_id character varying, + type integer NOT NULL, + format_type integer NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.team_members ( + id character varying NOT NULL, + membership_state integer NOT NULL, + permissions text NOT NULL, + team_id character varying, + user_id character varying + );`); + + await queryRunner.query(`CREATE TABLE public.teams ( + id character varying NOT NULL, + icon character varying, + name character varying NOT NULL, + owner_user_id character varying + );`); + + await queryRunner.query(`CREATE TABLE public.templates ( + id character varying NOT NULL, + code character varying NOT NULL, + name character varying NOT NULL, + description character varying, + usage_count integer, + creator_id character varying, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + source_guild_id character varying, + serialized_source_guild text NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.user_settings ( + index integer NOT NULL, + afk_timeout integer, + allow_accessibility_detection boolean, + animate_emoji boolean, + animate_stickers integer, + contact_sync_enabled boolean, + convert_emoticons boolean, + custom_status text, + default_guilds_restricted boolean, + detect_platform_accounts boolean, + developer_mode boolean, + disable_games_tab boolean, + enable_tts_command boolean, + explicit_content_filter integer, + friend_source_flags text, + gateway_connected boolean, + gif_auto_play boolean, + guild_folders text, + guild_positions text, + inline_attachment_media boolean, + inline_embed_media boolean, + locale character varying, + message_display_compact boolean, + native_phone_integration_enabled boolean, + render_embeds boolean, + render_reactions boolean, + restricted_guilds text, + show_current_game boolean, + status character varying, + stream_notifications_enabled boolean, + theme character varying, + timezone_offset integer + );`); + + await queryRunner.query(`CREATE SEQUENCE public.user_settings_index_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1;` + ); + + await queryRunner.query(`ALTER SEQUENCE public.user_settings_index_seq OWNED BY public.user_settings.index;`); + await queryRunner.query(`CREATE TABLE public.users ( + id character varying NOT NULL, + username character varying NOT NULL, + discriminator character varying NOT NULL, + avatar character varying, + accent_color integer, + banner character varying, + theme_colors text, + pronouns character varying, + phone character varying, + desktop boolean NOT NULL, + mobile boolean NOT NULL, + premium boolean NOT NULL, + premium_type integer NOT NULL, + bot boolean NOT NULL, + bio character varying NOT NULL, + system boolean NOT NULL, + nsfw_allowed boolean NOT NULL, + mfa_enabled boolean NOT NULL, + totp_secret character varying, + totp_last_ticket character varying, + created_at timestamp without time zone NOT NULL, + premium_since timestamp without time zone, + verified boolean NOT NULL, + disabled boolean NOT NULL, + deleted boolean NOT NULL, + email character varying, + flags bigint NOT NULL, + public_flags bigint NOT NULL, + purchased_flags bigint NOT NULL, + premium_usage_flags integer NOT NULL, + rights bigint NOT NULL, + data text NOT NULL, + fingerprints text NOT NULL, + extended_settings text NOT NULL, + "settingsIndex" integer + );`); + + await queryRunner.query(`CREATE TABLE public.valid_registration_tokens ( + token character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + expires_at timestamp without time zone NOT NULL + );`); + + await queryRunner.query(`CREATE TABLE public.voice_states ( + id character varying NOT NULL, + guild_id character varying, + channel_id character varying, + user_id character varying, + session_id character varying NOT NULL, + token character varying, + deaf boolean NOT NULL, + mute boolean NOT NULL, + self_deaf boolean NOT NULL, + self_mute boolean NOT NULL, + self_stream boolean, + self_video boolean NOT NULL, + suppress boolean NOT NULL, + request_to_speak_timestamp timestamp without time zone + );`); + + await queryRunner.query(`CREATE TABLE public.webhooks ( + id character varying NOT NULL, + type integer NOT NULL, + name character varying, + avatar character varying, + token character varying, + guild_id character varying, + channel_id character varying, + application_id character varying, + user_id character varying, + source_guild_id character varying + );`); + + await queryRunner.query(`ALTER TABLE ONLY public.members ALTER COLUMN index SET DEFAULT nextval('public.members_index_seq'::regclass);`); + await queryRunner.query(`ALTER TABLE ONLY public.migrations ALTER COLUMN id SET DEFAULT nextval('public.migrations_id_seq'::regclass);`); + await queryRunner.query(`ALTER TABLE ONLY public.user_settings ALTER COLUMN index SET DEFAULT nextval('public.user_settings_index_seq'::regclass);`); + await queryRunner.query(`ALTER TABLE ONLY public.embed_cache ADD CONSTRAINT "PK_0abb7581d4efc5a8b1361389c5e" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.messages ADD CONSTRAINT "PK_18325f38ae6de43878487eff986" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.audit_logs ADD CONSTRAINT "PK_1bb179d048bbc581caa3b013439" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.categories ADD CONSTRAINT "PK_24dbc6126a28ff948da33e97d3b" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.config ADD CONSTRAINT "PK_26489c99ddbb4c91631ef5cc791" PRIMARY KEY (key);`); + await queryRunner.query(`ALTER TABLE ONLY public.sessions ADD CONSTRAINT "PK_3238ef96f18b355b671619111bc" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.invites ADD CONSTRAINT "PK_33fd8a248db1cd832baa8aa25bf" PRIMARY KEY (code);`); + await queryRunner.query(`ALTER TABLE ONLY public.backup_codes ADD CONSTRAINT "PK_34ab957382dbc57e8fb53f1638f" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.rate_limits ADD CONSTRAINT "PK_3b4449f1f5fc167d921ee619f65" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.security_settings ADD CONSTRAINT "PK_4aec436cf81177ae97a1bcec3c7" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.client_release ADD CONSTRAINT "PK_4c4ea258342d2d6ba1be0a71a43" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.templates ADD CONSTRAINT "PK_515948649ce0bbbe391de702ae5" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.attachments ADD CONSTRAINT "PK_5e1f050bcff31e3084a1d662412" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.connected_accounts ADD CONSTRAINT "PK_70416f1da0be645bb31da01c774" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.message_role_mentions ADD CONSTRAINT "PK_74dba92cc300452a6e14b83ed44" PRIMARY KEY ("messagesId", "rolesId");`); + await queryRunner.query(`ALTER TABLE ONLY public.teams ADD CONSTRAINT "PK_7e5523774a38b08a6236d322403" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.message_channel_mentions ADD CONSTRAINT "PK_85cb45351497cd9d06a79ced65e" PRIMARY KEY ("messagesId", "channelsId");`); + await queryRunner.query(`ALTER TABLE ONLY public.migrations ADD CONSTRAINT "PK_8c82d7f526340ab734260ea46be" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.applications ADD CONSTRAINT "PK_938c0a27255637bde919591888f" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.member_roles ADD CONSTRAINT "PK_951c1d72a0fd1da8760b4a1fd66" PRIMARY KEY (index, role_id);`); + await queryRunner.query(`ALTER TABLE ONLY public.emojis ADD CONSTRAINT "PK_9adb96a675f555c6169bad7ba62" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.message_user_mentions ADD CONSTRAINT "PK_9b9b6e245ad47a48dbd7605d4fb" PRIMARY KEY ("messagesId", "usersId");`); + await queryRunner.query(`ALTER TABLE ONLY public.webhooks ADD CONSTRAINT "PK_9e8795cfc899ab7bdaa831e8527" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.sticker_packs ADD CONSTRAINT "PK_a27381efea0f876f5d3233af655" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.users ADD CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.bans ADD CONSTRAINT "PK_a4d6f261bffa4615c62d756566a" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.voice_states ADD CONSTRAINT "PK_ada09a50c134fad1369b510e3ce" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.notes ADD CONSTRAINT "PK_af6206538ea96c4e77e9f400c3d" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.members ADD CONSTRAINT "PK_b4a6b8c2478e5df990909c6cf6a" PRIMARY KEY (index);`); + await queryRunner.query(`ALTER TABLE ONLY public.relationships ADD CONSTRAINT "PK_ba20e2f5cf487408e08e4dcecaf" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.connection_config ADD CONSTRAINT "PK_bc0554f736ad71dde346549488a" PRIMARY KEY (key);`); + await queryRunner.query(`ALTER TABLE ONLY public.channels ADD CONSTRAINT "PK_bc603823f3f741359c2339389f9" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.roles ADD CONSTRAINT "PK_c1433d71a4838793a49dcad46ab" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.team_members ADD CONSTRAINT "PK_ca3eae89dcf20c9fd95bf7460aa" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.recipients ADD CONSTRAINT "PK_de8fc5a9c364568f294798fe1e9" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.valid_registration_tokens ADD CONSTRAINT "PK_e0f5c8e3fcefe3134a092c50485" PRIMARY KEY (token);`); + await queryRunner.query(`ALTER TABLE ONLY public.stickers ADD CONSTRAINT "PK_e1dafa4063a5532645cc2810374" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.read_states ADD CONSTRAINT "PK_e6956a804978f01b713b1ed58e2" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.guilds ADD CONSTRAINT "PK_e7e7f2a51bd6d96a9ac2aa560f9" PRIMARY KEY (id);`); + await queryRunner.query(`ALTER TABLE ONLY public.user_settings ADD CONSTRAINT "PK_e81f8bb92802737337d35c00981" PRIMARY KEY (index);`); + await queryRunner.query(`ALTER TABLE ONLY public.message_stickers ADD CONSTRAINT "PK_ed820c4093d0b8cd1d2bcf66087" PRIMARY KEY ("messagesId", "stickersId");`); + await queryRunner.query(`ALTER TABLE ONLY public.users ADD CONSTRAINT "REL_0c14beb78d8c5ccba66072adbc" UNIQUE ("settingsIndex");`); + await queryRunner.query(`ALTER TABLE ONLY public.applications ADD CONSTRAINT "REL_2ce5a55796fe4c2f77ece57a64" UNIQUE (bot_user_id);`); + await queryRunner.query(`ALTER TABLE ONLY public.notes ADD CONSTRAINT "UQ_74e6689b9568cc965b8bfc9150b" UNIQUE (owner_id, target_id);`); + await queryRunner.query(`ALTER TABLE ONLY public.templates ADD CONSTRAINT "UQ_be38737bf339baf63b1daeffb55" UNIQUE (code);`); + await queryRunner.query(`CREATE INDEX "IDX_05535bc695e9f7ee104616459d" ON public.messages USING btree (author_id);`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0abf8b443321bd3cf7f81ee17a" ON public.read_states USING btree (channel_id, user_id);`); + await queryRunner.query(`CREATE INDEX "IDX_29d63eb1a458200851bc37d074" ON public.message_role_mentions USING btree ("rolesId");`); + await queryRunner.query(`CREATE INDEX "IDX_2a27102ecd1d81b4582a436092" ON public.message_channel_mentions USING btree ("messagesId");`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_3ed7a60fb7dbe04e1ba9332a8b" ON public.messages USING btree (channel_id, id);`); + await queryRunner.query(`CREATE INDEX "IDX_40bb6f23e7cc133292e92829d2" ON public.message_stickers USING btree ("messagesId");`); + await queryRunner.query(`CREATE INDEX "IDX_5d7ddc8a5f9c167f548625e772" ON public.member_roles USING btree (index);`); + await queryRunner.query(`CREATE INDEX "IDX_86b9109b155eb70c0a2ca3b4b6" ON public.messages USING btree (channel_id);`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a0b2ff0a598df0b0d055934a17" ON public.relationships USING btree (from_id, to_id);`); + await queryRunner.query(`CREATE INDEX "IDX_a343387fc560ef378760681c23" ON public.message_user_mentions USING btree ("messagesId");`); + await queryRunner.query(`CREATE INDEX "IDX_a8242cf535337a490b0feaea0b" ON public.message_role_mentions USING btree ("messagesId");`); + await queryRunner.query(`CREATE INDEX "IDX_b831eb18ceebd28976239b1e2f" ON public.message_user_mentions USING btree ("usersId");`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_bb2bf9386ac443afbbbf9f12d3" ON public.members USING btree (id, guild_id);`); + await queryRunner.query(`CREATE INDEX "IDX_bdb8c09e1464cabf62105bf4b9" ON public.message_channel_mentions USING btree ("channelsId");`); + await queryRunner.query(`CREATE INDEX "IDX_e22a70819d07659c7a71c112a1" ON public.message_stickers USING btree ("stickersId");`); + await queryRunner.query(`CREATE INDEX "IDX_e9080e7a7997a0170026d5139c" ON public.member_roles USING btree (role_id);`); + await queryRunner.query(`ALTER TABLE ONLY public.voice_states ADD CONSTRAINT "FK_03779ef216d4b0358470d9cb748" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.messages ADD CONSTRAINT "FK_05535bc695e9f7ee104616459d3" FOREIGN KEY (author_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.bans ADD CONSTRAINT "FK_07ad88c86d1f290d46748410d58" FOREIGN KEY (executor_id) REFERENCES public.users(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.sessions ADD CONSTRAINT "FK_085d540d9f418cfbdc7bd55bb19" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.users ADD CONSTRAINT "FK_0c14beb78d8c5ccba66072adbc7" FOREIGN KEY ("settingsIndex") REFERENCES public.user_settings(index);`); + await queryRunner.query(`ALTER TABLE ONLY public.webhooks ADD CONSTRAINT "FK_0d523f6f997c86e052c49b1455f" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.invites ADD CONSTRAINT "FK_11a0d394f8fc649c19ce5f16b59" FOREIGN KEY (target_user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.teams ADD CONSTRAINT "FK_13f00abf7cb6096c43ecaf8c108" FOREIGN KEY (owner_user_id) REFERENCES public.users(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.invites ADD CONSTRAINT "FK_15c35422032e0b22b4ada95f48f" FOREIGN KEY (inviter_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.members ADD CONSTRAINT "FK_16aceddd5b89825b8ed6029ad1c" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.stickers ADD CONSTRAINT "FK_193d551d852aca5347ef5c9f205" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.read_states ADD CONSTRAINT "FK_195f92e4dd1254a4e348c043763" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.notes ADD CONSTRAINT "FK_23e08e5b4481711d573e1abecdc" FOREIGN KEY (target_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.members ADD CONSTRAINT "FK_28b53062261b996d9c99fa12404" FOREIGN KEY (id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.message_role_mentions ADD CONSTRAINT "FK_29d63eb1a458200851bc37d074b" FOREIGN KEY ("rolesId") REFERENCES public.roles(id) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.message_channel_mentions ADD CONSTRAINT "FK_2a27102ecd1d81b4582a4360921" FOREIGN KEY ("messagesId") REFERENCES public.messages(id) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.applications ADD CONSTRAINT "FK_2ce5a55796fe4c2f77ece57a647" FOREIGN KEY (bot_user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.recipients ADD CONSTRAINT "FK_2f18ee1ba667f233ae86c0ea60e" FOREIGN KEY (channel_id) REFERENCES public.channels(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.channels ADD CONSTRAINT "FK_3274522d14af40540b1a883fc80" FOREIGN KEY (parent_id) REFERENCES public.channels(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.channels ADD CONSTRAINT "FK_3873ed438575cce703ecff4fc7b" FOREIGN KEY (owner_id) REFERENCES public.users(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.webhooks ADD CONSTRAINT "FK_3a285f4f49c40e0706d3018bc9f" FOREIGN KEY (source_guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.audit_logs ADD CONSTRAINT "FK_3cd01cd3ae7aab010310d96ac8e" FOREIGN KEY (target_id) REFERENCES public.users(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.invites ADD CONSTRAINT "FK_3f4939aa1461e8af57fea3fb05d" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.message_stickers ADD CONSTRAINT "FK_40bb6f23e7cc133292e92829d28" FOREIGN KEY ("messagesId") REFERENCES public.messages(id) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.read_states ADD CONSTRAINT "FK_40da2fca4e0eaf7a23b5bfc5d34" FOREIGN KEY (channel_id) REFERENCES public.channels(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.templates ADD CONSTRAINT "FK_445d00eaaea0e60a017a5ed0c11" FOREIGN KEY (source_guild_id) REFERENCES public.guilds(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.sticker_packs ADD CONSTRAINT "FK_448fafba4355ee1c837bbc865f1" FOREIGN KEY ("coverStickerId") REFERENCES public.stickers(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.webhooks ADD CONSTRAINT "FK_487a7af59d189f744fe394368fc" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.emojis ADD CONSTRAINT "FK_4b988e0db89d94cebcf07f598cc" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.bans ADD CONSTRAINT "FK_5999e8e449f80a236ff72023559" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.messages ADD CONSTRAINT "FK_5d3ec1cb962de6488637fd779d6" FOREIGN KEY (application_id) REFERENCES public.applications(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.member_roles ADD CONSTRAINT "FK_5d7ddc8a5f9c167f548625e772e" FOREIGN KEY (index) REFERENCES public.members(index) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.voice_states ADD CONSTRAINT "FK_5fe1d5f931a67e85039c640001b" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.recipients ADD CONSTRAINT "FK_6157e8b6ba4e6e3089616481fe2" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.messages ADD CONSTRAINT "FK_61a92bb65b302a76d9c1fcd3174" FOREIGN KEY (message_reference_id) REFERENCES public.messages(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.attachments ADD CONSTRAINT "FK_623e10eec51ada466c5038979e3" FOREIGN KEY (message_id) REFERENCES public.messages(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.invites ADD CONSTRAINT "FK_6a15b051fe5050aa00a4b9ff0f6" FOREIGN KEY (channel_id) REFERENCES public.channels(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.backup_codes ADD CONSTRAINT "FK_70066ea80d2f4b871beda32633b" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.messages ADD CONSTRAINT "FK_86b9109b155eb70c0a2ca3b4b6d" FOREIGN KEY (channel_id) REFERENCES public.channels(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.guilds ADD CONSTRAINT "FK_8d450b016dc8bec35f36729e4b0" FOREIGN KEY (public_updates_channel_id) REFERENCES public.channels(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.stickers ADD CONSTRAINT "FK_8f4ee73f2bb2325ff980502e158" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.guilds ADD CONSTRAINT "FK_95828668aa333460582e0ca6396" FOREIGN KEY (rules_channel_id) REFERENCES public.channels(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.relationships ADD CONSTRAINT "FK_9af4194bab1250b1c584ae4f1d7" FOREIGN KEY (from_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.relationships ADD CONSTRAINT "FK_9c7f6b98a9843b76dce1b0c878b" FOREIGN KEY (to_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.guilds ADD CONSTRAINT "FK_9d1d665379eefde7876a17afa99" FOREIGN KEY (widget_channel_id) REFERENCES public.channels(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.bans ADD CONSTRAINT "FK_9d3ab7dd180ebdd245cdb66ecad" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.voice_states ADD CONSTRAINT "FK_9f8d389866b40b6657edd026dd4" FOREIGN KEY (channel_id) REFERENCES public.channels(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.message_user_mentions ADD CONSTRAINT "FK_a343387fc560ef378760681c236" FOREIGN KEY ("messagesId") REFERENCES public.messages(id) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.applications ADD CONSTRAINT "FK_a36ed02953077f408d0f3ebc424" FOREIGN KEY (team_id) REFERENCES public.teams(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.message_role_mentions ADD CONSTRAINT "FK_a8242cf535337a490b0feaea0b4" FOREIGN KEY ("messagesId") REFERENCES public.messages(id) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.messages ADD CONSTRAINT "FK_b0525304f2262b7014245351c76" FOREIGN KEY (member_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.messages ADD CONSTRAINT "FK_b193588441b085352a4c0109423" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.message_user_mentions ADD CONSTRAINT "FK_b831eb18ceebd28976239b1e2f8" FOREIGN KEY ("usersId") REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.audit_logs ADD CONSTRAINT "FK_bd2726fd31b35443f2245b93ba0" FOREIGN KEY (user_id) REFERENCES public.users(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.message_channel_mentions ADD CONSTRAINT "FK_bdb8c09e1464cabf62105bf4b9d" FOREIGN KEY ("channelsId") REFERENCES public.channels(id) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.channels ADD CONSTRAINT "FK_c253dafe5f3a03ec00cd8fb4581" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.team_members ADD CONSTRAINT "FK_c2bf4967c8c2a6b845dadfbf3d4" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.roles ADD CONSTRAINT "FK_c32c1ab1c4dc7dcb0278c4b1b8b" FOREIGN KEY (guild_id) REFERENCES public.guilds(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.webhooks ADD CONSTRAINT "FK_c3e5305461931763b56aa905f1c" FOREIGN KEY (application_id) REFERENCES public.applications(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.guilds ADD CONSTRAINT "FK_cfc3d3ad260f8121c95b31a1fce" FOREIGN KEY (system_channel_id) REFERENCES public.channels(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.templates ADD CONSTRAINT "FK_d7374b7f8f5fbfdececa4fb62e1" FOREIGN KEY (creator_id) REFERENCES public.users(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.webhooks ADD CONSTRAINT "FK_df528cf77e82f8032230e7e37d8" FOREIGN KEY (channel_id) REFERENCES public.channels(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.message_stickers ADD CONSTRAINT "FK_e22a70819d07659c7a71c112a1f" FOREIGN KEY ("stickersId") REFERENCES public.stickers(id) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.guilds ADD CONSTRAINT "FK_e2a2f873a64a5cf62526de42325" FOREIGN KEY (template_id) REFERENCES public.templates(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.applications ADD CONSTRAINT "FK_e57508958bf92b9d9d25231b5e8" FOREIGN KEY (owner_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.stickers ADD CONSTRAINT "FK_e7cfa5cefa6661b3fb8fda8ce69" FOREIGN KEY (pack_id) REFERENCES public.sticker_packs(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.member_roles ADD CONSTRAINT "FK_e9080e7a7997a0170026d5139c1" FOREIGN KEY (role_id) REFERENCES public.roles(id) ON UPDATE CASCADE ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.connected_accounts ADD CONSTRAINT "FK_f47244225a6a1eac04a3463dd90" FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.guilds ADD CONSTRAINT "FK_f591a66b8019d87b0fe6c12dad6" FOREIGN KEY (afk_channel_id) REFERENCES public.channels(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.messages ADD CONSTRAINT "FK_f83c04bcf1df4e5c0e7a52ed348" FOREIGN KEY (webhook_id) REFERENCES public.webhooks(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.notes ADD CONSTRAINT "FK_f9e103f8ae67cb1787063597925" FOREIGN KEY (owner_id) REFERENCES public.users(id) ON DELETE CASCADE;`); + await queryRunner.query(`ALTER TABLE ONLY public.emojis ADD CONSTRAINT "FK_fa7ddd5f9a214e28ce596548421" FOREIGN KEY (user_id) REFERENCES public.users(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.guilds ADD CONSTRAINT "FK_fc1a451727e3643ca572a3bb394" FOREIGN KEY (owner_id) REFERENCES public.users(id);`); + await queryRunner.query(`ALTER TABLE ONLY public.team_members ADD CONSTRAINT "FK_fdad7d5768277e60c40e01cdcea" FOREIGN KEY (team_id) REFERENCES public.teams(id) ON DELETE CASCADE;`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + throw new Error("Can't revert this: just throw away your database lol"); + } +} + diff --git a/src/util/migration/postgres/1696420827239-guildChannelOrdering.ts b/src/util/migration/postgres/1696420827239-guildChannelOrdering.ts
index 6fc80ffd..1623f983 100644 --- a/src/util/migration/postgres/1696420827239-guildChannelOrdering.ts +++ b/src/util/migration/postgres/1696420827239-guildChannelOrdering.ts
@@ -34,7 +34,29 @@ export class guildChannelOrdering1696420827239 implements MigrationInterface { await queryRunner.query(`ALTER TABLE channels DROP COLUMN position`); } - public async down(): Promise<void> { - // don't care actually, sorry. + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE channels ADD position integer NOT NULL DEFAULT 0`); + + const guilds = await queryRunner.query( + `SELECT id, channel_ordering FROM guilds`, + undefined, + true, + ); + + for (const guild of guilds.records) { + const channel_ordering: string[] = JSON.parse(guild.channel_ordering); + + for (let i = 0; i < channel_ordering.length; i++) { + const channel_id = channel_ordering[i]; + await queryRunner.query( + `UPDATE channels SET position = $1 WHERE id = $2`, + [i, channel_id], + ); + } + } + + await queryRunner.query( + `ALTER TABLE guilds DROP COLUMN channel_ordering`, + ); } } diff --git a/src/util/migration/postgres/1713116476900-messageFlagsNotNull.ts b/src/util/migration/postgres/1713116476900-messageFlagsNotNull.ts
index 026b069b..543a7ce3 100644 --- a/src/util/migration/postgres/1713116476900-messageFlagsNotNull.ts +++ b/src/util/migration/postgres/1713116476900-messageFlagsNotNull.ts
@@ -16,8 +16,16 @@ export class MessageFlagsNotNull1713116476900 implements MigrationInterface { await queryRunner.query("ALTER TABLE messages DROP COLUMN flags_old;"); } - public async down(): Promise<void> { - // dont care - throw new Error("Migration down is not implemented."); + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + "ALTER TABLE messages RENAME COLUMN flags TO flags_new;", + ); + await queryRunner.query( + "ALTER TABLE messages ADD COLUMN flags integer;", + ); + await queryRunner.query( + "UPDATE messages SET flags = flags_new;", + ); + await queryRunner.query("ALTER TABLE messages DROP COLUMN flags_new;"); } } diff --git a/src/util/migration/postgres/1720628601997-badges.ts b/src/util/migration/postgres/1720628601997-badges.ts
index f7b9958b..0cecb6ea 100644 --- a/src/util/migration/postgres/1720628601997-badges.ts +++ b/src/util/migration/postgres/1720628601997-badges.ts
@@ -12,5 +12,6 @@ export class Badges1720628601997 implements MigrationInterface { public async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "badge_ids"`); + await queryRunner.query(`DROP TABLE "badges"`); } } diff --git a/src/util/migration/postgres/1752321571508-RoleColors.ts b/src/util/migration/postgres/1752321571508-RoleColors.ts
index 687142e3..beb42062 100644 --- a/src/util/migration/postgres/1752321571508-RoleColors.ts +++ b/src/util/migration/postgres/1752321571508-RoleColors.ts
@@ -5,6 +5,8 @@ export class RoleColors1752321571508 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`ALTER TABLE "roles" ADD "colors" text`); + await queryRunner.query(`UPDATE "roles" SET "colors" = jsonb_build_object('primary_color', "color") WHERE "colors" IS NULL`); + await queryRunner.query(`ALTER TABLE "roles" ALTER COLUMN "colors" SET NOT NULL`); } public async down(queryRunner: QueryRunner): Promise<void> { diff --git a/src/util/migration/postgres/1752342900886-RoleColorsSolidColor.ts b/src/util/migration/postgres/1752342900886-RoleColorsSolidColor.ts deleted file mode 100644
index 36c24a37..00000000 --- a/src/util/migration/postgres/1752342900886-RoleColorsSolidColor.ts +++ /dev/null
@@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RoleColorsSolidColor1752342900886 implements MigrationInterface { - name = 'RoleColorsSolidColor1752342900886' - - public async up(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(`UPDATE "roles" SET "colors" = jsonb_build_object('primary_color', "color") WHERE "colors" IS NULL`); - await queryRunner.query(`ALTER TABLE "roles" ALTER COLUMN "colors" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(`ALTER TABLE "roles" ALTER COLUMN "colors" DROP NOT NULL`); - } - -} diff --git a/src/util/migration/postgres/1752383879533-message_pinned_at.ts b/src/util/migration/postgres/1752383879533-message_pinned_at.ts
index 2e294aad..9f71f5fa 100644 --- a/src/util/migration/postgres/1752383879533-message_pinned_at.ts +++ b/src/util/migration/postgres/1752383879533-message_pinned_at.ts
@@ -14,12 +14,12 @@ export class MessagePinnedAt1752383879533 implements MigrationInterface { } public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query( - `ALTER TABLE "messages" DROP COLUMN "pinned_at"`, - ); await queryRunner.query(`ALTER TABLE "messages" ADD "pinned" boolean`); await queryRunner.query( `UPDATE "messages" SET "pinned" = true WHERE "pinned_at" IS NOT NULL`, ); + await queryRunner.query( + `ALTER TABLE "messages" DROP COLUMN "pinned_at"`, + ); } } diff --git a/src/util/migration/postgres/1761113394664-delete-bot-users-without-an-application.ts b/src/util/migration/postgres/1761113394664-delete-bot-users-without-an-application.ts
index 02f8a934..e3209927 100644 --- a/src/util/migration/postgres/1761113394664-delete-bot-users-without-an-application.ts +++ b/src/util/migration/postgres/1761113394664-delete-bot-users-without-an-application.ts
@@ -1,6 +1,7 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class DeleteBotUsersWithoutAnApplication1761113394664 implements MigrationInterface { + name = "DeleteBotUsersWithoutAnApplication1761113394664"; public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`DELETE FROM users WHERE bot = true AND id NOT IN (SELECT bot_user_id FROM applications);`); } diff --git a/src/util/migration/postgres/1762611552514-fix-gif-stickers-format_type.ts b/src/util/migration/postgres/1762611552514-fix-gif-stickers-format_type.ts
index 47de7506..0e17bf81 100644 --- a/src/util/migration/postgres/1762611552514-fix-gif-stickers-format_type.ts +++ b/src/util/migration/postgres/1762611552514-fix-gif-stickers-format_type.ts
@@ -1,6 +1,7 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class FixGifStickersFormatType1762611552514 implements MigrationInterface { + name = "FixGifStickersFormatType1762611552514"; public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`UPDATE "stickers" SET "format_type" = 4 WHERE "format_type" = 0;`); } diff --git a/src/util/migration/postgres/1765578570423-InstanceBanTable.ts b/src/util/migration/postgres/1765578570423-InstanceBanTable.ts new file mode 100644
index 00000000..0baa7dc9 --- /dev/null +++ b/src/util/migration/postgres/1765578570423-InstanceBanTable.ts
@@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class InstanceBanTable1765578570423 implements MigrationInterface { + name = 'InstanceBanTable1765578570423' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`CREATE TABLE "instance_bans" ("id" character varying NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "reason" character varying NOT NULL, "user_id" character varying, "fingerprint" character varying, "ip_address" character varying, "is_from_other_instance_ban" boolean NOT NULL DEFAULT false, "origin_instance_ban_id" character varying, CONSTRAINT "REL_0b02d18d0d830f160c921192a3" UNIQUE ("origin_instance_ban_id"), CONSTRAINT "PK_3aa6e80a6d325601054892b1340" PRIMARY KEY ("id"))`); + await queryRunner.query(`ALTER TABLE "instance_bans" ADD CONSTRAINT "FK_0b02d18d0d830f160c921192a30" FOREIGN KEY ("origin_instance_ban_id") REFERENCES "instance_bans"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "instance_bans" DROP CONSTRAINT "FK_0b02d18d0d830f160c921192a30"`); + await queryRunner.query(`DROP TABLE "instance_bans"`); + } + +} diff --git a/src/util/migration/postgres/1765587835846-InstanceBanAllowlist.ts b/src/util/migration/postgres/1765587835846-InstanceBanAllowlist.ts new file mode 100644
index 00000000..fd8da01a --- /dev/null +++ b/src/util/migration/postgres/1765587835846-InstanceBanAllowlist.ts
@@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class InstanceBanAllowlist1765587835846 implements MigrationInterface { + name = 'InstanceBanAllowlist1765587835846' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "instance_bans" ADD "is_allowlisted" boolean NOT NULL DEFAULT false`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "instance_bans" DROP COLUMN "is_allowlisted"`); + } + +} diff --git a/src/util/migration/postgres/1765665440000-DropDefaultIPDataKey.ts b/src/util/migration/postgres/1765665440000-DropDefaultIPDataKey.ts new file mode 100644
index 00000000..ad1b736f --- /dev/null +++ b/src/util/migration/postgres/1765665440000-DropDefaultIPDataKey.ts
@@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class DropDefaultIPDataKey1765665440000 implements MigrationInterface { + name = 'DropDefaultIPDataKey1765665440000' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`UPDATE "config" SET "value" = NULL WHERE "key" = 'security_ipdataApiKey' AND "value" = '"eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9"'`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`UPDATE "config" SET "value" = '"eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9"' WHERE "key" = 'security_ipdataApiKey' AND "value" IS NULL`); + } + +} diff --git a/src/util/util/Database.ts b/src/util/util/Database.ts
index 50763dd9..78712b9e 100644 --- a/src/util/util/Database.ts +++ b/src/util/util/Database.ts
@@ -22,6 +22,7 @@ import { green, red, yellow } from "picocolors"; import { DataSource } from "typeorm"; import { ConfigEntity } from "../entities/Config"; import { Migration } from "../entities/Migration"; +import fs from "fs"; // UUID extension option is only supported with postgres // We want to generate all id's with Snowflakes that's why we have our own BaseEntity class @@ -33,12 +34,9 @@ if (!process.env) { config({ quiet: true }); } -const dbConnectionString = - process.env.DATABASE || path.join(process.cwd(), "database.db"); +const dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db"); -export const DatabaseType = dbConnectionString.includes("://") - ? dbConnectionString.split(":")[0]?.replace("+srv", "") - : "sqlite"; +export const DatabaseType = dbConnectionString.includes("://") ? dbConnectionString.split(":")[0]?.replace("+srv", "") : "sqlite"; const isSqlite = DatabaseType.includes("sqlite"); export const DataSourceOptions = new DataSource({ @@ -69,11 +67,7 @@ export async function initDatabase(): Promise<DataSource> { if (dbConnection) return dbConnection; if (isSqlite) { - console.log( - `[Database] ${red( - `You are running sqlite! Please keep in mind that we recommend setting up a dedicated database!`, - )}`, - ); + console.log(`[Database] ${red(`You are running sqlite! Please keep in mind that we recommend setting up a dedicated database!`)}`); } if (!process.env.DB_SYNC) { @@ -94,6 +88,13 @@ export async function initDatabase(): Promise<DataSource> { dbConnection = await DataSourceOptions.initialize(); + if (DatabaseType === "sqlite") { + console.log(`[Database] ${yellow("Warning: SQLite is not supported. Forcing sync, this may lead to data loss!")}`); + await dbConnection.synchronize(); + console.log(`[Database] ${green("Connected")}`); + return dbConnection; + } + // Crude way of detecting if the migrations table exists. const dbExists = async () => { try { @@ -103,27 +104,17 @@ export async function initDatabase(): Promise<DataSource> { return false; } }; - if (!(await dbExists())) { - console.log( - "[Database] This appears to be a fresh database. Synchronising.", - ); - await dbConnection.synchronize(); - // On next start, typeorm will try to run all the migrations again from beginning. - // Manually insert every current migration to prevent this: - await Promise.all( - dbConnection.migrations.map((migration) => - Migration.insert({ - name: migration.name, - timestamp: Date.now(), - }), - ), - ); - } else { - console.log("[Database] Applying missing migrations, if any."); - await dbConnection.runMigrations(); + if (!(await dbExists())) { + console.log("[Database] This appears to be a fresh database. Running initial DDL."); + const qr = dbConnection.createQueryRunner(); + if (fs.existsSync(path.join(__dirname, "..", "migration", DatabaseType, "initial0.js"))) + await new (require(`../migration/${DatabaseType}-initial`).initial0)().up(qr); } + console.log("[Database] Applying missing migrations, if any."); + await dbConnection.runMigrations(); + console.log(`[Database] ${green("Connected")}`); return dbConnection; diff --git a/src/util/util/ElapsedTime.ts b/src/util/util/ElapsedTime.ts
index 518025c6..08f246a1 100644 --- a/src/util/util/ElapsedTime.ts +++ b/src/util/util/ElapsedTime.ts
@@ -68,4 +68,17 @@ export class ElapsedTime { get days(): number { return this.totalDays; } + + toString(): string { + // Format: "DD.HH:MM:SS.mmmuuuNNN", with days being optional + const daysPart = Math.floor(this.days) > 0 ? `${Math.floor(this.days)}.` : ""; + const hoursPart = Math.floor(this.hours).toString().padStart(2, "0"); + const minutesPart = Math.floor(this.minutes).toString().padStart(2, "0"); + const secondsPart = Math.floor(this.seconds).toString().padStart(2, "0"); + const millisecondsPart = Math.floor(this.milliseconds).toString().padStart(3, "0"); + const microsecondsPart = Math.floor(this.microseconds).toString().padStart(3, "0"); + const nanosecondsPart = Math.floor(this.nanoseconds).toString().padStart(3, "0"); + + return `${daysPart}${hoursPart}:${minutesPart}:${secondsPart}.${millisecondsPart}${microsecondsPart}${nanosecondsPart}`; + } } \ No newline at end of file diff --git a/src/util/util/Event.ts b/src/util/util/Event.ts
index f56d6664..6f2b0d9a 100644 --- a/src/util/util/Event.ts +++ b/src/util/util/Event.ts
@@ -20,31 +20,28 @@ import { Channel } from "amqplib"; import { RabbitMQ } from "./RabbitMQ"; import EventEmitter from "events"; import { EVENT, Event } from "../interfaces"; +import { randomUUID } from "crypto"; export const events = new EventEmitter(); export async function emitEvent(payload: Omit<Event, "created_at">) { - const id = (payload.guild_id || - payload.channel_id || - payload.user_id) as string; + const id = (payload.guild_id || payload.channel_id || payload.user_id) as string; if (!id) return console.error("event doesn't contain any id", payload); if (RabbitMQ.connection) { - const data = - typeof payload.data === "object" - ? JSON.stringify(payload.data) - : payload.data; // use rabbitmq for event transmission - await RabbitMQ.channel?.assertExchange(id, "fanout", { - durable: false, - }); + const data = typeof payload.data === "object" ? JSON.stringify(payload.data) : payload.data; // use rabbitmq for event transmission + const channel = await RabbitMQ.getSafeChannel(); + try { + await channel.assertExchange(id, "fanout", { + durable: false, + }); - // assertQueue isn't needed, because a queue will automatically created if it doesn't exist - const successful = RabbitMQ.channel?.publish( - id, - "", - Buffer.from(`${data}`), - { type: payload.event }, - ); - if (!successful) throw new Error("failed to send event"); + // assertQueue isn't needed, because a queue will automatically created if it doesn't exist + const successful = channel.publish(id, "", Buffer.from(`${data}`), { type: payload.event }); + if (!successful) throw new Error("failed to send event"); + } catch (e) { + // todo: should we retry publishng the event? + console.log("[RabbitMQ] ", e); + } } else if (process.env.EVENT_TRANSMISSION === "process") { process.send?.({ type: "event", event: payload, id } as ProcessEvent); } else { @@ -79,17 +76,11 @@ export interface ProcessEvent { id: string; } -export async function listenEvent( - event: string, - callback: (event: EventOpts) => unknown, - opts?: ListenEventOpts, -) { +export async function listenEvent(event: string, callback: (event: EventOpts) => unknown, opts?: ListenEventOpts): Promise<() => Promise<void>> { if (RabbitMQ.connection) { - const channel = opts?.channel || RabbitMQ.channel; - if (!channel) - throw new Error( - "[Events] An event was sent without an associated channel", - ); + const rabbitMQChannel = await RabbitMQ.getSafeChannel(); + const channel = opts?.channel || rabbitMQChannel; + if (!channel) throw new Error("[Events] An event was sent without an associated channel"); return await rabbitListen(channel, event, callback, { acknowledge: opts?.acknowledge, }); @@ -101,9 +92,7 @@ export async function listenEvent( const listener = (msg: ProcessEvent) => { // eslint-disable-next-line @typescript-eslint/no-unused-expressions - msg.type === "event" && - msg.id === event && - callback({ ...msg.event, cancel }); + msg.type === "event" && msg.id === event && callback({ ...msg.event, cancel }); }; // TODO: assert the type is correct? @@ -124,20 +113,17 @@ export async function listenEvent( } } -async function rabbitListen( - channel: Channel, - id: string, - callback: (event: EventOpts) => unknown, - opts?: { acknowledge?: boolean }, -) { +async function rabbitListen(channel: Channel, id: string, callback: (event: EventOpts) => unknown, opts?: { acknowledge?: boolean }): Promise<() => Promise<void>> { await channel.assertExchange(id, "fanout", { durable: false }); const q = await channel.assertQueue("", { exclusive: true, autoDelete: true, }); + const consumerTag = randomUUID(); + const cancel = async () => { - await channel.cancel(q.queue); + await channel.cancel(consumerTag); await channel.unbindQueue(q.queue, id, ""); }; @@ -163,6 +149,7 @@ async function rabbitListen( }, { noAck: !opts?.acknowledge, + consumerTag: consumerTag, }, ); diff --git a/src/util/util/RabbitMQ.ts b/src/util/util/RabbitMQ.ts
index 1a61aee9..f11f701f 100644 --- a/src/util/util/RabbitMQ.ts +++ b/src/util/util/RabbitMQ.ts
@@ -23,6 +23,7 @@ export const RabbitMQ: { connection: ChannelModel | null; channel: Channel | null; init: () => Promise<void>; + getSafeChannel: () => Promise<Channel>; } = { connection: null, channel: null, @@ -34,7 +35,38 @@ export const RabbitMQ: { timeout: 1000 * 60, }); console.log(`[RabbitMQ] connected`); + + // log connection errors + this.connection.on("error", (err) => { + console.error("[RabbitMQ] Connection Error:", err); + }); + + this.connection.on("close", () => { + console.error("[RabbitMQ] connection closed"); + // TODO: Add reconnection logic here if the connection crashes?? + // will be a pain since we will have to reconstruct entire state + }); + + await this.getSafeChannel(); + }, + getSafeChannel: async function () { + if (!this.connection) return Promise.reject(); + + if (this.channel) return this.channel; + this.channel = await this.connection.createChannel(); console.log(`[RabbitMQ] channel created`); + + // log channel errors + this.channel.on("error", (err) => { + console.error("[RabbitMQ] Channel Error:", err); + }); + + this.channel.on("close", () => { + console.log("[RabbitMQ] channel closed"); + this.channel = null; + }); + + return this.channel; }, }; diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts
index 57634953..9e071ed9 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts
@@ -18,15 +18,12 @@ import jwt, { VerifyOptions } from "jsonwebtoken"; import { Config } from "./Config"; -import { User } from "../entities"; +import { InstanceBan, User } from "../entities"; import crypto from "node:crypto"; import fs from "fs/promises"; import { existsSync } from "fs"; // TODO: dont use deprecated APIs lol -import { - FindOptionsRelationByString, - FindOptionsSelectByString, -} from "typeorm"; +import { FindManyOptions, FindOptions, FindOptionsRelationByString, FindOptionsSelect, FindOptionsSelectByString, FindOptionsWhere } from "typeorm"; import * as console from "node:console"; export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] }; @@ -37,7 +34,7 @@ export type UserTokenData = { }; function logAuth(text: string) { - if(process.env.LOG_AUTH !== "true") return; + if (process.env.LOG_AUTH !== "true") return; console.log(`[AUTH] ${text}`); } @@ -51,6 +48,8 @@ export const checkToken = ( opts?: { select?: FindOptionsSelectByString<User>; relations?: FindOptionsRelationByString; + ipAddress?: string; + fingerprint?: string; }, ): Promise<UserTokenData> => new Promise((resolve, reject) => { @@ -66,15 +65,7 @@ export const checkToken = ( const user = await User.findOne({ where: { id: decoded.id }, - select: [ - ...(opts?.select || []), - "id", - "bot", - "disabled", - "deleted", - "rights", - "data", - ], + select: [...(opts?.select || []), "id", "bot", "disabled", "deleted", "rights", "data"], relations: opts?.relations, }); @@ -84,10 +75,7 @@ export const checkToken = ( } // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds - if ( - decoded.iat * 1000 < - new Date(user.data.valid_tokens_since).setSeconds(0, 0) - ) { + if (decoded.iat * 1000 < new Date(user.data.valid_tokens_since).setSeconds(0, 0)) { logAuth("validateUser rejected: Token not yet valid"); return rejectAndLog(reject, "Invalid Token"); } @@ -96,11 +84,18 @@ export const checkToken = ( logAuth("validateUser rejected: User disabled"); return rejectAndLog(reject, "User disabled"); } + if (user.deleted) { logAuth("validateUser rejected: User deleted"); return rejectAndLog(reject, "User not found"); } + const banReasons = await InstanceBan.findInstanceBans({ userId: user.id, ipAddress: opts?.ipAddress, fingerprint: opts?.fingerprint, propagateBan: true }); + if (banReasons.length > 0) { + logAuth("validateUser rejected: User banned for reasons: " + banReasons.join(", ")); + return rejectAndLog(reject, "Invalid Token"); + } + logAuth("validateUser success: " + JSON.stringify({ decoded, user })); return resolve({ decoded, user }); }; @@ -109,21 +104,11 @@ export const checkToken = ( if (!dec) return reject("Could not parse token"); logAuth("Decoded token: " + JSON.stringify(dec)); - if (dec.header.alg == "HS256") { - jwt.verify( - token, - Config.get().security.jwtSecret, - JWTOptions, - validateUser, - ); + if (dec.header.alg == "HS256" && Config.get().security.jwtSecret !== null) { + jwt.verify(token, Config.get().security.jwtSecret!, JWTOptions, validateUser); } else if (dec.header.alg == "ES512") { loadOrGenerateKeypair().then((keyPair) => { - jwt.verify( - token, - keyPair.publicKey, - { algorithms: ["ES512"] }, - validateUser, - ); + jwt.verify(token, keyPair.publicKey, { algorithms: ["ES512"] }, validateUser); }); } else return reject("Invalid token algorithm"); }); @@ -152,7 +137,7 @@ let cachedKeypair: { privateKey: crypto.KeyObject; publicKey: crypto.KeyObject; fingerprint: string; -} +}; // Get ECDSA keypair from file or generate it export async function loadOrGenerateKeypair() { @@ -176,10 +161,7 @@ export async function loadOrGenerateKeypair() { let publicKey: crypto.KeyObject; if (existsSync("jwt.key") && existsSync("jwt.key.pub")) { - const [loadedPrivateKey, loadedPublicKey] = await Promise.all([ - fs.readFile("jwt.key"), - fs.readFile("jwt.key.pub"), - ]); + const [loadedPrivateKey, loadedPublicKey] = await Promise.all([fs.readFile("jwt.key"), fs.readFile("jwt.key.pub")]); privateKey = crypto.createPrivateKey(loadedPrivateKey); publicKey = crypto.createPublicKey(loadedPublicKey); @@ -192,14 +174,8 @@ export async function loadOrGenerateKeypair() { publicKey = res.publicKey; await Promise.all([ - fs.writeFile( - "jwt.key", - privateKey.export({ format: "pem", type: "sec1" }), - ), - fs.writeFile( - "jwt.key.pub", - publicKey.export({ format: "pem", type: "spki" }), - ), + fs.writeFile("jwt.key", privateKey.export({ format: "pem", type: "sec1" })), + fs.writeFile("jwt.key.pub", publicKey.export({ format: "pem", type: "spki" })), ]); } @@ -209,5 +185,5 @@ export async function loadOrGenerateKeypair() { .digest("hex"); lastFsCheck = Date.now(); - return cachedKeypair = { privateKey, publicKey, fingerprint }; + return (cachedKeypair = { privateKey, publicKey, fingerprint }); } diff --git a/src/util/util/WebAuthn.ts b/src/util/util/WebAuthn.ts
index a746f364..d499097d 100644 --- a/src/util/util/WebAuthn.ts +++ b/src/util/util/WebAuthn.ts
@@ -19,13 +19,14 @@ import { Fido2Lib } from "fido2-lib"; import jwt from "jsonwebtoken"; import { Config } from "./Config"; +import { loadOrGenerateKeypair } from "./Token"; const jwtSignOptions: jwt.SignOptions = { - algorithm: "HS256", + algorithm: "ES512", expiresIn: "5m", }; const jwtVerifyOptions: jwt.VerifyOptions = { - algorithms: ["HS256"], + algorithms: ["ES512"], }; export const WebAuthn: { @@ -44,28 +45,32 @@ export async function generateWebAuthnTicket( challenge: string, ): Promise<string> { return new Promise((res, rej) => { - jwt.sign( - { challenge }, - Config.get().security.jwtSecret, - jwtSignOptions, - (err, token) => { - if (err || !token) return rej(err || "no token"); - return res(token); - }, + loadOrGenerateKeypair().then(kp=> + jwt.sign( + { challenge }, + kp.privateKey, + jwtSignOptions, + (err, token) => { + if (err || !token) return rej(err || "no token"); + return res(token); + }, + ) ); }); } export async function verifyWebAuthnToken(token: string) { return new Promise((res, rej) => { - jwt.verify( - token, - Config.get().security.jwtSecret, - jwtVerifyOptions, - async (err, decoded) => { - if (err) return rej(err); - return res(decoded); - }, + loadOrGenerateKeypair().then(kp=> + jwt.verify( + token, + kp.publicKey, + jwtVerifyOptions, + async (err, decoded) => { + if (err) return rej(err); + return res(decoded); + }, + ) ); }); }