diff --git a/extra/admin-api/DiscordEmojiConverter/DiscordEmojiConverter.csproj b/extra/admin-api/DiscordEmojiConverter/DiscordEmojiConverter.csproj
new file mode 100644
index 00000000..72d306b3
--- /dev/null
+++ b/extra/admin-api/DiscordEmojiConverter/DiscordEmojiConverter.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net10.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="ArcaneLibs" Version="1.0.0-preview.20251207-164820" />
+ </ItemGroup>
+
+</Project>
diff --git a/extra/admin-api/DiscordEmojiConverter/DiscordEmojiJson.cs b/extra/admin-api/DiscordEmojiConverter/DiscordEmojiJson.cs
new file mode 100644
index 00000000..71c4a53f
--- /dev/null
+++ b/extra/admin-api/DiscordEmojiConverter/DiscordEmojiJson.cs
@@ -0,0 +1,55 @@
+using System.Text.Json.Serialization;
+
+namespace DiscordEmojiConverter;
+
+public class DiscordEmojiJson {
+ [JsonPropertyName("emojis")]
+ public List<DiscordEmoji>? Emojis { get; set; }
+
+ [JsonPropertyName("emojisByCategory")]
+ public Dictionary<string, List<int>>? EmojisByCategory { get; set; }
+
+ [JsonPropertyName("nameToEmoji")]
+ public Dictionary<string, int>? NameToEmoji { get; set; }
+
+ [JsonPropertyName("surrogateToEmoji")]
+ public Dictionary<string, int>? SurrogateToEmoji { get; set; }
+
+ [JsonPropertyName("numDiversitySprites")]
+ public int? NumDiversitySprites { get; set; }
+
+ [JsonPropertyName("numNonDiversitySprites")]
+ public int? NumNonDiversitySprites { get; set; }
+}
+
+public class DiscordEmoji {
+ [JsonPropertyName("names")]
+ public List<string>? Names { get; set; }
+
+ [JsonPropertyName("surrogates")]
+ public string? Surrogates { get; set; }
+
+ [JsonPropertyName("unicodeVersion")]
+ public float? UnicodeVersion { get; set; }
+
+ [JsonPropertyName("spriteIndex")]
+ public int? SpriteIndex { get; set; }
+
+ [JsonPropertyName("hasMultiDiversity")]
+ public bool? HasMultiDiversity { get; set; }
+
+ [JsonPropertyName("hasMultiDiversityParent")]
+ public bool? HasMultiDiversityParent { get; set; }
+
+ [JsonPropertyName("hasDiversity")]
+ public bool? HasDiversity { get; set; }
+
+ [JsonPropertyName("hasDiversityParent")]
+ public bool? HasDiversityParent { get; set; }
+
+ [JsonPropertyName("diversityChildren")]
+ public List<int>? DiversityChildren { get; set; }
+
+ [JsonPropertyName("diversity")]
+ public List<string>? Diversity { get; set; }
+}
\ No newline at end of file
diff --git a/extra/admin-api/DiscordEmojiConverter/Program.cs b/extra/admin-api/DiscordEmojiConverter/Program.cs
new file mode 100644
index 00000000..96593958
--- /dev/null
+++ b/extra/admin-api/DiscordEmojiConverter/Program.cs
@@ -0,0 +1,27 @@
+// See https://aka.ms/new-console-template for more information
+
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+using ArcaneLibs.Extensions;
+using DiscordEmojiConverter;
+
+var emojis = JsonSerializer.Deserialize<DiscordEmojiJson>(File.OpenRead(args[0]),
+ new JsonSerializerOptions() { UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow });
+
+emojis.SurrogateToEmoji = null;
+emojis.NameToEmoji = null;
+emojis.NumDiversitySprites = null;
+emojis.NumNonDiversitySprites = null;
+
+foreach (var emoji in emojis.Emojis!)
+{
+ emoji.HasDiversity = null;
+ emoji.HasDiversityParent = null;
+ emoji.HasMultiDiversity = null;
+ emoji.HasMultiDiversityParent = null;
+ emoji.SpriteIndex = null;
+ emoji.UnicodeVersion = null;
+}
+
+Console.WriteLine(emojis.ToJson(ignoreNull: true, indent: false));
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.ConfigModel/Class1.cs b/extra/admin-api/Spacebar.ConfigModel/Class1.cs
index 948ca91a..26e9d0fc 100644
--- a/extra/admin-api/Spacebar.ConfigModel/Class1.cs
+++ b/extra/admin-api/Spacebar.ConfigModel/Class1.cs
@@ -2,63 +2,32 @@
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 class Config {
+ [JsonPropertyName("admin")]
+ public EndpointConfig Admin { get; set; } = null!;
- public Config ReadFromKv(Dictionary<string, object?> kv)
- {
- // to object
-
+ [JsonPropertyName("api")]
+ public EndpointConfig Api { get; set; } = null!;
- foreach (var (key, value) in kv)
- {
- switch (key.Split('_', 2)[0])
- {
- default:
- Console.WriteLine($"Unrecognized config key prefix: {key}");
- continue;
- }
- }
+ [JsonPropertyName("gateway")]
+ public EndpointConfig Gateway { get; set; } = null!;
- return this;
- }
+ [JsonPropertyName("cdn")]
+ public EndpointConfig Cdn { get; set; } = null!;
}
-public class EndpointConfig
-{
- [JsonPropertyName("endpointPrivate")] public string? EndpointPrivate { get; set; }
- [JsonPropertyName("endpointPublic")] public string? EndpointPublic { get; set; }
+public class EndpointConfig {
+ [JsonPropertyName("endpointPrivate")]
+ public string? EndpointPrivate { 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;
- }
+ [JsonPropertyName("endpointPublic")]
+ public string? EndpointPublic { get; set; }
}
-public class ApiConfig : EndpointConfig
-{
- [JsonPropertyName("activeVersions")] public List<string> ActiveVersions { get; set; } = null!;
- [JsonPropertyName("defaultVersion")] public string DefaultVersion { get; set; } = null!;
+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/DefaultsConfiguration.cs b/extra/admin-api/Spacebar.ConfigModel/DefaultsConfiguration.cs
new file mode 100644
index 00000000..884f3d54
--- /dev/null
+++ b/extra/admin-api/Spacebar.ConfigModel/DefaultsConfiguration.cs
@@ -0,0 +1,37 @@
+using System.Text.Json.Serialization;
+
+namespace Spacebar.ConfigModel;
+
+public class DefaultsConfiguration
+{
+ [JsonPropertyName("guild")] public GuildDefaults Guild = new GuildDefaults();
+ [JsonPropertyName("user")] public ChannelDefaults Channel = new ChannelDefaults();
+}
+
+public class GuildDefaults
+{
+ [JsonPropertyName("maxPresences")] public int MaxPresences { get; set; } = 250000;
+
+ [JsonPropertyName("maxVideoChannelUsers")]
+ public int MaxVideoChannelUsers { get; set; } = 200;
+
+ [JsonPropertyName("afkTimeout")] public int AfkTimeout { get; set; } = 300;
+
+ [JsonPropertyName("defaultMessageNotifications")]
+ public int DefaultMessageNotifications { get; set; } = 1;
+
+ [JsonPropertyName("explicitContentFilter")]
+ public int ExplicitContentFilter { get; set; } = 0;
+}
+
+public class ChannelDefaults
+{
+ [JsonPropertyName("premium")]
+ public bool Premium { get; set; } = true;
+
+ [JsonPropertyName("premiumType")]
+ public int PremiumType { get; set; } = 2;
+
+ [JsonPropertyName("verified")]
+ public bool Verified { get; set; } = true;
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.ConfigModel/GeneralConfiguration.cs b/extra/admin-api/Spacebar.ConfigModel/GeneralConfiguration.cs
new file mode 100644
index 00000000..1e006759
--- /dev/null
+++ b/extra/admin-api/Spacebar.ConfigModel/GeneralConfiguration.cs
@@ -0,0 +1,35 @@
+using System.Text.Json.Serialization;
+
+namespace Spacebar.ConfigModel;
+
+public class GeneralConfiguration {
+ [JsonPropertyName("instanceName")]
+ public string InstanceName { get; set; } = "Spacebar Instance";
+
+ [JsonPropertyName("serverName")]
+ public string? ServerName { get; set; } = null;
+
+ [JsonPropertyName("instanceDescription")]
+ public string InstanceDescription { get; set; } = "This is a Spacebar instance made in the pre-release days";
+
+ [JsonPropertyName("frontPage")]
+ public string? FrontPage { get; set; } = null;
+
+ [JsonPropertyName("tosPage")]
+ public string? TosPage { get; set; } = null;
+
+ [JsonPropertyName("correspondenceEmail")]
+ public string? CorrespondenceEmail { get; set; } = null;
+
+ [JsonPropertyName("correspondenceUserID")]
+ public string? CorrespondenceUserID { get; set; } = null;
+
+ [JsonPropertyName("image")]
+ public string? Image { get; set; } = null;
+
+ [JsonPropertyName("instanceId")]
+ public string InstanceId { get; set; } = null!; // {get;set;}=Snowflake.generate();
+
+ [JsonPropertyName("autoCreateBotUsers")]
+ public bool AutoCreateBotUsers { get; set; } = false;
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.ConfigModel/LimitsConfiguration.cs b/extra/admin-api/Spacebar.ConfigModel/LimitsConfiguration.cs
new file mode 100644
index 00000000..8a773744
--- /dev/null
+++ b/extra/admin-api/Spacebar.ConfigModel/LimitsConfiguration.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace Spacebar.ConfigModel;
+
+public class LimitsConfiguration {
+ [JsonPropertyName("user")] public UserLimits User = new UserLimits();
+ [JsonPropertyName("guild")] public GuildLimits Guild = new GuildLimits();
+ [JsonPropertyName("message")] public MessageLimits Message = new MessageLimits();
+ [JsonPropertyName("channel")] public ChannelLimits Channel = new ChannelLimits();
+ [JsonPropertyName("rate")] public RateLimits Rate = new RateLimits();
+ [JsonPropertyName("absoluteRate")] public GlobalRateLimits AbsoluteRate = new GlobalRateLimits();
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.ConfigModel/SecurityConfiguration.cs b/extra/admin-api/Spacebar.ConfigModel/SecurityConfiguration.cs
new file mode 100644
index 00000000..017e89ba
--- /dev/null
+++ b/extra/admin-api/Spacebar.ConfigModel/SecurityConfiguration.cs
@@ -0,0 +1,26 @@
+using System.Text.Json.Serialization;
+
+namespace Spacebar.ConfigModel;
+
+public class SecurityConfiguration
+{
+ [JsonPropertyName("captcha")] public CaptchaConfiguration Captcha = new CaptchaConfiguration();
+ [JsonPropertyName("twoFactor")] public TwoFactorConfiguration TwoFactor = new TwoFactorConfiguration();
+ [JsonPropertyName("autoUpdate")] public bool AutoUpdate = true;
+ [JsonPropertyName("requestSignature")] public string RequestSignature; // = crypto.randomBytes(32).toString("base64");
+ [JsonPropertyName("jwtSecret")] public string? JwtSecret = null;
+ [JsonPropertyName("forwardedFor")] public string? ForwardedFor = null;
+ [JsonPropertyName("trustedProxies")] public string TrustedProxies = null;
+ [JsonPropertyName("abuseIpDbApiKey")] public string? AbuseIpDbApiKey = null;
+ [JsonPropertyName("abuseipdbBlacklistRatelimit")] public int AbuseipdbBlacklistRatelimit = 5;
+ [JsonPropertyName("abuseipdbConfidenceScoreTreshold")] public int AbuseipdbConfidenceScoreTreshold = 50;
+ [JsonPropertyName("ipdataApiKey")] public string? IpdataApiKey = null;
+ [JsonPropertyName("mfaBackupCodeCount")] public int MfaBackupCodeCount = 10;
+ [JsonPropertyName("statsWorldReadable")] public bool StatsWorldReadable = true;
+ [JsonPropertyName("defaultRegistrationTokenExpiration")] public int DefaultRegistrationTokenExpiration = 1000 * 60 * 60 * 24 * 7;
+ [JsonPropertyName("cdnSignUrls")] public bool CdnSignUrls = false;
+ [JsonPropertyName("cdnSignatureKey")] public string CdnSignatureKey = crypto.randomBytes(32).toString("base64");
+ [JsonPropertyName("cdnSignatureDuration")] public string CdnSignatureDuration = "24h";
+ [JsonPropertyName("cdnSignatureIncludeIp")] public bool CdnSignatureIncludeIp = true;
+ [JsonPropertyName("cdnSignatureIncludeUserAgent")] public bool CdnSignatureIncludeUserAgent = true;
+}
\ No newline at end of file
diff --git a/extra/admin-api/Spacebar.ConfigModel/ServerConfiguration.cs b/extra/admin-api/Spacebar.ConfigModel/ServerConfiguration.cs
new file mode 100644
index 00000000..49309ab1
--- /dev/null
+++ b/extra/admin-api/Spacebar.ConfigModel/ServerConfiguration.cs
@@ -0,0 +1,106 @@
+using System.Text.Json.Serialization;
+
+namespace Spacebar.ConfigModel;
+
+public class ServerConfiguration {
+ [JsonPropertyName("admin")] public EndpointConfiguration Admin = new EndpointConfiguration();
+ [JsonPropertyName("gateway")] public EndpointConfiguration Gateway = new EndpointConfiguration();
+ [JsonPropertyName("cdn")] public CdnConfiguration Cdn = new CdnConfiguration();
+ [JsonPropertyName("api")] public ApiConfiguration Api = new ApiConfiguration();
+ [JsonPropertyName("general")] public GeneralConfiguration General = new GeneralConfiguration();
+ [JsonPropertyName("limits")] public LimitsConfiguration Limits = new LimitsConfiguration();
+ [JsonPropertyName("security")] public SecurityConfiguration Security = new SecurityConfiguration();
+ [JsonPropertyName("login")] public LoginConfiguration Login = new LoginConfiguration();
+ [JsonPropertyName("register")] public RegisterConfiguration Register = new RegisterConfiguration();
+ [JsonPropertyName("regions")] public RegionConfiguration Regions = new RegionConfiguration();
+ [JsonPropertyName("guild")] public GuildConfiguration Guild = new GuildConfiguration();
+ [JsonPropertyName("gif")] public GifConfiguration Gif = new GifConfiguration();
+ [JsonPropertyName("rabbitmq")] public RabbitMQConfiguration Rabbitmq = new RabbitMQConfiguration();
+ [JsonPropertyName("kafka")] public KafkaConfiguration Kafka = new KafkaConfiguration();
+ [JsonPropertyName("templates")] public TemplateConfiguration Templates = new TemplateConfiguration();
+ [JsonPropertyName("metrics")] public MetricsConfiguration Metrics = new MetricsConfiguration();
+ [JsonPropertyName("defaults")] public DefaultsConfiguration Defaults = new DefaultsConfiguration();
+ [JsonPropertyName("external")] public ExternalTokensConfiguration External = new ExternalTokensConfiguration();
+ [JsonPropertyName("email")] public EmailConfiguration Email = new EmailConfiguration();
+ [JsonPropertyName("passwordReset")] public PasswordResetConfiguration PasswordReset = new PasswordResetConfiguration();
+ [JsonPropertyName("user")] public UserConfiguration User = new UserConfiguration();
+}
+
+public class GeneralConfiguration {
+ [JsonPropertyName("instanceName")] public string InstanceName = "Spacebar Instance";
+
+ [JsonPropertyName("instanceDescription")]
+ public string? InstanceDescription = "This is a Spacebar instance made in the pre-release days";
+
+ [JsonPropertyName("frontPage")] public string? FrontPage = null;
+ [JsonPropertyName("tosPage")] public string? TosPage = null;
+
+ [JsonPropertyName("correspondenceEmail")]
+ public string? CorrespondenceEmail = null;
+
+ [JsonPropertyName("correspondenceUserID")]
+ public string? CorrespondenceUserId = null;
+
+ [JsonPropertyName("image")] public string? Image = null;
+ [JsonPropertyName("instanceId")] public string InstanceId = Snowflake.generate();
+
+ [JsonPropertyName("autoCreateBotUsers")]
+ public bool AutoCreateBotUsers = false;
+}
+
+public class EndpointConfiguration {
+ [JsonPropertyName("endpointPrivate")]
+ public string? EndpointPrivate { get; set; }
+
+ [JsonPropertyName("endpointPublic")]
+ public string? EndpointPublic { get; set; }
+}
+
+public class ApiConfiguration : EndpointConfiguration {
+ [JsonPropertyName("activeVersions")]
+ public List<string> ActiveVersions { get; set; } = null!;
+
+ [JsonPropertyName("defaultVersion")]
+ public string DefaultVersion { get; set; } = null!;
+}
+
+public class CdnConfiguration : EndpointConfiguration {
+ [JsonPropertyName("resizeHeightMax")] public int ResizeHeightMax = 1000;
+ [JsonPropertyName("resizeWidthMax")] public int ResizeWidthMax = 1000;
+ [JsonPropertyName("imagorServerUrl")] public string? ImagorServerUrl = null;
+
+ [JsonPropertyName("proxyCacheHeaderSeconds")]
+ public int ProxyCacheHeaderSeconds = 60 * 60 * 24;
+
+ [JsonPropertyName("maxAttachmentSize")]
+ public int MaxAttachmentSize = 25 * 1024 * 1024; // 25 MB
+
+ // limits: CdnLimitsConfiguration = new CdnLimitsConfiguration();
+}
+
+public class CdnLimitsConfiguration {
+ // ordered by route register order in CDN...
+ [JsonPropertyName("icon")] public CdnImageLimitsConfiguration Icon = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("roleIcon")] public CdnImageLimitsConfiguration RoleIcon = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("emoji")] public CdnImageLimitsConfiguration Emoji = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("sticker")] public CdnImageLimitsConfiguration Sticker = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("banner")] public CdnImageLimitsConfiguration Banner = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("splash")] public CdnImageLimitsConfiguration Splash = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("avatar")] public CdnImageLimitsConfiguration Avatar = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("discoverySplash")] public CdnImageLimitsConfiguration DiscoverySplash = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("appIcon")] public CdnImageLimitsConfiguration AppIcon = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("discoverSplash")] public CdnImageLimitsConfiguration DiscoverSplash = new CdnImageLimitsConfiguration(); //what even is this?
+ [JsonPropertyName("teamIcon")] public CdnImageLimitsConfiguration TeamIcon = new CdnImageLimitsConfiguration();
+ [JsonPropertyName("channelIcon")] public CdnImageLimitsConfiguration ChannelIcon = new CdnImageLimitsConfiguration(); // is this even used?
+ [JsonPropertyName("guildAvatar")] public CdnImageLimitsConfiguration GuildAvatar = new CdnImageLimitsConfiguration();
+}
+
+public class CdnImageLimitsConfiguration {
+ [JsonPropertyName("maxHeight")] public int MaxHeight = 8192;
+ [JsonPropertyName("maxWidth")] public int MaxWidth = 8192;
+
+ [JsonPropertyName("maxSize")] public int MaxSize = 10 * 1024 * 1024; // 10 MB
+
+ // "always" | "never" | "premium"
+ [JsonPropertyName("allowAnimated")] public string AllowAnimated = "always";
+}
\ No newline at end of file
diff --git a/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/ServerConfig.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/ServerConfig.razor
new file mode 100644
index 00000000..7a83bd52
--- /dev/null
+++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/ServerConfig.razor
@@ -0,0 +1,26 @@
+@page "/ServerConfig"
+@inject Config Config
+@using System.Net.Http.Headers
+@using System.Text.Json
+@using Spacebar.AdminApi.TestClient.Services
+@using Spacebar.ConfigModel.Extensions
+<h3>Server Config</h3>
+@if (CurrentServerConfig is null) {
+ <p>Loading server config...</p>
+} else {
+ <pre>@JsonSerializer.Serialize(CurrentServerConfig, new JsonSerializerOptions { WriteIndented = true })</pre>
+}
+
+@code {
+
+ private Spacebar.ConfigModel.ServerConfiguration? CurrentServerConfig { get; set; } = new();
+
+ protected override async Task OnInitializedAsync() {
+ var hc = new StreamingHttpClient();
+ hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config.AccessToken);
+ var cfgKv = await hc.GetFromJsonAsync<Dictionary<string, string?>>($"{Config.AdminUrl}/_spacebar/admin/config");
+ var cfg = cfgKv.ToNestedJsonObject();
+ CurrentServerConfig = cfg.Deserialize<Spacebar.ConfigModel.ServerConfiguration>();
+ }
+
+}
\ No newline at end of file
|