From 9d74a4104b913536fc63d85b7370f4831be1073c Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 14 May 2022 15:50:57 -0400 Subject: [PATCH] Implement YML configuration - Sorts config better - Stored in a robust format - Migrates from old JSON format automatically on startup - Retains version migration feature - Renames ServerSettings to ServerConfiguration --- .gitignore | 1 + .idea/.idea.ProjectLighthouse/.idea/misc.xml | 5 - .../DatabaseFactAttribute.cs | 4 +- ProjectLighthouse.sln.DotSettings | 2 + .../GameApi/ClientConfigurationController.cs | 2 +- .../Controllers/GameApi/LoginController.cs | 4 +- .../Controllers/GameApi/MessageController.cs | 4 +- .../GameApi/Resources/PhotosController.cs | 4 +- .../GameApi/Slots/PublishController.cs | 8 +- .../GameApi/Slots/SlotsController.cs | 18 +- ProjectLighthouse/Database.cs | 6 +- ProjectLighthouse/Helpers/CaptchaHelper.cs | 4 +- ProjectLighthouse/Helpers/CensorHelper.cs | 4 +- .../Helpers/Extensions/RequestExtensions.cs | 2 +- ProjectLighthouse/Helpers/FileHelper.cs | 2 +- ProjectLighthouse/Helpers/InfluxHelper.cs | 7 +- ProjectLighthouse/Helpers/SMTPHelper.cs | 12 +- ProjectLighthouse/Helpers/WebhookHelper.cs | 7 +- .../Logging/Loggers/InfluxLogger.cs | 2 +- .../CompleteEmailVerificationPage.cshtml.cs | 2 +- .../ExternalAuth/AuthenticationPage.cshtml.cs | 2 +- ProjectLighthouse/Pages/LandingPage.cshtml | 2 +- .../Pages/Layouts/BaseLayout.cshtml | 8 +- ProjectLighthouse/Pages/LoginForm.cshtml | 4 +- ProjectLighthouse/Pages/LoginForm.cshtml.cs | 4 +- .../Pages/Partials/CaptchaPartial.cshtml | 4 +- .../Pages/Partials/SlotCardPartial.cshtml | 2 +- ProjectLighthouse/Pages/RegisterForm.cshtml | 4 +- .../Pages/RegisterForm.cshtml.cs | 10 +- .../Pages/SendVerificationEmailPage.cshtml.cs | 4 +- ProjectLighthouse/Pages/SetEmailForm.cshtml | 2 +- .../Pages/SetEmailForm.cshtml.cs | 4 +- ProjectLighthouse/Pages/SlotPage.cshtml | 7 +- ProjectLighthouse/Pages/SlotPage.cshtml.cs | 4 +- ProjectLighthouse/Pages/UserPage.cshtml.cs | 13 +- ProjectLighthouse/Program.cs | 10 +- ProjectLighthouse/ProjectLighthouse.csproj | 2 + .../Startup/DebugWarmupLifetime.cs | 2 +- ProjectLighthouse/Startup/Startup.cs | 16 +- ProjectLighthouse/Types/Levels/Slot.cs | 4 +- .../AuthenticationConfiguration.cs | 8 + .../CaptchaConfiguration.cs | 13 ++ .../DigestKeyConfiguration.cs | 8 + .../DiscordIntegrationConfiguration.cs | 11 + .../GoogleAnalyticsConfiguration.cs | 8 + .../InfluxDBConfiguration.cs | 16 ++ .../MailConfiguration.cs | 18 ++ .../UserGeneratedContentLimitConfiguration.cs | 21 ++ .../WebsiteConfiguration.cs | 8 + .../Settings/Legacy/LegacyServerSettings.cs | 214 ++++++++++++++++++ .../Types/Settings/ServerConfiguration.cs | 189 ++++++++++++++++ .../Types/Settings/ServerSettings.cs | 198 ---------------- ProjectLighthouse/Types/User.cs | 12 +- 53 files changed, 630 insertions(+), 302 deletions(-) create mode 100644 ProjectLighthouse/Types/Settings/ConfigurationCategories/AuthenticationConfiguration.cs create mode 100644 ProjectLighthouse/Types/Settings/ConfigurationCategories/CaptchaConfiguration.cs create mode 100644 ProjectLighthouse/Types/Settings/ConfigurationCategories/DigestKeyConfiguration.cs create mode 100644 ProjectLighthouse/Types/Settings/ConfigurationCategories/DiscordIntegrationConfiguration.cs create mode 100644 ProjectLighthouse/Types/Settings/ConfigurationCategories/GoogleAnalyticsConfiguration.cs create mode 100644 ProjectLighthouse/Types/Settings/ConfigurationCategories/InfluxDBConfiguration.cs create mode 100644 ProjectLighthouse/Types/Settings/ConfigurationCategories/MailConfiguration.cs create mode 100644 ProjectLighthouse/Types/Settings/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs create mode 100644 ProjectLighthouse/Types/Settings/ConfigurationCategories/WebsiteConfiguration.cs create mode 100644 ProjectLighthouse/Types/Settings/Legacy/LegacyServerSettings.cs create mode 100644 ProjectLighthouse/Types/Settings/ServerConfiguration.cs delete mode 100644 ProjectLighthouse/Types/Settings/ServerSettings.cs diff --git a/.gitignore b/.gitignore index 39e45d7c..2783648d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ png/ /ProjectLighthouse/r/* /ProjectLighthouse/logs/* lighthouse.config.json +lighthouse.yml gitBranch.txt gitVersion.txt gitRemotes.txt diff --git a/.idea/.idea.ProjectLighthouse/.idea/misc.xml b/.idea/.idea.ProjectLighthouse/.idea/misc.xml index 4e1d56f4..1d8c84d0 100644 --- a/.idea/.idea.ProjectLighthouse/.idea/misc.xml +++ b/.idea/.idea.ProjectLighthouse/.idea/misc.xml @@ -1,10 +1,5 @@ - - - diff --git a/ProjectLighthouse.Tests/DatabaseFactAttribute.cs b/ProjectLighthouse.Tests/DatabaseFactAttribute.cs index e9e177f8..708ae3bb 100644 --- a/ProjectLighthouse.Tests/DatabaseFactAttribute.cs +++ b/ProjectLighthouse.Tests/DatabaseFactAttribute.cs @@ -10,8 +10,8 @@ public sealed class DatabaseFactAttribute : FactAttribute public DatabaseFactAttribute() { - ServerSettings.Instance = new ServerSettings(); - ServerSettings.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; + ServerConfiguration.Instance = new ServerConfiguration(); + ServerConfiguration.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; if (!ServerStatics.DbConnected) this.Skip = "Database not available"; else lock(migrateLock) diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings index 5d18923a..fbc60c3d 100644 --- a/ProjectLighthouse.sln.DotSettings +++ b/ProjectLighthouse.sln.DotSettings @@ -76,6 +76,7 @@ UseExplicitType UseExplicitType BE + DB DDS DLC IP @@ -89,6 +90,7 @@ PSP RPCS SMTP + SSL TLS <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> diff --git a/ProjectLighthouse/Controllers/GameApi/ClientConfigurationController.cs b/ProjectLighthouse/Controllers/GameApi/ClientConfigurationController.cs index e446a657..fb0f59e4 100644 --- a/ProjectLighthouse/Controllers/GameApi/ClientConfigurationController.cs +++ b/ProjectLighthouse/Controllers/GameApi/ClientConfigurationController.cs @@ -33,7 +33,7 @@ public class ClientConfigurationController : ControllerBase "ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels false\nAllowModeratedPoppetItems false\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 240.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 60.0\nTIMEOUT_DIVE_IN_TOTAL 300.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false\nAllowOnlineCreate true" + $"TelemetryServer {hostname}\n" + $"CDNHostName {hostname}\n" + - $"ShowLevelBoos {ServerSettings.Instance.BooingEnabled.ToString().ToLower()}\n" + $"ShowLevelBoos {ServerConfiguration.Instance.UserGeneratedContentLimits.BooingEnabled.ToString().ToLower()}\n" ); } diff --git a/ProjectLighthouse/Controllers/GameApi/LoginController.cs b/ProjectLighthouse/Controllers/GameApi/LoginController.cs index 364215ff..97fc8093 100644 --- a/ProjectLighthouse/Controllers/GameApi/LoginController.cs +++ b/ProjectLighthouse/Controllers/GameApi/LoginController.cs @@ -81,9 +81,9 @@ public class LoginController : ControllerBase return this.StatusCode(403, ""); } - if (ServerSettings.Instance.UseExternalAuth) + if (ServerConfiguration.Instance.Authentication.UseExternalAuth) { - if (ServerSettings.Instance.BlockDeniedUsers) + if (ServerConfiguration.Instance.Authentication.BlockDeniedUsers) { string ipAddressAndName = $"{token.UserLocation}|{user.Username}"; if (DeniedAuthenticationHelper.RecentlyDenied(ipAddressAndName) || DeniedAuthenticationHelper.GetAttempts(ipAddressAndName) > 3) diff --git a/ProjectLighthouse/Controllers/GameApi/MessageController.cs b/ProjectLighthouse/Controllers/GameApi/MessageController.cs index 667fbcd8..3ed72850 100644 --- a/ProjectLighthouse/Controllers/GameApi/MessageController.cs +++ b/ProjectLighthouse/Controllers/GameApi/MessageController.cs @@ -41,7 +41,7 @@ along with this program. If not, see ."; User? user = await this.database.UserFromGameRequest(this.Request); if (user == null) return this.StatusCode(403, ""); - return this.Ok($"{license}\n{ServerSettings.Instance.EulaText}"); + return this.Ok($"{license}\n{ServerConfiguration.Instance.EulaText}"); } [HttpGet("announce")] @@ -60,7 +60,7 @@ along with this program. If not, see ."; GameToken gameToken = userAndToken.Value.Item2; #endif - string announceText = ServerSettings.Instance.AnnounceText; + string announceText = ServerConfiguration.Instance.AnnounceText; announceText = announceText.Replace("%user", user.Username); announceText = announceText.Replace("%id", user.UserId.ToString()); diff --git a/ProjectLighthouse/Controllers/GameApi/Resources/PhotosController.cs b/ProjectLighthouse/Controllers/GameApi/Resources/PhotosController.cs index bf646cda..0425d2d6 100644 --- a/ProjectLighthouse/Controllers/GameApi/Resources/PhotosController.cs +++ b/ProjectLighthouse/Controllers/GameApi/Resources/PhotosController.cs @@ -34,7 +34,7 @@ public class PhotosController : ControllerBase User? user = await this.database.UserFromGameRequest(this.Request); if (user == null) return this.StatusCode(403, ""); - if (user.PhotosByMe >= ServerSettings.Instance.PhotosQuota) return this.BadRequest(); + if (user.PhotosByMe >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest(); this.Request.Body.Position = 0; string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); @@ -99,7 +99,7 @@ public class PhotosController : ControllerBase { Title = "New photo uploaded!", Description = $"{user.Username} uploaded a new photo.", - ImageUrl = $"{ServerSettings.Instance.ExternalUrl}/gameAssets/{photo.LargeHash}", + ImageUrl = $"{ServerConfiguration.Instance.ExternalUrl}/gameAssets/{photo.LargeHash}", Color = WebhookHelper.UnionColor, } ); diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs b/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs index 3a86d5ab..1c8852e9 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs +++ b/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs @@ -56,7 +56,7 @@ public class PublishController : ControllerBase if (oldSlot == null) return this.NotFound(); if (oldSlot.CreatorId != user.UserId) return this.BadRequest(); } - else if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerSettings.Instance.EntitledSlots) + else if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) { return this.StatusCode(403, ""); } @@ -167,7 +167,7 @@ public class PublishController : ControllerBase return this.Ok(oldSlot.Serialize(gameToken.GameVersion)); } - if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerSettings.Instance.EntitledSlots) + if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) { return this.StatusCode(403, ""); } @@ -198,7 +198,7 @@ public class PublishController : ControllerBase await WebhookHelper.SendWebhook ( "New level published!", - $"**{user.Username}** just published a new level: [**{slot.Name}**]({ServerSettings.Instance.ExternalUrl}/slot/{slot.SlotId})\n{slot.Description}" + $"**{user.Username}** just published a new level: [**{slot.Name}**]({ServerConfiguration.Instance.ExternalUrl}/slot/{slot.SlotId})\n{slot.Description}" ); return this.Ok(slot.Serialize(gameToken.GameVersion)); @@ -232,7 +232,7 @@ public class PublishController : ControllerBase XmlSerializer serializer = new(typeof(Slot)); Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString)); - + SanitizationHelper.SanitizeStringsInClass(slot); return slot; diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs b/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs index fbcd2e66..cac415e7 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs +++ b/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs @@ -42,7 +42,7 @@ public class SlotsController : ControllerBase this.database.Slots.ByGameVersion(gameVersion, token.UserId == user.UserId, true) .Where(s => s.Creator!.Username == user.Username) .Skip(pageStart - 1) - .Take(Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)), + .Take(Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots)), string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion) ); @@ -56,7 +56,7 @@ public class SlotsController : ControllerBase new Dictionary { { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) }, { "total", user.UsedSlots @@ -135,7 +135,7 @@ public class SlotsController : ControllerBase new Dictionary { { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) }, { "total", await StatisticsHelper.SlotCount() @@ -169,7 +169,7 @@ public class SlotsController : ControllerBase new Dictionary { { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) }, { "total", await StatisticsHelper.TeamPickCount() @@ -200,7 +200,7 @@ public class SlotsController : ControllerBase new Dictionary { { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) }, { "total", await StatisticsHelper.SlotCount() @@ -244,7 +244,7 @@ public class SlotsController : ControllerBase new Dictionary { { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) }, { "total", await StatisticsHelper.SlotCount() @@ -302,7 +302,7 @@ public class SlotsController : ControllerBase new Dictionary { { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) }, { "total", await StatisticsHelper.SlotCount() @@ -346,7 +346,7 @@ public class SlotsController : ControllerBase new Dictionary { { - "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots) + "hint_start", pageStart + Math.Min(pageSize, ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) }, { "total", await StatisticsHelper.SlotCount() @@ -403,4 +403,4 @@ public class SlotsController : ControllerBase return whereSlots.Include(s => s.Creator).Include(s => s.Location); } -} +} \ No newline at end of file diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 7fdab8fa..80d7434b 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -46,7 +46,7 @@ public class Database : DbContext public DbSet EmailSetTokens { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) - => options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion); + => options.UseMySql(ServerConfiguration.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion); #nullable enable public async Task CreateUser(string username, string password, string? emailAddress = null) @@ -82,10 +82,10 @@ public class Database : DbContext await this.SaveChangesAsync(); - if (emailAddress != null && ServerSettings.Instance.SMTPEnabled) + if (emailAddress != null && ServerConfiguration.Instance.Mail.MailEnabled) { string body = "An account for Project Lighthouse has been registered with this email address.\n\n" + - $"You can login at {ServerSettings.Instance.ExternalUrl}."; + $"You can login at {ServerConfiguration.Instance.ExternalUrl}."; SMTPHelper.SendEmail(emailAddress, "Project Lighthouse Account Created: " + username, body); } diff --git a/ProjectLighthouse/Helpers/CaptchaHelper.cs b/ProjectLighthouse/Helpers/CaptchaHelper.cs index 4b124333..9751db11 100644 --- a/ProjectLighthouse/Helpers/CaptchaHelper.cs +++ b/ProjectLighthouse/Helpers/CaptchaHelper.cs @@ -18,11 +18,11 @@ public static class CaptchaHelper [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeNotEvident")] public static async Task Verify(string token) { - if (!ServerSettings.Instance.HCaptchaEnabled) return true; + if (!ServerConfiguration.Instance.Captcha.CaptchaEnabled) return true; List> payload = new() { - new("secret", ServerSettings.Instance.HCaptchaSecret), + new("secret", ServerConfiguration.Instance.Captcha.Secret), new("response", token), }; diff --git a/ProjectLighthouse/Helpers/CensorHelper.cs b/ProjectLighthouse/Helpers/CensorHelper.cs index 5b51a965..d4b28c58 100644 --- a/ProjectLighthouse/Helpers/CensorHelper.cs +++ b/ProjectLighthouse/Helpers/CensorHelper.cs @@ -21,7 +21,7 @@ public static class CensorHelper public static string ScanMessage(string message) { - if (ServerSettings.Instance.UserInputFilterMode == FilterMode.None) return message; + if (ServerConfiguration.Instance.UserInputFilterMode == FilterMode.None) return message; int profaneIndex = -1; @@ -44,7 +44,7 @@ public static class CensorHelper sb.Append(message.AsSpan(0, profanityIndex)); - switch (ServerSettings.Instance.UserInputFilterMode) + switch (ServerConfiguration.Instance.UserInputFilterMode) { case FilterMode.Random: for(int i = 0; i < profanityLength; i++) diff --git a/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs b/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs index 5f8c81d9..3f67231e 100644 --- a/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs +++ b/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs @@ -26,7 +26,7 @@ public static class RequestExtensions public static async Task CheckCaptchaValidity(this HttpRequest request) { - if (ServerSettings.Instance.HCaptchaEnabled) + if (ServerConfiguration.Instance.Captcha.CaptchaEnabled) { bool gotCaptcha = request.Form.TryGetValue("h-captcha-response", out StringValues values); if (!gotCaptcha) return false; diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Helpers/FileHelper.cs index a4e18379..7305fedb 100644 --- a/ProjectLighthouse/Helpers/FileHelper.cs +++ b/ProjectLighthouse/Helpers/FileHelper.cs @@ -20,7 +20,7 @@ public static class FileHelper public static bool IsFileSafe(LbpFile file) { - if (!ServerSettings.Instance.CheckForUnsafeFiles) return true; + if (!ServerConfiguration.Instance.CheckForUnsafeFiles) return true; if (file.FileType == LbpFileType.Unknown) return false; diff --git a/ProjectLighthouse/Helpers/InfluxHelper.cs b/ProjectLighthouse/Helpers/InfluxHelper.cs index 5a607446..b70052f7 100644 --- a/ProjectLighthouse/Helpers/InfluxHelper.cs +++ b/ProjectLighthouse/Helpers/InfluxHelper.cs @@ -14,7 +14,8 @@ namespace LBPUnion.ProjectLighthouse.Helpers; public static class InfluxHelper { - public static readonly InfluxDBClient Client = InfluxDBClientFactory.Create(ServerSettings.Instance.InfluxUrl, ServerSettings.Instance.InfluxToken); + public static readonly InfluxDBClient Client = InfluxDBClientFactory.Create + (url: ServerConfiguration.Instance.InfluxDB.Url, token: ServerConfiguration.Instance.InfluxDB.Token); private static readonly List gameVersions = new() { @@ -40,10 +41,10 @@ public static class InfluxHelper .Tag("game", gameVersion.ToString()) .Field("playerCountGame", await StatisticsHelper.RecentMatchesForGame(gameVersion)); - writeApi.WritePoint(gamePoint, ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg); + writeApi.WritePoint(gamePoint, ServerConfiguration.Instance.InfluxDB.Bucket, ServerConfiguration.Instance.InfluxDB.Organization); } - writeApi.WritePoint(point, ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg); + writeApi.WritePoint(point, ServerConfiguration.Instance.InfluxDB.Bucket, ServerConfiguration.Instance.InfluxDB.Organization); writeApi.Flush(); } diff --git a/ProjectLighthouse/Helpers/SMTPHelper.cs b/ProjectLighthouse/Helpers/SMTPHelper.cs index 01e15869..ca5b903e 100644 --- a/ProjectLighthouse/Helpers/SMTPHelper.cs +++ b/ProjectLighthouse/Helpers/SMTPHelper.cs @@ -12,20 +12,20 @@ public static class SMTPHelper static SMTPHelper() { - if (!ServerSettings.Instance.SMTPEnabled) return; + if (!ServerConfiguration.Instance.Mail.MailEnabled) return; - client = new SmtpClient(ServerSettings.Instance.SMTPHost, ServerSettings.Instance.SMTPPort) + client = new SmtpClient(ServerConfiguration.Instance.Mail.Host, ServerConfiguration.Instance.Mail.Port) { - EnableSsl = ServerSettings.Instance.SMTPSsl, - Credentials = new NetworkCredential(ServerSettings.Instance.SMTPFromAddress, ServerSettings.Instance.SMTPPassword), + EnableSsl = ServerConfiguration.Instance.Mail.UseSSL, + Credentials = new NetworkCredential(ServerConfiguration.Instance.Mail.FromAddress, ServerConfiguration.Instance.Mail.Password), }; - fromAddress = new MailAddress(ServerSettings.Instance.SMTPFromAddress, ServerSettings.Instance.SMTPFromName); + fromAddress = new MailAddress(ServerConfiguration.Instance.Mail.FromAddress, ServerConfiguration.Instance.Mail.FromName); } public static bool SendEmail(string recipientAddress, string subject, string body) { - if (!ServerSettings.Instance.SMTPEnabled) return false; + if (!ServerConfiguration.Instance.Mail.MailEnabled) return false; MailMessage message = new(fromAddress, new MailAddress(recipientAddress)) { diff --git a/ProjectLighthouse/Helpers/WebhookHelper.cs b/ProjectLighthouse/Helpers/WebhookHelper.cs index 9d0ea1c2..a708ba1a 100644 --- a/ProjectLighthouse/Helpers/WebhookHelper.cs +++ b/ProjectLighthouse/Helpers/WebhookHelper.cs @@ -7,14 +7,17 @@ namespace LBPUnion.ProjectLighthouse.Helpers; public static class WebhookHelper { - private static readonly DiscordWebhookClient client = (ServerSettings.Instance.DiscordWebhookEnabled ? new DiscordWebhookClient(ServerSettings.Instance.DiscordWebhookUrl) : null); + private static readonly DiscordWebhookClient client = (ServerConfiguration.Instance.DiscordIntegration.DiscordIntegrationEnabled + ? new DiscordWebhookClient(ServerConfiguration.Instance.DiscordIntegration.Url) + : null); + public static readonly Color UnionColor = new(0, 140, 255); public static Task SendWebhook(EmbedBuilder builder) => SendWebhook(builder.Build()); public static async Task SendWebhook(Embed embed) { - if (!ServerSettings.Instance.DiscordWebhookEnabled) return; + if (!ServerConfiguration.Instance.DiscordIntegration.DiscordIntegrationEnabled) return; await client.SendMessageAsync ( diff --git a/ProjectLighthouse/Logging/Loggers/InfluxLogger.cs b/ProjectLighthouse/Logging/Loggers/InfluxLogger.cs index 908ad38f..777bd04e 100644 --- a/ProjectLighthouse/Logging/Loggers/InfluxLogger.cs +++ b/ProjectLighthouse/Logging/Loggers/InfluxLogger.cs @@ -18,6 +18,6 @@ public class InfluxLogger : ILogger PointData point = PointData.Measurement("lighthouseLog").Field("level", level).Field("content", content); - writeApi.WritePoint(point, ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg); + writeApi.WritePoint(point, ServerConfiguration.Instance.InfluxDB.Bucket, ServerConfiguration.Instance.InfluxDB.Organization); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs b/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs index 4c40f4f9..c5934fad 100644 --- a/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs +++ b/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs @@ -18,7 +18,7 @@ public class CompleteEmailVerificationPage : BaseLayout public async Task OnGet(string token) { - if (!ServerSettings.Instance.SMTPEnabled) return this.NotFound(); + if (!ServerConfiguration.Instance.Mail.MailEnabled) return this.NotFound(); User? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); diff --git a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs index 3836a6f2..04276a88 100644 --- a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs +++ b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs @@ -21,7 +21,7 @@ public class AuthenticationPage : BaseLayout public IActionResult OnGet() { - if (!ServerSettings.Instance.UseExternalAuth) return this.NotFound(); + if (!ServerConfiguration.Instance.Authentication.UseExternalAuth) return this.NotFound(); if (this.User == null) return this.StatusCode(403, ""); this.IpAddress = this.HttpContext.Connection.RemoteIpAddress; diff --git a/ProjectLighthouse/Pages/LandingPage.cshtml b/ProjectLighthouse/Pages/LandingPage.cshtml index 47735d4c..19bbc0e6 100644 --- a/ProjectLighthouse/Pages/LandingPage.cshtml +++ b/ProjectLighthouse/Pages/LandingPage.cshtml @@ -12,7 +12,7 @@ @if (Model.User != null) {

You are currently logged in as @Model.User.Username.

- if (ServerSettings.Instance.UseExternalAuth && Model.AuthenticationAttemptsCount > 0) + if (ServerConfiguration.Instance.Authentication.UseExternalAuth && Model.AuthenticationAttemptsCount > 0) {

You have @Model.AuthenticationAttemptsCount authentication attempts pending. Click here to view them. diff --git a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml index 279ca575..0b83e680 100644 --- a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml +++ b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml @@ -11,7 +11,7 @@ } else { - if (ServerSettings.Instance.UseExternalAuth) + if (ServerConfiguration.Instance.Authentication.UseExternalAuth) { Model.NavigationItems.Add(new PageNavigationItem("Authentication", "/authentication", "key")); } @@ -62,16 +62,16 @@ @* Google Analytics *@ - @if (ServerSettings.Instance.GoogleAnalyticsEnabled) + @if (ServerConfiguration.Instance.GoogleAnalytics.AnalyticsEnabled) { - + } diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml b/ProjectLighthouse/Pages/LoginForm.cshtml index f07daa9f..74594112 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml +++ b/ProjectLighthouse/Pages/LoginForm.cshtml @@ -50,13 +50,13 @@ - @if (ServerSettings.Instance.HCaptchaEnabled) + @if (ServerConfiguration.Instance.Captcha.CaptchaEnabled) { @await Html.PartialAsync("Partials/CaptchaPartial") } - @if (ServerSettings.Instance.RegistrationEnabled) + @if (ServerConfiguration.Instance.Authentication.RegistrationEnabled) {

diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml.cs b/ProjectLighthouse/Pages/LoginForm.cshtml.cs index 3cdd72cf..d0503841 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml.cs +++ b/ProjectLighthouse/Pages/LoginForm.cshtml.cs @@ -65,7 +65,7 @@ public class LoginForm : BaseLayout return this.Page(); } - if (user.EmailAddress == null && ServerSettings.Instance.SMTPEnabled) + if (user.EmailAddress == null && ServerConfiguration.Instance.Mail.MailEnabled) { Logger.LogWarn($"User {user.Username} (id: {user.UserId}) failed to login; email not set", LogArea.Login); @@ -104,7 +104,7 @@ public class LoginForm : BaseLayout Logger.LogSuccess($"User {user.Username} (id: {user.UserId}) successfully logged in on web", LogArea.Login); if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); - if (ServerSettings.Instance.SMTPEnabled && !user.EmailAddressVerified) return this.Redirect("~/login/sendVerificationEmail"); + if (ServerConfiguration.Instance.Mail.MailEnabled && !user.EmailAddressVerified) return this.Redirect("~/login/sendVerificationEmail"); return this.RedirectToPage(nameof(LandingPage)); } diff --git a/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml b/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml index e1891722..e887bf7b 100644 --- a/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml +++ b/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml @@ -1,6 +1,6 @@ @using LBPUnion.ProjectLighthouse.Types.Settings -@if (ServerSettings.Instance.HCaptchaEnabled) +@if (ServerConfiguration.Instance.Captcha.CaptchaEnabled) { -
+
} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml b/ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml index 3852e600..7098ef52 100644 --- a/ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml +++ b/ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml @@ -26,7 +26,7 @@ bool showLink = (bool?)ViewData["ShowLink"] ?? false; string iconHash = Model.IconHash; - if (string.IsNullOrWhiteSpace(iconHash) || iconHash.StartsWith('g')) iconHash = ServerSettings.Instance.MissingIconHash; + if (string.IsNullOrWhiteSpace(iconHash) || iconHash.StartsWith('g')) iconHash = ServerConfiguration.Instance.WebsiteConfiguration.MissingIconHash; }
@{ diff --git a/ProjectLighthouse/Pages/RegisterForm.cshtml b/ProjectLighthouse/Pages/RegisterForm.cshtml index 9cbbe64e..a0d25fd8 100644 --- a/ProjectLighthouse/Pages/RegisterForm.cshtml +++ b/ProjectLighthouse/Pages/RegisterForm.cshtml @@ -43,7 +43,7 @@
- @if (ServerSettings.Instance.SMTPEnabled) + @if (ServerConfiguration.Instance.Mail.MailEnabled) {
@@ -72,7 +72,7 @@
- @if (ServerSettings.Instance.HCaptchaEnabled) + @if (ServerConfiguration.Instance.Captcha.CaptchaEnabled) { @await Html.PartialAsync("Partials/CaptchaPartial") } diff --git a/ProjectLighthouse/Pages/RegisterForm.cshtml.cs b/ProjectLighthouse/Pages/RegisterForm.cshtml.cs index b3b15110..e09bf791 100644 --- a/ProjectLighthouse/Pages/RegisterForm.cshtml.cs +++ b/ProjectLighthouse/Pages/RegisterForm.cshtml.cs @@ -22,7 +22,7 @@ public class RegisterForm : BaseLayout [SuppressMessage("ReSharper", "SpecifyStringComparison")] public async Task OnPost(string username, string password, string confirmPassword, string emailAddress) { - if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); + if (!ServerConfiguration.Instance.Authentication.RegistrationEnabled) return this.NotFound(); if (string.IsNullOrWhiteSpace(username)) { @@ -36,7 +36,7 @@ public class RegisterForm : BaseLayout return this.Page(); } - if (string.IsNullOrWhiteSpace(emailAddress) && ServerSettings.Instance.SMTPEnabled) + if (string.IsNullOrWhiteSpace(emailAddress) && ServerConfiguration.Instance.Mail.MailEnabled) { this.Error = "Email address field is required."; return this.Page(); @@ -54,7 +54,7 @@ public class RegisterForm : BaseLayout return this.Page(); } - if (ServerSettings.Instance.SMTPEnabled && + if (ServerConfiguration.Instance.Mail.MailEnabled && await this.Database.Users.FirstOrDefaultAsync(u => u.EmailAddress.ToLower() == emailAddress.ToLower()) != null) { this.Error = "The email address you've chosen is already taken."; @@ -80,7 +80,7 @@ public class RegisterForm : BaseLayout this.Response.Cookies.Append("LighthouseToken", webToken.UserToken); - if (ServerSettings.Instance.SMTPEnabled) return this.Redirect("~/login/sendVerificationEmail"); + if (ServerConfiguration.Instance.Mail.MailEnabled) return this.Redirect("~/login/sendVerificationEmail"); return this.RedirectToPage(nameof(LandingPage)); } @@ -90,7 +90,7 @@ public class RegisterForm : BaseLayout public IActionResult OnGet() { this.Error = string.Empty; - if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); + if (!ServerConfiguration.Instance.Authentication.RegistrationEnabled) return this.NotFound(); return this.Page(); } diff --git a/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs b/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs index d21b1d07..cd86d62d 100644 --- a/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs +++ b/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs @@ -17,7 +17,7 @@ public class SendVerificationEmailPage : BaseLayout public async Task OnGet() { - if (!ServerSettings.Instance.SMTPEnabled) return this.NotFound(); + if (!ServerConfiguration.Instance.Mail.MailEnabled) return this.NotFound(); User? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("/login"); @@ -47,7 +47,7 @@ public class SendVerificationEmailPage : BaseLayout string body = "Hello,\n\n" + $"This email is a request to verify this email for your (likely new!) Project Lighthouse account ({user.Username}).\n\n" + - $"To verify your account, click the following link: {ServerSettings.Instance.ExternalUrl}/verifyEmail?token={verifyToken.EmailToken}\n\n\n" + + $"To verify your account, click the following link: {ServerConfiguration.Instance.ExternalUrl}/verifyEmail?token={verifyToken.EmailToken}\n\n\n" + "If this wasn't you, feel free to ignore this email."; if (SMTPHelper.SendEmail(user.EmailAddress, "Project Lighthouse Email Verification", body)) diff --git a/ProjectLighthouse/Pages/SetEmailForm.cshtml b/ProjectLighthouse/Pages/SetEmailForm.cshtml index 2333e837..407438ae 100644 --- a/ProjectLighthouse/Pages/SetEmailForm.cshtml +++ b/ProjectLighthouse/Pages/SetEmailForm.cshtml @@ -12,7 +12,7 @@
@Html.AntiForgeryToken() - @if (ServerSettings.Instance.SMTPEnabled) + @if (ServerConfiguration.Instance.Mail.MailEnabled) {
diff --git a/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs b/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs index f46c2270..fb4d7ee2 100644 --- a/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs +++ b/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs @@ -22,7 +22,7 @@ public class SetEmailForm : BaseLayout public async Task OnGet(string? token = null) { - if (!ServerSettings.Instance.SMTPEnabled) return this.NotFound(); + if (!ServerConfiguration.Instance.Mail.MailEnabled) return this.NotFound(); if (token == null) return this.Redirect("/login"); EmailSetToken? emailToken = await this.Database.EmailSetTokens.FirstOrDefaultAsync(t => t.EmailToken == token); @@ -35,7 +35,7 @@ public class SetEmailForm : BaseLayout public async Task OnPost(string emailAddress, string token) { - if (!ServerSettings.Instance.SMTPEnabled) return this.NotFound(); + if (!ServerConfiguration.Instance.Mail.MailEnabled) return this.NotFound(); EmailSetToken? emailToken = await this.Database.EmailSetTokens.Include(t => t.User).FirstOrDefaultAsync(t => t.EmailToken == token); if (emailToken == null) return this.Redirect("/login"); diff --git a/ProjectLighthouse/Pages/SlotPage.cshtml b/ProjectLighthouse/Pages/SlotPage.cshtml index 1f8b9a96..eaed5b40 100644 --- a/ProjectLighthouse/Pages/SlotPage.cshtml +++ b/ProjectLighthouse/Pages/SlotPage.cshtml @@ -82,7 +82,7 @@ }
- @for (int i = 0; i < Model.Reviews.Count; i++) + @for(int i = 0; i < Model.Reviews.Count; i++) { Review review = Model.Reviews[i]; string faceHash = review.Thumb switch { @@ -95,10 +95,9 @@ if (string.IsNullOrWhiteSpace(faceHash)) { - faceHash = ServerSettings.Instance.MissingIconHash; + faceHash = ServerConfiguration.Instance.WebsiteConfiguration.MissingIconHash; } - string faceAlt = review.Thumb switch { -1 => "Boo!", 0 => "Meh.", @@ -111,7 +110,7 @@
- @faceAlt + @faceAlt

@review.Reviewer?.Username

diff --git a/ProjectLighthouse/Pages/SlotPage.cshtml.cs b/ProjectLighthouse/Pages/SlotPage.cshtml.cs index dee4e672..01a70915 100644 --- a/ProjectLighthouse/Pages/SlotPage.cshtml.cs +++ b/ProjectLighthouse/Pages/SlotPage.cshtml.cs @@ -18,8 +18,8 @@ public class SlotPage : BaseLayout public List Comments = new(); public List Reviews = new(); - public readonly bool CommentsEnabled = ServerSettings.Instance.LevelCommentsEnabled; - public readonly bool ReviewsEnabled = ServerSettings.Instance.LevelReviewsEnabled; + public readonly bool CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled; + public readonly bool ReviewsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelReviewsEnabled; public Slot? Slot; public SlotPage(Database database) : base(database) diff --git a/ProjectLighthouse/Pages/UserPage.cshtml.cs b/ProjectLighthouse/Pages/UserPage.cshtml.cs index 04562e55..4d1c913c 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml.cs +++ b/ProjectLighthouse/Pages/UserPage.cshtml.cs @@ -15,7 +15,7 @@ public class UserPage : BaseLayout { public List? Comments; - public bool CommentsEnabled = ServerSettings.Instance.ProfileCommentsEnabled; + public bool CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.ProfileCommentsEnabled; public bool IsProfileUserHearted; @@ -45,15 +45,16 @@ public class UserPage : BaseLayout } if (this.User == null) return this.Page(); - + foreach (Comment c in this.Comments) { - Reaction? reaction = await this.Database.Reactions.FirstOrDefaultAsync(r => r.UserId == this.User.UserId && r.TargetId == c.CommentId); - if (reaction != null) c.YourThumb = reaction.Rating; + Reaction? reaction = await this.Database.Reactions.FirstOrDefaultAsync(r => r.UserId == this.User.UserId && r.TargetId == c.CommentId); + if (reaction != null) c.YourThumb = reaction.Rating; } this.IsProfileUserHearted = await this.Database.HeartedProfiles.FirstOrDefaultAsync - (u => u.UserId == this.User.UserId && u.HeartedUserId == this.ProfileUser.UserId) != null; + (u => u.UserId == this.User.UserId && u.HeartedUserId == this.ProfileUser.UserId) != + null; return this.Page(); } -} +} \ No newline at end of file diff --git a/ProjectLighthouse/Program.cs b/ProjectLighthouse/Program.cs index 0889f0d3..2bf5a12f 100644 --- a/ProjectLighthouse/Program.cs +++ b/ProjectLighthouse/Program.cs @@ -31,7 +31,7 @@ public static class Program Logger.LogInfo($"You are running version {VersionHelper.FullVersion}", LogArea.Startup); // Referencing ServerSettings.Instance here loads the config, see ServerSettings.cs for more information - Logger.LogSuccess("Loaded config file version " + ServerSettings.Instance.ConfigVersion, LogArea.Startup); + Logger.LogSuccess("Loaded config file version " + ServerConfiguration.Instance.ConfigVersion, LogArea.Startup); Logger.LogInfo("Determining if the database is available...", LogArea.Startup); bool dbConnected = ServerStatics.DbConnected; @@ -50,11 +50,11 @@ public static class Program Logger.LogInfo("Migrating database...", LogArea.Database); MigrateDatabase(database); - if (ServerSettings.Instance.InfluxEnabled) + if (ServerConfiguration.Instance.InfluxDB.InfluxEnabled) { Logger.LogInfo("Influx logging is enabled. Starting influx logging...", LogArea.Startup); InfluxHelper.StartLogging().Wait(); - if (ServerSettings.Instance.InfluxLoggingEnabled) Logger.AddLogger(new InfluxLogger()); + if (ServerConfiguration.Instance.InfluxDB.LoggingEnabled) Logger.AddLogger(new InfluxLogger()); } Logger.LogDebug @@ -72,7 +72,7 @@ public static class Program return; } - if (ServerSettings.Instance.ConvertAssetsOnStartup) FileHelper.ConvertAllTexturesToPng(); + if (ServerConfiguration.Instance.WebsiteConfiguration.ConvertAssetsOnStartup) FileHelper.ConvertAllTexturesToPng(); Logger.LogInfo("Starting room cleanup thread...", LogArea.Startup); RoomHelper.StartCleanupThread(); @@ -102,7 +102,7 @@ public static class Program { webBuilder.UseStartup(); webBuilder.UseWebRoot("StaticFiles"); - webBuilder.UseUrls(ServerSettings.Instance.ServerListenUrl); + webBuilder.UseUrls(ServerConfiguration.Instance.ListenUrl); } ) .ConfigureLogging diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index 99261293..2f44b9ad 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -27,6 +27,7 @@ + @@ -52,6 +53,7 @@ + diff --git a/ProjectLighthouse/Startup/DebugWarmupLifetime.cs b/ProjectLighthouse/Startup/DebugWarmupLifetime.cs index f4d3ad2f..7e8fb2cd 100644 --- a/ProjectLighthouse/Startup/DebugWarmupLifetime.cs +++ b/ProjectLighthouse/Startup/DebugWarmupLifetime.cs @@ -37,7 +37,7 @@ public class DebugWarmupLifetime : IHostLifetime { using HttpClient client = new(); - string url = ServerSettings.Instance.ServerListenUrl; + string url = ServerConfiguration.Instance.ListenUrl; url = url.Replace("0.0.0.0", "127.0.0.1"); Logger.LogDebug("Warming up Hot Reload...", LogArea.Startup); diff --git a/ProjectLighthouse/Startup/Startup.cs b/ProjectLighthouse/Startup/Startup.cs index a2a149f8..4c10ab80 100644 --- a/ProjectLighthouse/Startup/Startup.cs +++ b/ProjectLighthouse/Startup/Startup.cs @@ -96,7 +96,7 @@ public class Startup { bool computeDigests = true; - if (string.IsNullOrEmpty(ServerSettings.Instance.ServerDigestKey)) + if (string.IsNullOrEmpty(ServerConfiguration.Instance.DigestKey.PrimaryDigestKey)) { Logger.LogWarn ( @@ -172,7 +172,8 @@ public class Startup if (computeDigests && digestPath.StartsWith("/LITTLEBIGPLANETPS3_XML")) { - string clientRequestDigest = await CryptoHelper.ComputeDigest(digestPath, authCookie, body, ServerSettings.Instance.ServerDigestKey); + string clientRequestDigest = await CryptoHelper.ComputeDigest + (digestPath, authCookie, body, ServerConfiguration.Instance.DigestKey.PrimaryDigestKey); // Check the digest we've just calculated against the X-Digest-A header if the game set the header. They should match. if (context.Request.Headers.TryGetValue("X-Digest-A", out StringValues sentDigest)) @@ -185,13 +186,14 @@ public class Startup // Reset the body stream body.Position = 0; - clientRequestDigest = await CryptoHelper.ComputeDigest(digestPath, authCookie, body, ServerSettings.Instance.AlternateDigestKey); + clientRequestDigest = await CryptoHelper.ComputeDigest + (digestPath, authCookie, body, ServerConfiguration.Instance.DigestKey.AlternateDigestKey); if (clientRequestDigest != sentDigest) { #if DEBUG Console.WriteLine("Digest failed"); - Console.WriteLine("digestKey: " + ServerSettings.Instance.ServerDigestKey); - Console.WriteLine("altDigestKey: " + ServerSettings.Instance.AlternateDigestKey); + Console.WriteLine("digestKey: " + ServerConfiguration.Instance.DigestKey.PrimaryDigestKey); + Console.WriteLine("altDigestKey: " + ServerConfiguration.Instance.DigestKey.AlternateDigestKey); Console.WriteLine("computed digest: " + clientRequestDigest); #endif // We still failed to validate. Abort the request. @@ -218,7 +220,9 @@ public class Startup { responseBuffer.Position = 0; - string digestKey = usedAlternateDigestKey ? ServerSettings.Instance.AlternateDigestKey : ServerSettings.Instance.ServerDigestKey; + string digestKey = usedAlternateDigestKey + ? ServerConfiguration.Instance.DigestKey.AlternateDigestKey + : ServerConfiguration.Instance.DigestKey.PrimaryDigestKey; // Compute the digest for the response. string serverDigest = await CryptoHelper.ComputeDigest(context.Request.Path, authCookie, responseBuffer, digestKey); diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Types/Levels/Slot.cs index 481bdfab..39948064 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Types/Levels/Slot.cs @@ -289,8 +289,8 @@ public class Slot LbpSerializer.StringElement("yourlbpPlayCount", yourVisitedStats?.PlaysLBP1) + LbpSerializer.StringElement("yourlbp3PlayCount", yourVisitedStats?.PlaysLBP3) + yourReview?.Serialize("yourReview") + - LbpSerializer.StringElement("reviewsEnabled", ServerSettings.Instance.LevelReviewsEnabled) + - LbpSerializer.StringElement("commentsEnabled", ServerSettings.Instance.LevelCommentsEnabled) + + LbpSerializer.StringElement("reviewsEnabled", ServerConfiguration.Instance.UserGeneratedContentLimits.LevelReviewsEnabled) + + LbpSerializer.StringElement("commentsEnabled", ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled) + LbpSerializer.StringElement("playerCount", playerCount) + LbpSerializer.StringElement("reviewCount", this.ReviewCount); diff --git a/ProjectLighthouse/Types/Settings/ConfigurationCategories/AuthenticationConfiguration.cs b/ProjectLighthouse/Types/Settings/ConfigurationCategories/AuthenticationConfiguration.cs new file mode 100644 index 00000000..b94e3699 --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ConfigurationCategories/AuthenticationConfiguration.cs @@ -0,0 +1,8 @@ +namespace LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +public class AuthenticationConfiguration +{ + public bool BlockDeniedUsers { get; set; } = true; + public bool RegistrationEnabled { get; set; } = true; + public bool UseExternalAuth { get; set; } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ConfigurationCategories/CaptchaConfiguration.cs b/ProjectLighthouse/Types/Settings/ConfigurationCategories/CaptchaConfiguration.cs new file mode 100644 index 00000000..fcbd1532 --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ConfigurationCategories/CaptchaConfiguration.cs @@ -0,0 +1,13 @@ +namespace LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +public class CaptchaConfiguration +{ + // TODO: support recaptcha, not just hcaptcha + // use an enum to define which captcha services can be used? + // LBPUnion.ProjectLighthouse.Types.Settings.CaptchaService + public bool CaptchaEnabled { get; set; } + + public string SiteKey { get; set; } = ""; + + public string Secret { get; set; } = ""; +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ConfigurationCategories/DigestKeyConfiguration.cs b/ProjectLighthouse/Types/Settings/ConfigurationCategories/DigestKeyConfiguration.cs new file mode 100644 index 00000000..5f2f858e --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ConfigurationCategories/DigestKeyConfiguration.cs @@ -0,0 +1,8 @@ +namespace LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +public class DigestKeyConfiguration +{ + // todo: move to list? + public string PrimaryDigestKey { get; set; } = ""; + public string AlternateDigestKey { get; set; } = ""; +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ConfigurationCategories/DiscordIntegrationConfiguration.cs b/ProjectLighthouse/Types/Settings/ConfigurationCategories/DiscordIntegrationConfiguration.cs new file mode 100644 index 00000000..c3563b54 --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ConfigurationCategories/DiscordIntegrationConfiguration.cs @@ -0,0 +1,11 @@ +#nullable enable +namespace LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +public class DiscordIntegrationConfiguration +{ + //TODO: integrations should be modular/abstracted away + + public bool DiscordIntegrationEnabled { get; set; } + + public string Url { get; set; } = ""; +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ConfigurationCategories/GoogleAnalyticsConfiguration.cs b/ProjectLighthouse/Types/Settings/ConfigurationCategories/GoogleAnalyticsConfiguration.cs new file mode 100644 index 00000000..84526c28 --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ConfigurationCategories/GoogleAnalyticsConfiguration.cs @@ -0,0 +1,8 @@ +namespace LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +public class GoogleAnalyticsConfiguration +{ + public bool AnalyticsEnabled { get; set; } + + public string Id { get; set; } = ""; +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ConfigurationCategories/InfluxDBConfiguration.cs b/ProjectLighthouse/Types/Settings/ConfigurationCategories/InfluxDBConfiguration.cs new file mode 100644 index 00000000..c94d5aa6 --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ConfigurationCategories/InfluxDBConfiguration.cs @@ -0,0 +1,16 @@ +namespace LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +public class InfluxDBConfiguration +{ + public bool InfluxEnabled { get; set; } + + /// + /// Whether or not to log to InfluxDB. + /// + public bool LoggingEnabled { get; set; } + + public string Organization { get; set; } = "lighthouse"; + public string Bucket { get; set; } = "lighthouse"; + public string Token { get; set; } = ""; + public string Url { get; set; } = "http://localhost:8086"; +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ConfigurationCategories/MailConfiguration.cs b/ProjectLighthouse/Types/Settings/ConfigurationCategories/MailConfiguration.cs new file mode 100644 index 00000000..2f3985b9 --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ConfigurationCategories/MailConfiguration.cs @@ -0,0 +1,18 @@ +namespace LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +public class MailConfiguration +{ + public bool MailEnabled { get; set; } + + public string Host { get; set; } = ""; + + public int Port { get; set; } = 587; + + public string FromAddress { get; set; } = "lighthouse@example.com"; + + public string FromName { get; set; } = "Project Lighthouse"; + + public string Password { get; set; } = ""; + + public bool UseSSL { get; set; } = true; +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs b/ProjectLighthouse/Types/Settings/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs new file mode 100644 index 00000000..14459ded --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs @@ -0,0 +1,21 @@ +namespace LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +public class UserGeneratedContentLimitConfiguration +{ + /// + /// The maximum amount of slots allowed on users' earth + /// + public int EntitledSlots { get; set; } = 50; + + public int ListsQuota { get; set; } = 50; + + public int PhotosQuota { get; set; } = 500; + + public bool ProfileCommentsEnabled { get; set; } = true; + + public bool LevelCommentsEnabled { get; set; } = true; + + public bool LevelReviewsEnabled { get; set; } = true; + + public bool BooingEnabled { get; set; } = true; +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ConfigurationCategories/WebsiteConfiguration.cs b/ProjectLighthouse/Types/Settings/ConfigurationCategories/WebsiteConfiguration.cs new file mode 100644 index 00000000..5715b795 --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ConfigurationCategories/WebsiteConfiguration.cs @@ -0,0 +1,8 @@ +namespace LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +public class WebsiteConfiguration +{ + public string MissingIconHash { get; set; } = ""; + + public bool ConvertAssetsOnStartup { get; set; } = true; +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/Legacy/LegacyServerSettings.cs b/ProjectLighthouse/Types/Settings/Legacy/LegacyServerSettings.cs new file mode 100644 index 00000000..2542299b --- /dev/null +++ b/ProjectLighthouse/Types/Settings/Legacy/LegacyServerSettings.cs @@ -0,0 +1,214 @@ +using System.IO; +using System.Text.Json; +using LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; + +namespace LBPUnion.ProjectLighthouse.Types.Settings.Legacy; +#nullable enable + +internal class LegacyServerSettings +{ + + #region Meta + + public const string ConfigFileName = "lighthouse.config.json"; + + #endregion + + #region InfluxDB + + public bool InfluxEnabled { get; set; } + public bool InfluxLoggingEnabled { get; set; } + public string InfluxOrg { get; set; } = "lighthouse"; + public string InfluxBucket { get; set; } = "lighthouse"; + public string InfluxToken { get; set; } = ""; + public string InfluxUrl { get; set; } = "http://localhost:8086"; + + #endregion + + public string EulaText { get; set; } = ""; + + #if !DEBUG + public string AnnounceText { get; set; } = "You are now logged in as %user."; + #else + public string AnnounceText { get; set; } = "You are now logged in as %user (id: %id)."; + #endif + + public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; + + public string ExternalUrl { get; set; } = "http://localhost:10060"; + public string ServerDigestKey { get; set; } = ""; + public string AlternateDigestKey { get; set; } = ""; + public bool UseExternalAuth { get; set; } + + public bool CheckForUnsafeFiles { get; set; } = true; + + public bool RegistrationEnabled { get; set; } = true; + + #region UGC Limits + + /// + /// The maximum amount of slots allowed on users' earth + /// + public int EntitledSlots { get; set; } = 50; + + public int ListsQuota { get; set; } = 50; + + public int PhotosQuota { get; set; } = 500; + + public bool ProfileCommentsEnabled { get; set; } = true; + + public bool LevelCommentsEnabled { get; set; } = true; + + public bool LevelReviewsEnabled { get; set; } = true; + + #endregion + + #region Google Analytics + + public bool GoogleAnalyticsEnabled { get; set; } + + public string GoogleAnalyticsId { get; set; } = ""; + + #endregion + + public bool BlockDeniedUsers { get; set; } = true; + + public bool BooingEnabled { get; set; } = true; + + public FilterMode UserInputFilterMode { get; set; } = FilterMode.None; + + #region Discord Webhook + + public bool DiscordWebhookEnabled { get; set; } + + public string DiscordWebhookUrl { get; set; } = ""; + + #endregion + + public bool ConfigReloading { get; set; } = true; + + public string MissingIconHash { get; set; } = ""; + + #region HCaptcha + + public bool HCaptchaEnabled { get; set; } + + public string HCaptchaSiteKey { get; set; } = ""; + + public string HCaptchaSecret { get; set; } = ""; + + #endregion + + public string ServerListenUrl { get; set; } = "http://localhost:10060"; + + public bool ConvertAssetsOnStartup { get; set; } = true; + + #region SMTP + + public bool SMTPEnabled { get; set; } + + public string SMTPHost { get; set; } = ""; + + public int SMTPPort { get; set; } = 587; + + public string SMTPFromAddress { get; set; } = "lighthouse@example.com"; + + public string SMTPFromName { get; set; } = "Project Lighthouse"; + + public string SMTPPassword { get; set; } = ""; + + public bool SMTPSsl { get; set; } = true; + + #endregion + + internal static LegacyServerSettings? FromFile(string path) + { + string data = File.ReadAllText(path); + return JsonSerializer.Deserialize(data); + } + + internal ServerConfiguration ToNewConfiguration() + { + ServerConfiguration configuration = new(); + configuration.ConfigReloading = this.ConfigReloading; + configuration.AnnounceText = this.AnnounceText; + configuration.EulaText = this.EulaText; + configuration.ExternalUrl = this.ExternalUrl; + configuration.DbConnectionString = this.DbConnectionString; + configuration.CheckForUnsafeFiles = this.CheckForUnsafeFiles; + configuration.UserInputFilterMode = this.UserInputFilterMode; + + // configuration categories + configuration.InfluxDB = new InfluxDBConfiguration + { + InfluxEnabled = this.InfluxEnabled, + LoggingEnabled = this.InfluxLoggingEnabled, + Bucket = this.InfluxBucket, + Organization = this.InfluxOrg, + Token = this.InfluxToken, + Url = InfluxUrl, + }; + + configuration.Authentication = new AuthenticationConfiguration + { + RegistrationEnabled = this.RegistrationEnabled, + BlockDeniedUsers = this.BlockDeniedUsers, + UseExternalAuth = this.UseExternalAuth, + }; + + configuration.Captcha = new CaptchaConfiguration + { + CaptchaEnabled = this.HCaptchaEnabled, + SiteKey = this.HCaptchaSiteKey, + Secret = this.HCaptchaSecret, + }; + + configuration.Mail = new MailConfiguration + { + MailEnabled = this.SMTPEnabled, + Host = this.SMTPHost, + Password = this.SMTPPassword, + Port = this.SMTPPort, + FromAddress = this.SMTPFromAddress, + FromName = this.SMTPFromName, + UseSSL = this.SMTPSsl, + }; + + configuration.DigestKey = new DigestKeyConfiguration + { + PrimaryDigestKey = this.ServerDigestKey, + AlternateDigestKey = this.AlternateDigestKey, + }; + + configuration.DiscordIntegration = new DiscordIntegrationConfiguration + { + DiscordIntegrationEnabled = this.DiscordWebhookEnabled, + Url = this.DiscordWebhookUrl, + }; + + configuration.GoogleAnalytics = new GoogleAnalyticsConfiguration + { + AnalyticsEnabled = this.GoogleAnalyticsEnabled, + Id = this.GoogleAnalyticsId, + }; + + configuration.UserGeneratedContentLimits = new UserGeneratedContentLimitConfiguration + { + BooingEnabled = this.BooingEnabled, + EntitledSlots = this.EntitledSlots, + ListsQuota = this.ListsQuota, + PhotosQuota = this.PhotosQuota, + LevelCommentsEnabled = this.LevelCommentsEnabled, + LevelReviewsEnabled = this.LevelReviewsEnabled, + ProfileCommentsEnabled = this.ProfileCommentsEnabled, + }; + + configuration.WebsiteConfiguration = new WebsiteConfiguration + { + MissingIconHash = this.MissingIconHash, + ConvertAssetsOnStartup = this.ConvertAssetsOnStartup, + }; + + return configuration; + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ServerConfiguration.cs b/ProjectLighthouse/Types/Settings/ServerConfiguration.cs new file mode 100644 index 00000000..39518f1e --- /dev/null +++ b/ProjectLighthouse/Types/Settings/ServerConfiguration.cs @@ -0,0 +1,189 @@ +#nullable enable +using System; +using System.Diagnostics; +using System.IO; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Types.Settings.ConfigurationCategories; +using LBPUnion.ProjectLighthouse.Types.Settings.Legacy; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace LBPUnion.ProjectLighthouse.Types.Settings; + +[Serializable] +public class ServerConfiguration +{ + // HEY, YOU! + // THIS VALUE MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! + // + // This is so Lighthouse can properly identify outdated configurations and update them with newer settings accordingly. + // If you are modifying anything here that isn't outside of a method, this value MUST be incremented. + // It is also strongly recommended to not remove any items, else it will cause deserialization errors. + // You can use an ObsoleteAttribute instead. Make sure you set it to error, though. + // + // Thanks for listening~ + public const int CurrentConfigVersion = 1; + + #region Meta + + public static ServerConfiguration Instance; + + [YamlMember(Alias = "configVersionDoNotModifyOrYouWillBeSlapped")] + public int ConfigVersion { get; set; } = CurrentConfigVersion; + + public const string ConfigFileName = "lighthouse.yml"; + public const string LegacyConfigFileName = LegacyServerSettings.ConfigFileName; + + #endregion Meta + + #region Setup + + private static FileSystemWatcher fileWatcher; + + // ReSharper disable once NotNullMemberIsNotInitialized + #pragma warning disable CS8618 + static ServerConfiguration() + { + if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own + + Logger.LogInfo("Loading config...", LogArea.Config); + + ServerConfiguration? tempConfig; + + // If a valid YML configuration is available! + if (File.Exists(ConfigFileName) && (tempConfig = fromFile(ConfigFileName)) != null) + { +// Instance = JsonSerializer.Deserialize(configFile) ?? throw new ArgumentNullException(nameof(ConfigFileName)); + Instance = tempConfig; + + if (Instance.ConfigVersion < CurrentConfigVersion) + { + Logger.LogInfo($"Upgrading config file from version {Instance.ConfigVersion} to version {CurrentConfigVersion}", LogArea.Config); + Instance.ConfigVersion = CurrentConfigVersion; + + Instance.writeConfig(ConfigFileName); + } + } + // If we have a valid legacy configuration we can migrate, let's do it now. + else if (File.Exists(LegacyConfigFileName)) + { + Logger.LogWarn("This version of Project Lighthouse now uses YML instead of JSON to store configuration.", LogArea.Config); + Logger.LogWarn + ("As such, the config will now be migrated to use YML. Do not modify the original JSON file; changes will not be kept.", LogArea.Config); + Logger.LogInfo($"The new configuration is stored at {ConfigFileName}.", LogArea.Config); + + LegacyServerSettings? legacyConfig = LegacyServerSettings.FromFile(LegacyConfigFileName); + Debug.Assert(legacyConfig != null); + Instance = legacyConfig.ToNewConfiguration(); + + Instance.writeConfig(ConfigFileName); + + Logger.LogSuccess("The configuration migration completed successfully.", LogArea.Config); + } + // If there is no valid YML configuration available, + // generate a blank one and ask the server operator to configure it, then exit. + else + { + new ServerConfiguration().writeConfig(ConfigFileName + ".configme"); + + Logger.LogWarn + ( + "The configuration file was not found. " + + "A blank configuration file has been created for you at " + + $"{Path.Combine(Environment.CurrentDirectory, ConfigFileName + ".configme")}", + LogArea.Config + ); + + Environment.Exit(1); + } + + // Set up reloading + if (Instance.ConfigReloading) + { + Logger.LogInfo("Setting up config reloading...", LogArea.Config); + fileWatcher = new FileSystemWatcher + { + Path = Environment.CurrentDirectory, + Filter = ConfigFileName, + NotifyFilter = NotifyFilters.LastWrite, // only watch for writes to config file + }; + + fileWatcher.Changed += onConfigChanged; // add event handler + + fileWatcher.EnableRaisingEvents = true; // begin watching + } + } + #pragma warning restore CS8618 + + private static void onConfigChanged(object sender, FileSystemEventArgs e) + { + Debug.Assert(e.Name == ConfigFileName); + Logger.LogInfo("Configuration file modified, reloading config...", LogArea.Config); + Logger.LogWarn("Some changes may not apply; they will require a restart of Lighthouse.", LogArea.Config); + + ServerConfiguration? configuration = fromFile(ConfigFileName); + if (configuration == null) + { + Logger.LogWarn("The new configuration was unable to be loaded for some reason. The old config has been kept.", LogArea.Config); + return; + } + + Instance = configuration; + + Logger.LogSuccess("Successfully reloaded the configuration!", LogArea.Config); + } + + private static INamingConvention namingConvention = CamelCaseNamingConvention.Instance; + + private static ServerConfiguration? fromFile(string path) + { + IDeserializer deserializer = new DeserializerBuilder().WithNamingConvention(namingConvention).Build(); + + string text; + + try + { + text = File.ReadAllText(path); + } + catch + { + return null; + } + + return deserializer.Deserialize(text); + } + + private void writeConfig(string path) + { + ISerializer serializer = new SerializerBuilder().WithNamingConvention(namingConvention).Build(); + + File.WriteAllText(path, serializer.Serialize(this)); + } + + #endregion + + public string ListenUrl { get; set; } = "http://localhost:10060"; + public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; + public string ExternalUrl { get; set; } = "http://localhost:10060"; + public bool ConfigReloading { get; set; } + public string EulaText { get; set; } = ""; + #if !DEBUG + public string AnnounceText { get; set; } = "You are now logged in as %user."; + #else + public string AnnounceText { get; set; } = "You are now logged in as %user (id: %id)."; + #endif + public bool CheckForUnsafeFiles { get; set; } = true; + + public FilterMode UserInputFilterMode { get; set; } = FilterMode.None; + + public AuthenticationConfiguration Authentication { get; set; } = new(); + public CaptchaConfiguration Captcha { get; set; } = new(); + public DigestKeyConfiguration DigestKey { get; set; } = new(); + public DiscordIntegrationConfiguration DiscordIntegration { get; set; } = new(); + public GoogleAnalyticsConfiguration GoogleAnalytics { get; set; } = new(); + public InfluxDBConfiguration InfluxDB { get; set; } = new(); + public MailConfiguration Mail { get; set; } = new(); + public UserGeneratedContentLimitConfiguration UserGeneratedContentLimits { get; set; } = new(); + public WebsiteConfiguration WebsiteConfiguration { get; set; } = new(); + +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ServerSettings.cs b/ProjectLighthouse/Types/Settings/ServerSettings.cs deleted file mode 100644 index b5c9c34a..00000000 --- a/ProjectLighthouse/Types/Settings/ServerSettings.cs +++ /dev/null @@ -1,198 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Text.Json; -using System.Text.Json.Serialization; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Logging; - -namespace LBPUnion.ProjectLighthouse.Types.Settings; - -[Serializable] -public class ServerSettings -{ - public const int CurrentConfigVersion = 26; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! - private static FileSystemWatcher fileWatcher; - - // ReSharper disable once NotNullMemberIsNotInitialized - static ServerSettings() - { - if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own - - Logger.LogInfo("Loading config...", LogArea.Config); - - if (File.Exists(ConfigFileName)) - { - string configFile = File.ReadAllText(ConfigFileName); - - Instance = JsonSerializer.Deserialize(configFile) ?? throw new ArgumentNullException(nameof(ConfigFileName)); - - if (Instance.ConfigVersion < CurrentConfigVersion) - { - Logger.LogInfo($"Upgrading config file from version {Instance.ConfigVersion} to version {CurrentConfigVersion}", LogArea.Config); - Instance.ConfigVersion = CurrentConfigVersion; - configFile = JsonSerializer.Serialize - ( - Instance, - typeof(ServerSettings), - new JsonSerializerOptions - { - WriteIndented = true, - } - ); - - File.WriteAllText(ConfigFileName, configFile); - } - } - else - { - string configFile = JsonSerializer.Serialize - ( - new ServerSettings(), - typeof(ServerSettings), - new JsonSerializerOptions - { - WriteIndented = true, - } - ); - - File.WriteAllText(ConfigFileName, configFile); - - Logger.LogWarn - ( - "The configuration file was not found. " + - "A blank configuration file has been created for you at " + - $"{Path.Combine(Environment.CurrentDirectory, ConfigFileName)}", - LogArea.Config - ); - - Environment.Exit(1); - } - - // Set up reloading - if (Instance.ConfigReloading) - { - Logger.LogInfo("Setting up config reloading...", LogArea.Config); - fileWatcher = new FileSystemWatcher - { - Path = Environment.CurrentDirectory, - Filter = ConfigFileName, - NotifyFilter = NotifyFilters.LastWrite, // only watch for writes to config file - }; - - fileWatcher.Changed += onConfigChanged; // add event handler - - fileWatcher.EnableRaisingEvents = true; // begin watching - } - } - - private static void onConfigChanged(object sender, FileSystemEventArgs e) - { - Debug.Assert(e.Name == ConfigFileName); - Logger.LogInfo("Configuration file modified, reloading config.", LogArea.Config); - Logger.LogWarn("Some changes may not apply, in which case may require a restart of Project Lighthouse.", LogArea.Config); - - string configFile = File.ReadAllText(ConfigFileName); - Instance = JsonSerializer.Deserialize(configFile) ?? throw new ArgumentNullException(nameof(ConfigFileName)); - } - - public bool InfluxEnabled { get; set; } - public bool InfluxLoggingEnabled { get; set; } - public string InfluxOrg { get; set; } = "lighthouse"; - public string InfluxBucket { get; set; } = "lighthouse"; - public string InfluxToken { get; set; } = ""; - public string InfluxUrl { get; set; } = "http://localhost:8086"; - - public string EulaText { get; set; } = ""; - - #if !DEBUG - public string AnnounceText { get; set; } = "You are now logged in as %user."; - #else - public string AnnounceText { get; set; } = "You are now logged in as %user (id: %id)."; - #endif - - public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; - - public string ExternalUrl { get; set; } = "http://localhost:10060"; - public string ServerDigestKey { get; set; } = ""; - public string AlternateDigestKey { get; set; } = ""; - public bool UseExternalAuth { get; set; } - - public bool CheckForUnsafeFiles { get; set; } = true; - - public bool RegistrationEnabled { get; set; } = true; - - /// - /// The maximum amount of slots allowed on users' earth - /// - public int EntitledSlots { get; set; } = 50; - - public int ListsQuota { get; set; } = 50; - - public int PhotosQuota { get; set; } = 500; - - public bool ProfileCommentsEnabled { get; set; } = true; - - public bool LevelCommentsEnabled { get; set; } = true; - - public bool LevelReviewsEnabled { get; set; } = true; - - public bool GoogleAnalyticsEnabled { get; set; } - - public string GoogleAnalyticsId { get; set; } = ""; - - public bool BlockDeniedUsers { get; set; } = true; - - public bool BooingEnabled { get; set; } = true; - - public FilterMode UserInputFilterMode { get; set; } = FilterMode.None; - - public bool DiscordWebhookEnabled { get; set; } - - public string DiscordWebhookUrl { get; set; } = ""; - - public bool ConfigReloading { get; set; } = true; - - public string MissingIconHash { get; set; } = ""; - - public bool HCaptchaEnabled { get; set; } - - public string HCaptchaSiteKey { get; set; } = ""; - - public string HCaptchaSecret { get; set; } = ""; - - public string ServerListenUrl { get; set; } = "http://localhost:10060"; - - public bool ConvertAssetsOnStartup { get; set; } = true; - - #region SMTP - - public bool SMTPEnabled { get; set; } - - public string SMTPHost { get; set; } = ""; - - public int SMTPPort { get; set; } = 587; - - public string SMTPFromAddress { get; set; } = "lighthouse@example.com"; - - public string SMTPFromName { get; set; } = "Project Lighthouse"; - - public string SMTPPassword { get; set; } = ""; - - public bool SMTPSsl { get; set; } = true; - - #endregion - - #region Meta - - [NotNull] - public static ServerSettings Instance; - - [JsonPropertyName("ConfigVersionDoNotModifyOrYouWillBeSlapped")] - public int ConfigVersion { get; set; } = CurrentConfigVersion; - - public const string ConfigFileName = "lighthouse.config.json"; - - #endregion Meta - -} \ No newline at end of file diff --git a/ProjectLighthouse/Types/User.cs b/ProjectLighthouse/Types/User.cs index b73fcecb..669c7135 100644 --- a/ProjectLighthouse/Types/User.cs +++ b/ProjectLighthouse/Types/User.cs @@ -64,7 +64,7 @@ public class User if (string.IsNullOrWhiteSpace(avatarHash) || this.IconHash.StartsWith('g')) avatarHash = this.YayHash; if (string.IsNullOrWhiteSpace(avatarHash)) avatarHash = this.MehHash; if (string.IsNullOrWhiteSpace(avatarHash)) avatarHash = this.BooHash; - if (string.IsNullOrWhiteSpace(avatarHash)) avatarHash = ServerSettings.Instance.MissingIconHash; + if (string.IsNullOrWhiteSpace(avatarHash)) avatarHash = ServerConfiguration.Instance.WebsiteConfiguration.MissingIconHash; return avatarHash; } @@ -149,13 +149,17 @@ public class User LbpSerializer.StringElement("game", (int)gameVersion) + this.serializeSlots(gameVersion) + LbpSerializer.StringElement("lists", this.Lists) + - LbpSerializer.StringElement("lists_quota", ServerSettings.Instance.ListsQuota) + // technically not a part of the user but LBP expects it + LbpSerializer.StringElement + ( + "lists_quota", + ServerConfiguration.Instance.UserGeneratedContentLimits.ListsQuota + ) + // technically not a part of the user but LBP expects it LbpSerializer.StringElement("biography", this.Biography) + LbpSerializer.StringElement("reviewCount", this.Reviews) + LbpSerializer.StringElement("commentCount", this.Comments) + LbpSerializer.StringElement("photosByMeCount", this.PhotosByMe) + LbpSerializer.StringElement("photosWithMeCount", this.PhotosWithMe) + - LbpSerializer.StringElement("commentsEnabled", ServerSettings.Instance.ProfileCommentsEnabled) + + LbpSerializer.StringElement("commentsEnabled", ServerConfiguration.Instance.UserGeneratedContentLimits.ProfileCommentsEnabled) + LbpSerializer.StringElement("location", this.Location.Serialize()) + LbpSerializer.StringElement("favouriteSlotCount", this.HeartedLevels) + LbpSerializer.StringElement("favouriteUserCount", this.HeartedUsers) + @@ -204,7 +208,7 @@ public class User [JsonIgnore] [XmlIgnore] - public int EntitledSlots => ServerSettings.Instance.EntitledSlots + this.AdminGrantedSlots; + public int EntitledSlots => ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots + this.AdminGrantedSlots; /// /// The number of slots remaining on the earth