diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 0cae9b37d..51f40ff29 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -17,6 +17,13 @@
+
+
+
+
+
+
+
+
+
+
@@ -55,52 +65,52 @@
- {
+ "keyToString": {
+ "NIXITCH_NIXPKGS_CONFIG": "/etc/nix/nixpkgs-config.nix",
+ "NIXITCH_NIX_CONF_DIR": "",
+ "NIXITCH_NIX_OTHER_STORES": "",
+ "NIXITCH_NIX_PATH": "/home/Rory/.nix-defexpr/channels:nixpkgs=/nix/store/wb6agba4kfsxpbnb5hzlq58vkjzvbsk6-source",
+ "NIXITCH_NIX_PROFILES": "/run/current-system/sw /nix/var/nix/profiles/default /etc/profiles/per-user/Rory /home/Rory/.local/state/nix/profile /nix/profile /home/Rory/.nix-profile",
+ "NIXITCH_NIX_REMOTE": "",
+ "NIXITCH_NIX_USER_PROFILE_DIR": "/nix/var/nix/profiles/per-user/Rory",
+ "Node.js.Server.ts.executor": "Debug",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "RunOnceActivity.typescript.service.memoryLimit.init": "true",
+ "git-widget-placeholder": "master",
+ "javascript.nodejs.core.library.configured.version": "24.11.1",
+ "javascript.nodejs.core.library.typings.version": "24.10.9",
+ "last_opened_file_path": "/home/Rory/git/spacebar/server-master/nix/modules/default",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.standard": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.standard": "",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_interpreter_path": "node",
+ "nodejs_package_manager_path": "npm",
+ "npm.Start API.executor": "Run",
+ "npm.Start CDN.executor": "Run",
+ "npm.Start Gateway.executor": "Run",
+ "npm.build.executor": "Run",
+ "npm.build:src.executor": "Run",
+ "npm.build:src:tsgo.executor": "Run",
+ "npm.build:tsgo.executor": "Run",
+ "npm.start.executor": "Debug",
+ "prettierjs.PrettierConfiguration.Package": "/home/Rory/git/spacebar/server-master/node_modules/prettier",
+ "ts.external.directory.path": "/home/Rory/git/spacebar/server-master/node_modules/typescript/lib"
},
- "keyToStringList": {
- "DatabaseDriversLRU": [
- "postgresql"
+ "keyToStringList": {
+ "DatabaseDriversLRU": [
+ "postgresql"
],
- "GitStage.ChangesTree.GroupingKeys": [
- "directory",
- "module",
- "repository"
+ "GitStage.ChangesTree.GroupingKeys": [
+ "directory",
+ "module",
+ "repository"
]
}
-}]]>
+}
@@ -191,7 +201,8 @@
-
+
+
@@ -230,4 +241,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.Cdn/Controllers/GetImageController.cs b/extra/admin-api/Spacebar.Cdn/Controllers/GetImageController.cs
index 9a7ddf23f..8e64100a0 100644
--- a/extra/admin-api/Spacebar.Cdn/Controllers/GetImageController.cs
+++ b/extra/admin-api/Spacebar.Cdn/Controllers/GetImageController.cs
@@ -7,25 +7,17 @@ using Spacebar.Interop.Cdn.Abstractions;
namespace Spacebar.Cdn.Controllers;
[ApiController]
-public class GetImageController(LruFileCache lfc, IFileSource fs, DiscordImageResizeService dirs) : ControllerBase {
- [HttpGet("/avatars/{_:required}")]
+public class GetImageController(LruFileCache lfc, IFileSource fs, DiscordImageResizeService dirs) : ImageController {
+ // [HttpGet("/avatars/{_:required}")]
[HttpGet("/emojis/{emoji_id:required}.{ext:required}")]
[HttpGet("/stickers/{sticker_id:required}.{ext:required}")]
- [HttpGet("/avatars/{user_id:required}/{avatar_hash:required}.{ext:required}")]
+ // [HttpGet("/avatars/{user_id:required}/{avatar_hash:required}.{ext:required}")]
[HttpGet("/banners/{user_id:required}/{user_banner:required}.{ext:required}")]
public async Task GetImage(string? ext) {
var originalKey = fs.BaseUrl + Request.Path;
var cacheKey = Request.Path + Request.QueryString;
-
- DiscordImageResizeParams resizeParams = new() {
- Size = Request.Query.ContainsKey("size") && uint.TryParse(Request.Query["size"], out uint size) ? size : null,
- Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality) ? quality : DiscordImageResizeQuality.High,
- KeepAspectRatio = !Request.Query.ContainsKey("keepAspectRatio") || !bool.TryParse(Request.Query["keepAspectRatio"], out bool kar) || kar,
- Passthrough = Request.Query.ContainsKey("passthrough") && bool.TryParse(Request.Query["passthrough"], out bool pt) && pt,
- Animated = Request.Query.ContainsKey("animated") && bool.TryParse(Request.Query["animated"], out bool an) && an,
- SpacebarAllowUpscale = Request.Query.ContainsKey("allowUpscale") && bool.TryParse(Request.Query["allowUpscale"], out bool au) && au,
- SpacebarOptimiseGif = Request.Query.ContainsKey("optimiseGif") && bool.TryParse(Request.Query["optimiseGif"], out bool og) && og
- };
+
+ DiscordImageResizeParams resizeParams = GetResizeParams();
var entry = await lfc.GetOrAdd(cacheKey, async () => {
var original = await fs.GetFile(Request.Path);
diff --git a/extra/admin-api/Spacebar.Cdn/Controllers/StaticAssetController.cs b/extra/admin-api/Spacebar.Cdn/Controllers/StaticAssetController.cs
new file mode 100644
index 000000000..e2ad13777
--- /dev/null
+++ b/extra/admin-api/Spacebar.Cdn/Controllers/StaticAssetController.cs
@@ -0,0 +1,35 @@
+using System.Collections.Immutable;
+using ArcaneLibs.Extensions.Streams;
+using Microsoft.AspNetCore.Mvc;
+using Spacebar.AdminApi.TestClient.Services.Services;
+using Spacebar.Cdn.Extensions;
+using Spacebar.Interop.Cdn.Abstractions;
+
+namespace Spacebar.Cdn.Controllers;
+
+[ApiController]
+public class StaticAssetController(LruFileCache lfc, IFileSource fs, DiscordImageResizeService dirs) : ImageController {
+ private static readonly Dictionary defaultAvatarHashMap = new() {
+ { "0", "4a8562cf00887030c416d3ec2d46385a" },
+ { "1", "9b0bb198936784c45c72833cc426cc55" },
+ { "2", "22341bdb500c7b63a93bbce957d1601e" },
+ { "3", "d9977836b82058bf2f74eebd50edc095" },
+ { "4", "9d6ddb4e4d899a533a8cc617011351c9" },
+ { "5", "7213ab6677377974697dfdfbaf5f6a6f" },
+ };
+
+ private static readonly Dictionary defaultGroupDMAvatarHashMap = new() {
+ { "0", "3b70bb66089c60f8be5e214bf8574c9d" },
+ { "1", "9581acd31832465bdeaa5385b0e919a3" },
+ { "2", "a8a4727cf2dc2939bd3c657fad4463fa" },
+ { "3", "2e46fe14586f8e95471c0917f56726b5" },
+ { "4", "fac7e78de9753d4a37083bba74c1d9ef" },
+ { "5", "4ab900144b0865430dc9be825c838faa" },
+ { "6", "1276374a404452756f3c9cc2601508a5" },
+ { "7", "904bf9f1b61f53ef4a3b7a893afeabe3" },
+ };
+ // [HttpGet("/embed/avatars/{userIndex}")]
+ // public async Task GetDefaultUserAvatar(string userIndex) {
+ //
+ // }
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.Cdn/Controllers/UserController.cs b/extra/admin-api/Spacebar.Cdn/Controllers/UserController.cs
new file mode 100644
index 000000000..976266098
--- /dev/null
+++ b/extra/admin-api/Spacebar.Cdn/Controllers/UserController.cs
@@ -0,0 +1,77 @@
+using ArcaneLibs.Extensions.Streams;
+using Microsoft.AspNetCore.Mvc;
+using Spacebar.AdminApi.TestClient.Services.Services;
+using Spacebar.Cdn.Extensions;
+using Spacebar.Interop.Cdn.Abstractions;
+
+namespace Spacebar.Cdn.Controllers;
+
+[ApiController]
+public class UserController(LruFileCache lfc, IFileSource fs, DiscordImageResizeService dirs) : ImageController {
+ [HttpGet("/avatars/{userId}/{hash}.{ext}")]
+ public async Task GetUserAvatar(string userId, string hash, string ext) {
+ var originalKey = fs.BaseUrl + Request.Path;
+ var cacheKey = Request.Path + Request.QueryString;
+
+ DiscordImageResizeParams resizeParams = GetResizeParams();
+
+ var entry = await lfc.GetOrAdd(cacheKey, async () => {
+ var original = await fs.GetFile(Request.Path);
+
+ if (Request.Query.Any()) {
+ using var img = await original.ToMagickImageCollectionAsync();
+ dirs.Apply(img, resizeParams);
+
+ var outStream = new MemoryStream();
+ await img.WriteAsync(outStream, img.First().Format);
+ outStream.Position = 0;
+
+ return new LruFileCache.Entry() {
+ Data = outStream.ReadToEnd().ToArray(),
+ MimeType = original.MimeType
+ };
+ }
+
+ return new LruFileCache.Entry() {
+ Data = original.Stream.ReadToEnd().ToArray(),
+ MimeType = original.MimeType
+ };
+ });
+
+ // byte array with mime type result
+ return new FileContentResult(entry.Data, entry.MimeType);
+ }
+ [HttpGet("/banners/{userId}/{hash}.{ext}")]
+ public async Task GetUserBanner(string userId, string hash, string ext) {
+ var originalKey = fs.BaseUrl + Request.Path;
+ var cacheKey = Request.Path + Request.QueryString;
+
+ DiscordImageResizeParams resizeParams = GetResizeParams();
+
+ var entry = await lfc.GetOrAdd(cacheKey, async () => {
+ var original = await fs.GetFile(Request.Path);
+
+ if (Request.Query.Any()) {
+ using var img = await original.ToMagickImageCollectionAsync();
+ dirs.Apply(img, resizeParams);
+
+ var outStream = new MemoryStream();
+ await img.WriteAsync(outStream, img.First().Format);
+ outStream.Position = 0;
+
+ return new LruFileCache.Entry() {
+ Data = outStream.ReadToEnd().ToArray(),
+ MimeType = original.MimeType
+ };
+ }
+
+ return new LruFileCache.Entry() {
+ Data = original.Stream.ReadToEnd().ToArray(),
+ MimeType = original.MimeType
+ };
+ });
+
+ // byte array with mime type result
+ return new FileContentResult(entry.Data, entry.MimeType);
+ }
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.Cdn/Extensions/ImageController.cs b/extra/admin-api/Spacebar.Cdn/Extensions/ImageController.cs
new file mode 100644
index 000000000..21531e90d
--- /dev/null
+++ b/extra/admin-api/Spacebar.Cdn/Extensions/ImageController.cs
@@ -0,0 +1,30 @@
+using Microsoft.AspNetCore.Mvc;
+using Spacebar.AdminApi.TestClient.Services.Services;
+
+namespace Spacebar.Cdn.Extensions;
+
+public class ImageController : ControllerBase {
+ protected DiscordImageResizeParams GetResizeParams() {
+ return new() {
+ Size = Request.Query.ContainsKey("size") && uint.TryParse(Request.Query["size"], out uint size) ? size : null,
+ Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality)
+ ? quality
+ : DiscordImageResizeQuality.High,
+ KeepAspectRatio = !Request.Query.ContainsKey("keepAspectRatio") || !bool.TryParse(Request.Query["keepAspectRatio"], out bool kar) || kar,
+ Passthrough = Request.Query.ContainsKey("passthrough") && bool.TryParse(Request.Query["passthrough"], out bool pt) && pt,
+ Animated = Request.Query.ContainsKey("animated") && bool.TryParse(Request.Query["animated"], out bool an) && an,
+ SpacebarAllowUpscale = Request.Query.ContainsKey("allowUpscale") && bool.TryParse(Request.Query["allowUpscale"], out bool au) && au,
+ SpacebarOptimiseGif = Request.Query.ContainsKey("optimiseGif") && bool.TryParse(Request.Query["optimiseGif"], out bool og) && og
+ };
+ }
+
+ protected void SetSuccessCacheHeader() {
+ int cacheDuration = (int)TimeSpan.FromHours(6).TotalSeconds;
+ Response.Headers.CacheControl = $"public, max-age={cacheDuration}, s-maxage={cacheDuration}, immutable";
+ }
+
+ protected void SetFailureCacheHeader() {
+ int cacheDuration = (int)TimeSpan.FromMinutes(5).TotalSeconds;
+ Response.Headers.CacheControl = $"public, max-age={cacheDuration}, s-maxage={cacheDuration}, immutable";
+ }
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.Cdn/Program.cs b/extra/admin-api/Spacebar.Cdn/Program.cs
index 47b518331..61a5c07bd 100644
--- a/extra/admin-api/Spacebar.Cdn/Program.cs
+++ b/extra/admin-api/Spacebar.Cdn/Program.cs
@@ -10,7 +10,8 @@ if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("APPSETTINGS_P
builder.Configuration.AddJsonFile(Environment.GetEnvironmentVariable("APPSETTINGS_PATH")!);
// Add services to the container.
-builder.Services.AddSingleton(new ProxyFileSource("http://cdn.old.server.spacebar.chat"));
+// builder.Services.AddSingleton(new ProxyFileSource("http://cdn.old.server.spacebar.chat"));
+builder.Services.AddSingleton(new FilesystemFileSource(Environment.GetEnvironmentVariable("STORAGE_PATH") ?? throw new InvalidOperationException("STORAGE_PATH not set!")));
builder.Services.AddSingleton(new LruFileCache(1 * 1024 * 1024 * 1024));
builder.Services.AddSingleton();
builder.Services.AddSingleton();
diff --git a/extra/admin-api/Spacebar.UApi/Controllers/GuildStickerController.cs b/extra/admin-api/Spacebar.UApi/Controllers/GuildStickerController.cs
index 9a8ee068b..6ce1fc313 100644
--- a/extra/admin-api/Spacebar.UApi/Controllers/GuildStickerController.cs
+++ b/extra/admin-api/Spacebar.UApi/Controllers/GuildStickerController.cs
@@ -12,31 +12,31 @@ namespace Spacebar.UApi.Controllers;
[Route("/api/v{_}/guilds/{guildId}/stickers/")]
public class GuildStickerController(ILogger logger, SpacebarDbContext db, SpacebarAspNetAuthenticationService authService, UApiConfiguration cfg, PermissionService permService, Config sbCfg) : ControllerBase {
// TODO proper response type
- [HttpPost]
- public async Task UploadGuildSticker(string guildId, MultipartFormDataContent content) {
-
- var sticker = new Sticker() {
- GuildId = guildId
- };
-
- foreach (var item in content) {
- switch (item.Headers.ContentDisposition.Name.Trim('"')) {
- case "name":
- sticker.Name = await item.ReadAsStringAsync();
- break;
- case "description":
- sticker.Description = await item.ReadAsStringAsync();
- break;
- case "tags":
- sticker.Tags = await item.ReadAsStringAsync();
- break;
- case "file":
- var fileContent = await item.ReadAsStreamAsync();
-
- break;
- }
- }
-
- return sticker;
- }
+ // [HttpPost]
+ // public async Task UploadGuildSticker(string guildId, MultipartFormDataContent content) {
+ //
+ // var sticker = new Sticker() {
+ // GuildId = guildId
+ // };
+ //
+ // foreach (var item in content) {
+ // switch (item.Headers.ContentDisposition.Name.Trim('"')) {
+ // case "name":
+ // sticker.Name = await item.ReadAsStringAsync();
+ // break;
+ // case "description":
+ // sticker.Description = await item.ReadAsStringAsync();
+ // break;
+ // case "tags":
+ // sticker.Tags = await item.ReadAsStringAsync();
+ // break;
+ // case "file":
+ // var fileContent = await item.ReadAsStreamAsync();
+ //
+ // break;
+ // }
+ // }
+ //
+ // return sticker;
+ // }
}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.UApi/Controllers/Messages/MessagesController.cs b/extra/admin-api/Spacebar.UApi/Controllers/Messages/MessagesController.cs
index 6ef3d4291..c76ef7ae8 100644
--- a/extra/admin-api/Spacebar.UApi/Controllers/Messages/MessagesController.cs
+++ b/extra/admin-api/Spacebar.UApi/Controllers/Messages/MessagesController.cs
@@ -11,42 +11,42 @@ namespace Spacebar.UApi.Controllers.Messages;
[ApiController]
[Route("/api/v{_}/channels/{channelId}/messages")]
public partial class MessagesController(ILogger logger, SpacebarDbContext db, SpacebarAspNetAuthenticationService authService, UApiConfiguration cfg) : ControllerBase {
- [Consumes("multipart/form-data")]
- [HttpPost]
- public async Task CreateMessageWithAttachments(string channelId, MultipartFormDataContent formData) {
- // Generic proxy doesnt work with multipart/form-data for some reason, so handle them specially
- JsonObject jsonPayload = null!;
-
- foreach (var content in formData)
- {
- if (content.Headers.ContentDisposition?.Name == "payload_json") {
- jsonPayload = await content.ReadFromJsonAsync();
- break;
- }
- if (FileNameRegex().IsMatch(content.Headers.ContentDisposition?.Name ?? "")) {
-
- break;
- }
- throw new InvalidOperationException("Invalid multipart/form-data payload: missing payload_json or file attachments");
- }
-
- var client = new StreamingHttpClient();
- var requestMessage = new HttpRequestMessage(
- new HttpMethod(Request.Method),
- cfg.FallbackApiEndpoint + Request.Path + Request.QueryString
- ) {
- Content = new StreamContent(Request.Body)
- };
- Console.WriteLine(requestMessage.RequestUri);
-
- var responseMessage = await client.SendUnhandledAsync(requestMessage, CancellationToken.None);
- Response.StatusCode = (int)responseMessage.StatusCode;
-
- foreach (var header in responseMessage.Headers) Response.Headers[header.Key] = header.Value.ToArray();
- foreach (var header in responseMessage.Content.Headers) Response.Headers[header.Key] = header.Value.ToArray();
-
- await responseMessage.Content.CopyToAsync(Response.Body);
- }
+ // [Consumes("multipart/form-data")]
+ // [HttpPost]
+ // public async Task CreateMessageWithAttachments(string channelId, MultipartFormDataContent formData) {
+ // // Generic proxy doesnt work with multipart/form-data for some reason, so handle them specially
+ // JsonObject jsonPayload = null!;
+ //
+ // foreach (var content in formData)
+ // {
+ // if (content.Headers.ContentDisposition?.Name == "payload_json") {
+ // jsonPayload = await content.ReadFromJsonAsync();
+ // break;
+ // }
+ // if (FileNameRegex().IsMatch(content.Headers.ContentDisposition?.Name ?? "")) {
+ //
+ // break;
+ // }
+ // throw new InvalidOperationException("Invalid multipart/form-data payload: missing payload_json or file attachments");
+ // }
+ //
+ // var client = new StreamingHttpClient();
+ // var requestMessage = new HttpRequestMessage(
+ // new HttpMethod(Request.Method),
+ // cfg.FallbackApiEndpoint + Request.Path + Request.QueryString
+ // ) {
+ // Content = new StreamContent(Request.Body)
+ // };
+ // Console.WriteLine(requestMessage.RequestUri);
+ //
+ // var responseMessage = await client.SendUnhandledAsync(requestMessage, CancellationToken.None);
+ // Response.StatusCode = (int)responseMessage.StatusCode;
+ //
+ // foreach (var header in responseMessage.Headers) Response.Headers[header.Key] = header.Value.ToArray();
+ // foreach (var header in responseMessage.Content.Headers) Response.Headers[header.Key] = header.Value.ToArray();
+ //
+ // await responseMessage.Content.CopyToAsync(Response.Body);
+ // }
[GeneratedRegex(@"files\[\d+\]")]
private static partial Regex FileNameRegex();
diff --git a/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/FsckService.cs b/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/FsckService.cs
index 6247df3b7..add630e5d 100644
--- a/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/FsckService.cs
+++ b/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/FsckService.cs
@@ -33,7 +33,7 @@ public class FsckService(ILogger logger, IServiceScopeFactory servi
}
private async Task RunFsckAsync(string name, string path, IQueryable items) {
- int i = 0, count = await items.CountAsync();
+ int i = 0, count = await items.CountAsync(), missing = 0;
List tasks = [];
await foreach (var item in items.AsAsyncEnumerable()) {
@@ -46,12 +46,16 @@ public class FsckService(ILogger logger, IServiceScopeFactory servi
i++;
if (!item.IsSingleSubDirFile) {
- if (!await fs.FileExists(item.Path))
+ if (!await fs.FileExists(item.Path)) {
logger.LogWarning("{itemType} {itemId} is missing at {path}", name, item.ItemId, item.Path);
+ missing++;
+ }
}
else if (item.IsSingleSubDirFile && fs is FilesystemFileSource ffs) {
- if(!await ffs.DirectoryExists(Path.GetDirectoryName(item.Path)))
+ if (!await ffs.DirectoryExists(Path.GetDirectoryName(item.Path))) {
logger.LogWarning("{itemType} {itemId} is missing at {path} (directory missing)", name, item.ItemId, item.Path);
+ missing++;
+ }
}
else {
logger.LogWarning("Unhandled case: {itemType} {itemId} -> {path} (IsSingleSubDirFile: {isSingleSubDirFile}, fstype: {fsType})", name, item.ItemId, item.Path, item.IsSingleSubDirFile, fs.GetType().Name);
@@ -62,7 +66,7 @@ public class FsckService(ILogger logger, IServiceScopeFactory servi
}
await Task.WhenAll(tasks);
- logger.LogInformation("Validated {count} items for {path}.", i, path);
+ logger.LogInformation("Validated {count} items for {path}, {missing} missing.", i, path, missing);
}
#region User Assets
@@ -112,17 +116,15 @@ public class FsckService(ILogger logger, IServiceScopeFactory servi
.OrderBy(x => x.Id)
.Select(x => new FsckItem {
Path = $"/stickers/{x.Id}",
- ItemId = x.Id,
- IsSingleSubDirFile = fs is FilesystemFileSource
+ ItemId = x.Id
});
public IQueryable EnumerateEmojiPathsAsync() =>
_db.Emojis
.OrderBy(x => x.Id)
.Select(x => new FsckItem {
- Path = $"/emojis/{x.Id}/",
- ItemId = x.Id,
- IsSingleSubDirFile = fs is FilesystemFileSource
+ Path = $"/emojis/{x.Id}",
+ ItemId = x.Id
});
#endregion
diff --git a/flake.nix b/flake.nix
index 5182ef624..89a0e209a 100644
--- a/flake.nix
+++ b/flake.nix
@@ -16,7 +16,7 @@
self,
nixpkgs,
flake-utils,
- pion-webrtc
+ pion-webrtc,
}:
nixpkgs.lib.recursiveUpdate
(
@@ -102,6 +102,10 @@
// {
nixosModules.default = import ./nix/modules/default self;
nixosConfigurations.testVm = import ./nix/testVm/default.nix { inherit self nixpkgs; };
+ nixosConfigurations.test = nixpkgs.lib.nixosSystem {
+ system = "x86_64-linux";
+ modules = [ ];
+ };
checks =
let
pkgs = import nixpkgs { system = "x86_64-linux"; };
@@ -125,4 +129,4 @@
inherit self nixpkgs flake-utils;
}
);
-}
\ No newline at end of file
+}
diff --git a/nix/modules/default/config-file.nix b/nix/modules/default/config-file.nix
index 1b321f0a7..55a558f04 100644
--- a/nix/modules/default/config-file.nix
+++ b/nix/modules/default/config-file.nix
@@ -25,7 +25,7 @@ let
};
}
// (
- if cfg.enableAdminApi then
+ if cfg.adminApi.enable then
{
adminApi = {
endpointPublic = "http${if cfg.adminApiEndpoint.useSsl then "s" else ""}://${cfg.adminApiEndpoint.host}:${toString cfg.adminApiEndpoint.publicPort}";
diff --git a/nix/modules/default/cs/cdn-cs.nix b/nix/modules/default/cs/cdn-cs.nix
new file mode 100644
index 000000000..42432dab8
--- /dev/null
+++ b/nix/modules/default/cs/cdn-cs.nix
@@ -0,0 +1,65 @@
+self:
+{
+ config,
+ lib,
+ pkgs,
+ spacebar,
+ ...
+}:
+
+let
+ secrets = import ../secrets.nix { inherit lib config; };
+ cfg = config.services.spacebarchat-server;
+ jsonFormat = pkgs.formats.json { };
+in
+{
+ imports = [ ];
+ options.services.spacebarchat-server.cdnCs = lib.mkOption {
+ default = { };
+ description = "Configuration for C# cdn.";
+ type = lib.types.submodule {
+ options = {
+ enable = lib.mkEnableOption "Enable experimental C# CDN.";
+ extraConfiguration = lib.mkOption {
+ type = jsonFormat.type;
+ default = import ./default-appsettings-json.nix;
+ description = "Extra appsettings.json configuration for the gateway offload daemon.";
+ };
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.cdnCs.enable (
+ let
+ makeServerTsService = import ../makeServerTsService.nix { inherit cfg lib secrets; };
+ in
+ {
+ assertions = [
+ (import ./assert-has-connection-string.nix "Admin API" cfg.adminApi.extraConfiguration)
+ ];
+
+ systemd.services.spacebar-cdn = makeServerTsService {
+ description = "Spacebar Server - CDN (C#)";
+ environment = builtins.mapAttrs (_: val: builtins.toString val) (
+ {
+ # things we set by default...
+ EVENT_TRANSMISSION = "unix";
+ EVENT_SOCKET_PATH = "/run/spacebar/";
+ }
+ // cfg.extraEnvironment
+ // {
+ # things we force...
+ # CONFIG_PATH = configFile;
+ CONFIG_READONLY = 1;
+ ASPNETCORE_URLS = "http://0.0.0.0:${toString cfg.cdnEndpoint.localPort}";
+ STORAGE_LOCATION = cfg.cdnPath;
+ APPSETTINGS_PATH = jsonFormat.generate "appsettings.spacebar-cdn.json" (lib.recursiveUpdate (import ./default-appsettings-json.nix) cfg.cdnCs.extraConfiguration);
+ }
+ );
+ serviceConfig = {
+ ExecStart = "${self.packages.${pkgs.stdenv.hostPlatform.system}.Spacebar-AdminApi}/bin/Spacebar.AdminApi";
+ };
+ };
+ }
+ );
+}
diff --git a/nix/modules/default/cs/uapi.nix b/nix/modules/default/cs/uapi.nix
index 9cd76b624..00caa214f 100644
--- a/nix/modules/default/cs/uapi.nix
+++ b/nix/modules/default/cs/uapi.nix
@@ -45,6 +45,7 @@ in
systemd.services.spacebar-uapi = makeServerTsService {
description = "Spacebar Server - C# API overlay";
+ # after = [ "spacebar-api.service" ];
environment = builtins.mapAttrs (_: val: builtins.toString val) (
{
# things we set by default...
diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix
index fb84c8f60..11899aeb5 100644
--- a/nix/modules/default/default.nix
+++ b/nix/modules/default/default.nix
@@ -18,6 +18,7 @@ in
./integration-nginx.nix
./users.nix
(import ./pion-sfu.nix self)
+ (import ./cs/cdn-cs.nix self)
(import ./cs/gateway-offload-cs.nix self)
(import ./cs/admin-api.nix self)
(import ./cs/uapi.nix self)
@@ -28,8 +29,6 @@ in
in
{
enable = lib.mkEnableOption "Spacebar server";
- enableAdminApi = lib.mkEnableOption "Spacebar server Admin API";
- enableCdnCs = lib.mkEnableOption "Spacebar's experimental CDN rewrite";
package = lib.mkPackageOption self.packages.${pkgs.stdenv.hostPlatform.system} "spacebar-server" { default = "default"; };
databaseFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
@@ -129,6 +128,7 @@ in
systemd.services.spacebar-gateway = makeServerTsService {
description = "Spacebar Server - Gateway";
+ # after = [ "spacebar-api.service" ];
environment = builtins.mapAttrs (_: val: builtins.toString val) (
{
# things we set by default...
@@ -150,7 +150,7 @@ in
};
};
- systemd.services.spacebar-cdn = lib.mkIf (!cfg.enableCdnCs) (makeServerTsService {
+ systemd.services.spacebar-cdn = lib.mkIf (!cfg.cdnCs.enable) (makeServerTsService {
description = "Spacebar Server - CDN";
environment = builtins.mapAttrs (_: val: builtins.toString val) (
{
diff --git a/nix/modules/default/lib.nix b/nix/modules/default/lib.nix
index 886b78fc8..cd7a7fb4d 100644
--- a/nix/modules/default/lib.nix
+++ b/nix/modules/default/lib.nix
@@ -11,4 +11,11 @@
else
80;
};
+
+ mkEndpointRaw = domain: port: publicPort: ssl: {
+ host = domain;
+ localPort = port;
+ useSsl = ssl;
+ publicPort = publicPort;
+ };
}
diff --git a/nix/modules/default/makeServerTsService.nix b/nix/modules/default/makeServerTsService.nix
index ae54e3f75..7a6112510 100644
--- a/nix/modules/default/makeServerTsService.nix
+++ b/nix/modules/default/makeServerTsService.nix
@@ -75,6 +75,7 @@ lib.recursiveUpdate
StateDirectoryMode = "0750";
RuntimeDirectory = "spacebar";
RuntimeDirectoryMode = "0750";
+ RuntimeDirectoryPreserve = "yes";
ReadWritePaths = [ cfg.cdnPath ];
NoExecPaths = [ cfg.cdnPath ];
diff --git a/nix/testVm/configuration.nix b/nix/testVm/configuration.nix
index db31cbe7a..7d33dc714 100644
--- a/nix/testVm/configuration.nix
+++ b/nix/testVm/configuration.nix
@@ -1,4 +1,9 @@
-{ pkgs, lib, ... }:
+{
+ config,
+ pkgs,
+ lib,
+ ...
+}:
let
nginxTestSigning = {
addSSL = true;
@@ -11,72 +16,66 @@ let
in
{
networking.hostName = "sbtest";
- services.nginx.virtualHosts."sb.localhost" = nginxTestSigning;
- services.nginx.virtualHosts."api.sb.localhost" = nginxTestSigning;
- services.nginx.virtualHosts."gw.sb.localhost" = nginxTestSigning;
- services.nginx.virtualHosts."cdn.sb.localhost" = nginxTestSigning;
- services.nginx.virtualHosts."webrtc.sb.localhost" = nginxTestSigning;
- services.nginx.virtualHosts."admin.sb.localhost" = nginxTestSigning;
+ services.nginx.virtualHosts.${config.services.spacebarchat-server.serverName} = nginxTestSigning;
+ services.nginx.virtualHosts.${config.services.spacebarchat-server.adminApiEndpoint.host} = nginxTestSigning;
+ services.nginx.virtualHosts.${config.services.spacebarchat-server.apiEndpoint.host} = nginxTestSigning;
+ services.nginx.virtualHosts.${config.services.spacebarchat-server.cdnEndpoint.host} = nginxTestSigning;
+ services.nginx.virtualHosts.${config.services.spacebarchat-server.gatewayEndpoint.host} = nginxTestSigning;
+ services.nginx.virtualHosts.${config.services.spacebarchat-server.webrtcEndpoint.host} = nginxTestSigning;
services.spacebarchat-server =
let
+ sbLib = import ../modules/default/lib.nix;
csConnectionString = "Host=127.0.0.1; Username=postgres; Password=postgres; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;";
cfg = {
enable = true;
- apiEndpoint = {
- useSsl = false;
- host = "api.sb.localhost";
- localPort = 3001;
- publicPort = 8080;
- };
- gatewayEndpoint = {
- useSsl = false;
- host = "gw.sb.localhost";
- localPort = 3002;
- publicPort = 8080;
- };
- cdnEndpoint = {
- useSsl = false;
- host = "cdn.sb.localhost";
- localPort = 3003;
- publicPort = 8080;
- };
- adminApiEndpoint = {
- useSsl = false;
- host = "admin.sb.localhost";
- localPort = 3004;
- publicPort = 8080;
- };
- webrtcEndpoint = {
- useSsl = false;
- host = "voice.sb.localhost";
- localPort = 3005;
- publicPort = 8080;
- };
+ apiEndpoint = sbLib.mkEndpointRaw "api.sb.localhost" 3001 8080 false;
+ gatewayEndpoint = sbLib.mkEndpointRaw "gw.sb.localhost" 3002 8080 false;
+ cdnEndpoint = sbLib.mkEndpointRaw "cdn.sb.localhost" 3003 8080 false;
+ adminApiEndpoint = sbLib.mkEndpointRaw "admin.sb.localhost" 3004 8080 false;
+ webrtcEndpoint = sbLib.mkEndpointRaw "voice.sb.localhost" 3005 8080 false;
nginx.enable = true;
serverName = "sb.localhost";
+ settings = {
+ security = {
+ requestSignature = "meow";
+ cdnSignatureDuration = "5m";
+ cdnSignatureIncludeIp = true;
+ cdnSignatureIncludeUserAgent = false;
+ cdnSignatureKey = "meow";
+ };
+ };
+
gatewayOffload = {
enable = true;
enableGuildSync = true;
extraConfiguration.ConnectionStrings.Spacebar = csConnectionString;
};
+
adminApi = {
enable = true;
extraConfiguration.ConnectionStrings.Spacebar = csConnectionString;
};
+
+ cdnCs = {
+ enable = false;
+ extraConfiguration.ConnectionStrings.Spacebar = csConnectionString;
+ };
+
uApi = {
enable = true;
extraConfiguration.ConnectionStrings.Spacebar = csConnectionString;
};
+
pion-sfu = {
enable = true;
publicIp = "127.0.0.1";
};
+
extraEnvironment = {
DATABASE = "postgres://postgres:postgres@127.0.0.1/spacebar";
- #WEBRTC_PORT_RANGE=60000-61000;
- #PUBLIC_IP=216.230.228.60;
- LOG_REQUESTS = "-200,204,304";
+# LOG_REQUESTS = "-200,204,304";
+ LOG_REQUESTS = "-";
LOG_VALIDATION_ERRORS = true;
#DB_LOGGING=true;
#LOG_GATEWAY_TRACES=true;