diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 5060fd65..e80b80ff 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "6.0.4", + "version": "6.0.5", "commands": [ "dotnet-ef" ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 549f2ba3..e5851b4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: matrix: os: - { prettyName: Linux, fullName: ubuntu-latest, database: true, webTest: true } - timeout-minutes: 10 + timeout-minutes: 5 env: DB_DATABASE: lighthouse DB_USER: root @@ -44,16 +44,16 @@ jobs: - name: Run tests on ProjectLighthouse.Tests continue-on-error: true - run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-Tests.trx" ProjectLighthouse.Tests + run: dotnet test --no-build --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-Tests.trx" ProjectLighthouse.Tests - name: Run tests on ProjectLighthouse.Tests.GameApiTests continue-on-error: true - run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-GameApiTests.trx" ProjectLighthouse.Tests.GameApiTests + run: dotnet test --no-build --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-GameApiTests.trx" ProjectLighthouse.Tests.GameApiTests - name: Run tests on ProjectLighthouse.Tests.WebsiteTests if: ${{ matrix.os.webTest }} continue-on-error: true - run: dotnet test --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-WebsiteTests.trx" ProjectLighthouse.Tests.WebsiteTests + run: dotnet test --no-build --logger "trx;LogFileName=${{github.workspace}}/TestResults-${{matrix.os.prettyName}}-WebsiteTests.trx" ProjectLighthouse.Tests.WebsiteTests # Attempt to upload results even if test fails. # https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always 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/indexLayout.xml b/.idea/.idea.ProjectLighthouse/.idea/indexLayout.xml index 69ba74a2..aff6dfa9 100644 --- a/.idea/.idea.ProjectLighthouse/.idea/indexLayout.xml +++ b/.idea/.idea.ProjectLighthouse/.idea/indexLayout.xml @@ -3,11 +3,13 @@ + .config/dotnet-tools.json .github .gitignore .idea CONTRIBUTING.md DatabaseMigrations + LICENSE ProjectLighthouse.sln.DotSettings ProjectLighthouse.sln.DotSettings.user README.md @@ -15,6 +17,7 @@ crowdin.yml docker-compose.yml global.json + scripts-and-tools .idea/.idea.ProjectLighthouse/.idea/workspace.xml diff --git a/.idea/.idea.ProjectLighthouse/.idea/vcs.xml b/.idea/.idea.ProjectLighthouse/.idea/vcs.xml index 588033f3..94a25f7f 100644 --- a/.idea/.idea.ProjectLighthouse/.idea/vcs.xml +++ b/.idea/.idea.ProjectLighthouse/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/.run/Development Database.run.xml b/.run/Development Database.run.xml index deac13d8..63dcf575 100644 --- a/.run/Development Database.run.xml +++ b/.run/Development Database.run.xml @@ -1,5 +1,5 @@ - @@ -7,6 +7,7 @@ + \ No newline at end of file diff --git a/.run/Lighthouse API.run.xml b/.run/Lighthouse API.run.xml new file mode 100644 index 00000000..08ab248a --- /dev/null +++ b/.run/Lighthouse API.run.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/.run/Lighthouse Game API.run.xml b/.run/Lighthouse Game API.run.xml new file mode 100644 index 00000000..73f4a935 --- /dev/null +++ b/.run/Lighthouse Game API.run.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/.run/Lighthouse Website.run.xml b/.run/Lighthouse Website.run.xml new file mode 100644 index 00000000..4734f1b8 --- /dev/null +++ b/.run/Lighthouse Website.run.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/.run/Lighthouse.run.xml b/.run/Lighthouse.run.xml new file mode 100644 index 00000000..9c9d716a --- /dev/null +++ b/.run/Lighthouse.run.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Api/SlotEndpoints.cs b/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs similarity index 87% rename from ProjectLighthouse/Controllers/Api/SlotEndpoints.cs rename to ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs index 909f9a95..36e536dd 100644 --- a/ProjectLighthouse/Controllers/Api/SlotEndpoints.cs +++ b/ProjectLighthouse.Servers.API/Controllers/SlotEndpoints.cs @@ -1,15 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +#nullable enable +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.Servers.API.Responses; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Api; +namespace LBPUnion.ProjectLighthouse.Servers.API.Controllers; /// /// A collection of endpoints relating to slots. diff --git a/ProjectLighthouse/Controllers/Api/StatisticsEndpoints.cs b/ProjectLighthouse.Servers.API/Controllers/StatisticsEndpoints.cs similarity index 86% rename from ProjectLighthouse/Controllers/Api/StatisticsEndpoints.cs rename to ProjectLighthouse.Servers.API/Controllers/StatisticsEndpoints.cs index a89828ac..072f7423 100644 --- a/ProjectLighthouse/Controllers/Api/StatisticsEndpoints.cs +++ b/ProjectLighthouse.Servers.API/Controllers/StatisticsEndpoints.cs @@ -1,11 +1,9 @@ -using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Servers.API.Responses; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Api; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.Api; +namespace LBPUnion.ProjectLighthouse.Servers.API.Controllers; /// /// A collection of endpoints relating to statistics. diff --git a/ProjectLighthouse/Controllers/Api/UserEndpoints.cs b/ProjectLighthouse.Servers.API/Controllers/UserEndpoints.cs similarity index 88% rename from ProjectLighthouse/Controllers/Api/UserEndpoints.cs rename to ProjectLighthouse.Servers.API/Controllers/UserEndpoints.cs index 63510326..30e7d655 100644 --- a/ProjectLighthouse/Controllers/Api/UserEndpoints.cs +++ b/ProjectLighthouse.Servers.API/Controllers/UserEndpoints.cs @@ -1,14 +1,12 @@ #nullable enable -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Profiles; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; // ReSharper disable RouteTemplates.ActionRoutePrefixCanBeExtractedToControllerRoute -namespace LBPUnion.ProjectLighthouse.Controllers.Api; +namespace LBPUnion.ProjectLighthouse.Servers.API.Controllers; /// /// A collection of endpoints relating to users. @@ -50,7 +48,7 @@ public class UserEndpoints : ApiEndpointController [HttpGet("user/{id:int}/status")] [ProducesResponseType(typeof(UserStatus), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetUserStatus(int id) + public IActionResult GetUserStatus(int id) { UserStatus userStatus = new(this.database, id); diff --git a/ProjectLighthouse.Servers.API/Program.cs b/ProjectLighthouse.Servers.API/Program.cs new file mode 100644 index 00000000..f2cb06d4 --- /dev/null +++ b/ProjectLighthouse.Servers.API/Program.cs @@ -0,0 +1,36 @@ +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Logging.Loggers.AspNet; +using LBPUnion.ProjectLighthouse.Servers.API.Startup; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace LBPUnion.ProjectLighthouse.Servers.API; + +public static class Program +{ + public static void Main(string[] args) + { + StartupTasks.Run(args, ServerType.Api); + + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + => Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults + ( + webBuilder => + { + webBuilder.UseStartup(); + webBuilder.UseUrls(ServerConfiguration.Instance.ApiListenUrl); + } + ) + .ConfigureLogging + ( + logging => + { + logging.ClearProviders(); + logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + } + ); +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.API/ProjectLighthouse.Servers.API.csproj b/ProjectLighthouse.Servers.API/ProjectLighthouse.Servers.API.csproj new file mode 100644 index 00000000..fca0d2c5 --- /dev/null +++ b/ProjectLighthouse.Servers.API/ProjectLighthouse.Servers.API.csproj @@ -0,0 +1,46 @@ + + + + net6.0 + enable + enable + LBPUnion.ProjectLighthouse.Servers.API + LBPUnion.ProjectLighthouse.Servers.API + false + + + + true + $(NoWarn);1591 + + + + + + + + + + Always + + + + Always + + + + Always + + + + Always + + + + + + + + + + diff --git a/ProjectLighthouse/Types/Levels/MinimalSlot.cs b/ProjectLighthouse.Servers.API/Responses/MinimalSlot.cs similarity index 78% rename from ProjectLighthouse/Types/Levels/MinimalSlot.cs rename to ProjectLighthouse.Servers.API/Responses/MinimalSlot.cs index 4778389f..f1d39c66 100644 --- a/ProjectLighthouse/Types/Levels/MinimalSlot.cs +++ b/ProjectLighthouse.Servers.API/Responses/MinimalSlot.cs @@ -1,4 +1,8 @@ -namespace LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.Types; + +namespace LBPUnion.ProjectLighthouse.Servers.API.Responses; public struct MinimalSlot { diff --git a/ProjectLighthouse/Types/Api/StatisticsResponse.cs b/ProjectLighthouse.Servers.API/Responses/StatisticsResponse.cs similarity index 78% rename from ProjectLighthouse/Types/Api/StatisticsResponse.cs rename to ProjectLighthouse.Servers.API/Responses/StatisticsResponse.cs index 79f443f7..8c4464a4 100644 --- a/ProjectLighthouse/Types/Api/StatisticsResponse.cs +++ b/ProjectLighthouse.Servers.API/Responses/StatisticsResponse.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Api; +namespace LBPUnion.ProjectLighthouse.Servers.API.Responses; public class StatisticsResponse { diff --git a/ProjectLighthouse.Servers.API/Startup/ApiStartup.cs b/ProjectLighthouse.Servers.API/Startup/ApiStartup.cs new file mode 100644 index 00000000..c0469ef6 --- /dev/null +++ b/ProjectLighthouse.Servers.API/Startup/ApiStartup.cs @@ -0,0 +1,74 @@ +using LBPUnion.ProjectLighthouse.Middlewares; +using LBPUnion.ProjectLighthouse.Serialization; +using Microsoft.OpenApi.Models; + +namespace LBPUnion.ProjectLighthouse.Servers.API.Startup; + +public class ApiStartup +{ + public ApiStartup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + + services.AddMvc + ( + options => + { + options.OutputFormatters.Add(new JsonOutputFormatter()); + } + ); + + services.AddDbContext(); + + services.AddSwaggerGen + ( + c => + { + // Give swagger the name and version of our project + c.SwaggerDoc + ( + "v1", + new OpenApiInfo + { + Title = "Project Lighthouse API", + Version = "v1", + } + ); + + // Filter out endpoints not in /api/v1 + c.DocumentFilter(); + + // Add XMLDoc to swagger + c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "LBPUnion.ProjectLighthouse.Servers.API.xml")); + } + ); + } + + public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + #if DEBUG + app.UseDeveloperExceptionPage(); + #endif + + app.UseSwagger(); + app.UseSwaggerUI + ( + c => + { + c.SwaggerEndpoint("v1/swagger.json", "Project Lighthouse API"); + } + ); + + app.UseMiddleware(); + + app.UseRouting(); + app.UseEndpoints(endpoints => endpoints.MapControllers()); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.API/Startup/ApiTestStartup.cs b/ProjectLighthouse.Servers.API/Startup/ApiTestStartup.cs new file mode 100644 index 00000000..5de26f1d --- /dev/null +++ b/ProjectLighthouse.Servers.API/Startup/ApiTestStartup.cs @@ -0,0 +1,15 @@ +using LBPUnion.ProjectLighthouse.Middlewares; + +namespace LBPUnion.ProjectLighthouse.Servers.API.Startup; + +public class ApiTestStartup : ApiStartup +{ + public ApiTestStartup(IConfiguration configuration) : base(configuration) + {} + + public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseMiddleware(); + base.Configure(app, env); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/SwaggerFilter.cs b/ProjectLighthouse.Servers.API/SwaggerFilter.cs similarity index 63% rename from ProjectLighthouse/Helpers/SwaggerFilter.cs rename to ProjectLighthouse.Servers.API/SwaggerFilter.cs index 90b333c9..1a922717 100644 --- a/ProjectLighthouse/Helpers/SwaggerFilter.cs +++ b/ProjectLighthouse.Servers.API/SwaggerFilter.cs @@ -1,10 +1,16 @@ -using System.Collections.Generic; -using System.Linq; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace LBPUnion.ProjectLighthouse.Helpers; +namespace LBPUnion.ProjectLighthouse.Servers.API; +/// +/// +/// A filter for the swagger documentation endpoint. +/// +/// +/// Makes sure that only endpoints under /api/v1 show up. +/// +/// public class SwaggerFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) diff --git a/ProjectLighthouse.Servers.API/appsettings.Development.json b/ProjectLighthouse.Servers.API/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/ProjectLighthouse.Servers.API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/ProjectLighthouse.Servers.API/appsettings.json b/ProjectLighthouse.Servers.API/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/ProjectLighthouse.Servers.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/ProjectLighthouse/Controllers/GameApi/ClientConfigurationController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs similarity index 91% rename from ProjectLighthouse/Controllers/GameApi/ClientConfigurationController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs index 26eb534e..7e9dd31d 100644 --- a/ProjectLighthouse/Controllers/GameApi/ClientConfigurationController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/ClientConfigurationController.cs @@ -1,12 +1,11 @@ #nullable enable using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -25,7 +24,7 @@ public class ClientConfigurationController : ControllerBase public async Task NetworkSettings() { GameToken? token = await this.database.GameTokenFromRequest(this.Request); - if (token == null) return null; + if (token == null) return this.StatusCode(403, ""); HostString hostname = this.Request.Host; return this.Ok @@ -33,7 +32,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/CommentController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs similarity index 95% rename from ProjectLighthouse/Controllers/GameApi/CommentController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs index 13bce479..46c239f5 100644 --- a/ProjectLighthouse/Controllers/GameApi/CommentController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs @@ -1,19 +1,15 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] diff --git a/ProjectLighthouse/Controllers/GameApi/DeveloperController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs similarity index 76% rename from ProjectLighthouse/Controllers/GameApi/DeveloperController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs index a12cdb00..00d98f51 100644 --- a/ProjectLighthouse/Controllers/GameApi/DeveloperController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/DeveloperController.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] diff --git a/ProjectLighthouse/Controllers/GameApi/FriendsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs similarity index 78% rename from ProjectLighthouse/Controllers/GameApi/FriendsController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs index f2a1159f..20ffda5c 100644 --- a/ProjectLighthouse/Controllers/GameApi/FriendsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/FriendsController.cs @@ -1,17 +1,16 @@ #nullable enable -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; +using LBPUnion.ProjectLighthouse.StorableLists; +using LBPUnion.ProjectLighthouse.StorableLists.Stores; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -57,14 +56,13 @@ public class FriendsController : ControllerBase blockedUsers.Add(blockedUser.UserId); } - if (FriendHelper.FriendIdsByUserId.ContainsKey(user.UserId)) - { - FriendHelper.FriendIdsByUserId.Remove(user.UserId); - FriendHelper.BlockedIdsByUserId.Remove(user.UserId); - } + UserFriendData? friendStore = UserFriendStore.GetUserFriendData(user.UserId); + if (friendStore == null) friendStore = UserFriendStore.CreateUserFriendData(user.UserId); - FriendHelper.FriendIdsByUserId.Add(user.UserId, friends.Select(u => u.UserId).ToArray()); - FriendHelper.BlockedIdsByUserId.Add(user.UserId, blockedUsers.ToArray()); + friendStore.FriendIds = friends.Select(u => u.UserId).ToList(); + friendStore.BlockedIds = blockedUsers; + + UserFriendStore.UpdateFriendData(friendStore); string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username)); @@ -82,11 +80,13 @@ public class FriendsController : ControllerBase User user = userAndToken.Value.Item1; GameToken gameToken = userAndToken.Value.Item2; - if (!FriendHelper.FriendIdsByUserId.TryGetValue(user.UserId, out int[]? friendIds) || friendIds == null) + UserFriendData? friendStore = UserFriendStore.GetUserFriendData(user.UserId); + + if (friendStore == null) return this.Ok(LbpSerializer.BlankElement("myFriends")); string friends = ""; - foreach (int friendId in friendIds) + foreach (int friendId in friendStore.FriendIds) { User? friend = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == friendId); if (friend == null) continue; diff --git a/ProjectLighthouse/Controllers/GameApi/LoginController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs similarity index 64% rename from ProjectLighthouse/Controllers/GameApi/LoginController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs index 20ec33ae..7b7ba8f4 100644 --- a/ProjectLighthouse/Controllers/GameApi/LoginController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/LoginController.cs @@ -1,19 +1,17 @@ #nullable enable -using System.IO; -using System.Linq; using System.Net; -using System.Threading.Tasks; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Match.Rooms; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Tickets; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; -using LBPUnion.ProjectLighthouse.Types.Tickets; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -using IOFile = System.IO.File; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/login")] @@ -46,14 +44,14 @@ public class LoginController : ControllerBase if (npTicket == null) { - Logger.Log("npTicket was null, rejecting login", LoggerLevelLogin.Instance); + Logger.Warn("npTicket was null, rejecting login", LogArea.Login); return this.BadRequest(); } IPAddress? remoteIpAddress = this.HttpContext.Connection.RemoteIpAddress; if (remoteIpAddress == null) { - Logger.Log("unable to determine ip, rejecting login", LoggerLevelLogin.Instance); + Logger.Warn("unable to determine ip, rejecting login", LogArea.Login); return this.StatusCode(403, ""); // 403 probably isnt the best status code for this, but whatever } @@ -69,7 +67,7 @@ public class LoginController : ControllerBase token = await this.database.AuthenticateUser(npTicket, ipAddress); if (token == null) { - Logger.Log("unable to find/generate a token, rejecting login", LoggerLevelLogin.Instance); + Logger.Warn($"Unable to find/generate a token for username {npTicket.Username}", LogArea.Login); return this.StatusCode(403, ""); // If not, then 403. } } @@ -78,28 +76,12 @@ public class LoginController : ControllerBase if (user == null || user.Banned) { - Logger.Log("unable to find a user from a token, rejecting login", LoggerLevelLogin.Instance); + Logger.Error($"Unable to find user {npTicket.Username} from token", LogArea.Login); return this.StatusCode(403, ""); } - if (ServerSettings.Instance.UseExternalAuth) + if (ServerConfiguration.Instance.Authentication.UseExternalAuth) { - if (ServerSettings.Instance.BlockDeniedUsers) - { - string ipAddressAndName = $"{token.UserLocation}|{user.Username}"; - if (DeniedAuthenticationHelper.RecentlyDenied(ipAddressAndName) || DeniedAuthenticationHelper.GetAttempts(ipAddressAndName) > 3) - { - this.database.AuthenticationAttempts.RemoveRange - (this.database.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId)); - - DeniedAuthenticationHelper.AddAttempt(ipAddressAndName); - - await this.database.SaveChangesAsync(); - Logger.Log("too many denied logins, rejecting login", LoggerLevelLogin.Instance); - return this.StatusCode(403, ""); - } - } - if (this.database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).Select(a => a.IpAddress).Contains(ipAddress)) { token.Approved = true; @@ -110,7 +92,7 @@ public class LoginController : ControllerBase { GameToken = token, GameTokenId = token.TokenId, - Timestamp = TimestampHelper.Timestamp, + Timestamp = TimeHelper.Timestamp, IPAddress = ipAddress, Platform = npTicket.Platform, }; @@ -127,11 +109,11 @@ public class LoginController : ControllerBase if (!token.Approved) { - Logger.Log("token unapproved, rejecting login", LoggerLevelLogin.Instance); + Logger.Warn($"Token unapproved for user {user.Username}, rejecting login", LogArea.Login); return this.StatusCode(403, ""); } - Logger.Log($"Successfully logged in user {user.Username} as {token.GameVersion} client", LoggerLevelLogin.Instance); + Logger.Success($"Successfully logged in user {user.Username} as {token.GameVersion} client", LogArea.Login); // After this point we are now considering this session as logged in. // We just logged in with the token. Mark it as used so someone else doesnt try to use it, @@ -141,14 +123,14 @@ public class LoginController : ControllerBase await this.database.SaveChangesAsync(); // Create a new room on LBP2/3/Vita - if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user, token.GameVersion, token.Platform); + if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user.UserId, token.GameVersion, token.Platform); return this.Ok ( new LoginResult { AuthTicket = "MM_AUTH=" + token.UserToken, - LbpEnvVer = ServerStatics.ServerName, + ServerBrand = VersionHelper.FullVersion, }.Serialize() ); } diff --git a/ProjectLighthouse/Controllers/GameApi/Matching/EnterLevelController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs similarity index 94% rename from ProjectLighthouse/Controllers/GameApi/Matching/EnterLevelController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs index 824f2b5d..2dc6e363 100644 --- a/ProjectLighthouse/Controllers/GameApi/Matching/EnterLevelController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/EnterLevelController.cs @@ -1,13 +1,12 @@ #nullable enable -using System; -using System.Linq; -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Matching; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] diff --git a/ProjectLighthouse/Controllers/GameApi/Matching/MatchController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs similarity index 77% rename from ProjectLighthouse/Controllers/GameApi/Matching/MatchController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs index a2124f02..23a195a5 100644 --- a/ProjectLighthouse/Controllers/GameApi/Matching/MatchController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Matching/MatchController.cs @@ -1,20 +1,18 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Text.Json; -using System.Threading.Tasks; -using Kettu; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Match; +using LBPUnion.ProjectLighthouse.Match.MatchCommands; +using LBPUnion.ProjectLighthouse.Match.Rooms; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Match; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Matching; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -48,29 +46,28 @@ public class MatchController : ControllerBase if (bodyString.Length == 0 || bodyString[0] != '[') return this.BadRequest(); - Logger.Log("Received match data: " + bodyString, LoggerLevelMatch.Instance); + Logger.Info("Received match data: " + bodyString, LogArea.Match); - IMatchData? matchData; + IMatchCommand? matchData; try { matchData = MatchHelper.Deserialize(bodyString); } catch(Exception e) { - Logger.Log("Exception while parsing matchData: ", LoggerLevelMatch.Instance); - string[] lines = e.ToDetailedException().Split("\n"); - foreach (string line in lines) Logger.Log(line, LoggerLevelMatch.Instance); + Logger.Error("Exception while parsing matchData: ", LogArea.Match); + Logger.Error(e.ToDetailedException(), LogArea.Match); return this.BadRequest(); } if (matchData == null) { - Logger.Log("Could not parse match data: matchData is null", LoggerLevelMatch.Instance); + Logger.Error($"Could not parse match data: {nameof(matchData)} is null", LogArea.Match); return this.BadRequest(); } - Logger.Log($"Parsed match from {user.Username} (type: {matchData.GetType()})", LoggerLevelMatch.Instance); + Logger.Info($"Parsed match from {user.Username} (type: {matchData.GetType()})", LogArea.Match); #endregion @@ -81,10 +78,10 @@ public class MatchController : ControllerBase if (matchData is UpdateMyPlayerData playerData) { MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation); - Room? room = RoomHelper.FindRoomByUser(user, gameToken.GameVersion, gameToken.Platform, true); + Room? room = RoomHelper.FindRoomByUser(user.UserId, gameToken.GameVersion, gameToken.Platform, true); if (playerData.RoomState != null) - if (room != null && Equals(room.Host, user)) + if (room != null && Equals(room.HostId, user.UserId)) room.State = (RoomState)playerData.RoomState; } @@ -108,12 +105,12 @@ public class MatchController : ControllerBase if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1) { - List users = new(); + List users = new(); foreach (string playerUsername in createRoom.Players) { User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername); // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (player != null) users.Add(player); + if (player != null) users.Add(player.UserId); else return this.BadRequest(); } @@ -123,7 +120,7 @@ public class MatchController : ControllerBase if (matchData is UpdatePlayersInRoom updatePlayersInRoom) { - Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.Host == user); + Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.HostId == user.UserId); if (room != null) { @@ -136,7 +133,7 @@ public class MatchController : ControllerBase else return this.BadRequest(); } - room.Players = users; + room.PlayerIds = users.Select(u => u.UserId).ToList(); RoomHelper.CleanupRooms(null, room); } } diff --git a/ProjectLighthouse/Controllers/GameApi/MessageController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs similarity index 69% rename from ProjectLighthouse/Controllers/GameApi/MessageController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs index b378413d..63067c7a 100644 --- a/ProjectLighthouse/Controllers/GameApi/MessageController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs @@ -1,14 +1,13 @@ #nullable enable -using System.IO; -using System.Threading.Tasks; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -17,6 +16,20 @@ public class MessageController : ControllerBase { private readonly Database database; + private const string license = @" +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see ."; + public MessageController(Database database) { this.database = database; @@ -28,7 +41,7 @@ public class MessageController : ControllerBase User? user = await this.database.UserFromGameRequest(this.Request); if (user == null) return this.StatusCode(403, ""); - return this.Ok($"{EulaHelper.License}\n{ServerSettings.Instance.EulaText}"); + return this.Ok($"{license}\n{ServerConfiguration.Instance.EulaText}"); } [HttpGet("announce")] @@ -47,7 +60,7 @@ public class MessageController : ControllerBase 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()); @@ -78,15 +91,15 @@ public class MessageController : ControllerBase public async Task Filter() { User? user = await this.database.UserFromGameRequest(this.Request); - + if (user == null) return this.StatusCode(403, ""); string response = await new StreamReader(this.Request.Body).ReadToEndAsync(); - + string scannedText = CensorHelper.ScanMessage(response); - Logger.Log($"{user.Username}: {response} / {scannedText}", LoggerLevelFilter.Instance); - + Logger.Info($"{user.Username}: {response} / {scannedText}", LogArea.Filter); + return this.Ok(scannedText); } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/GameApi/ReportController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs similarity index 83% rename from ProjectLighthouse/Controllers/GameApi/ReportController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs index d3808a19..0b39c355 100644 --- a/ProjectLighthouse/Controllers/GameApi/ReportController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/ReportController.cs @@ -1,14 +1,13 @@ #nullable enable -using System.IO; using System.Text.Json; -using System.Threading.Tasks; using System.Xml.Serialization; -using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Reports; -using Microsoft.AspNetCore.Mvc; +using LBPUnion.ProjectLighthouse.Administration.Reports; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -32,7 +31,7 @@ public class ReportController : ControllerBase string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); XmlSerializer serializer = new(typeof(GriefReport)); - GriefReport? report = (GriefReport?) serializer.Deserialize(new StringReader(bodyString)); + GriefReport? report = (GriefReport?)serializer.Deserialize(new StringReader(bodyString)); if (report == null) return this.BadRequest(); diff --git a/ProjectLighthouse/Controllers/GameApi/Resources/PhotosController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs similarity index 88% rename from ProjectLighthouse/Controllers/GameApi/Resources/PhotosController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs index b4149f2a..f9461c8d 100644 --- a/ProjectLighthouse/Controllers/GameApi/Resources/PhotosController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/PhotosController.cs @@ -1,21 +1,17 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using System.Xml.Serialization; using Discord; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Resources; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Resources; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -35,7 +31,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(); @@ -59,7 +55,7 @@ public class PhotosController : ControllerBase if (photo.Subjects.Count > 4) return this.BadRequest(); - if (photo.Timestamp > TimestampHelper.Timestamp) return this.BadRequest(); + if (photo.Timestamp > TimeHelper.Timestamp) photo.Timestamp = TimeHelper.Timestamp; foreach (PhotoSubject subject in photo.Subjects) { @@ -68,7 +64,7 @@ public class PhotosController : ControllerBase if (subject.User == null) continue; subject.UserId = subject.User.UserId; - Logger.Log($"Adding PhotoSubject (userid {subject.UserId}) to db", LoggerLevelPhotos.Instance); + Logger.Debug($"Adding PhotoSubject (userid {subject.UserId}) to db", LogArea.Photos); this.database.PhotoSubjects.Add(subject); } @@ -88,7 +84,7 @@ public class PhotosController : ControllerBase // photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId); - Logger.Log($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LoggerLevelPhotos.Instance); + Logger.Debug($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LogArea.Photos); this.database.Photos.Add(photo); @@ -100,7 +96,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/Resources/ResourcesController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs similarity index 67% rename from ProjectLighthouse/Controllers/GameApi/Resources/ResourcesController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs index 75fc15dc..1ea40e45 100644 --- a/ProjectLighthouse/Controllers/GameApi/Resources/ResourcesController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Resources/ResourcesController.cs @@ -1,18 +1,17 @@ #nullable enable -using System.IO; -using System.Linq; -using System.Threading.Tasks; +using System.Buffers; +using System.IO.Pipelines; using System.Xml.Serialization; -using Kettu; +using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Files; using Microsoft.AspNetCore.Mvc; using IOFile = System.IO.File; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Resources; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Resources; [ApiController] [Produces("text/xml")] @@ -63,25 +62,6 @@ public class ResourcesController : ControllerBase return this.NotFound(); } - [ResponseCache(Duration = 86400)] - [HttpGet("/gameAssets/{hash}")] - public IActionResult GetGameImage(string hash) - { - string path = Path.Combine("png", $"{hash}.png"); - - if (IOFile.Exists(path)) - { - return this.File(IOFile.OpenRead(path), "image/png"); - } - - LbpFile? file = LbpFile.FromHash(hash); - if (file != null && ImageHelper.LbpFileToPNG(file)) - { - return this.File(IOFile.OpenRead(path), "image/png"); - } - return this.NotFound(); - } - // TODO: check if this is a valid hash [HttpPost("upload/{hash}/unattributed")] [HttpPost("upload/{hash}")] @@ -97,28 +77,46 @@ public class ResourcesController : ControllerBase // lbp treats code 409 as success and as an indicator that the file is already present if (FileHelper.ResourceExists(hash)) this.Conflict(); - Logger.Log($"Processing resource upload (hash: {hash})", LoggerLevelResources.Instance); - LbpFile file = new(await BinaryHelper.ReadFromPipeReader(this.Request.BodyReader)); + Logger.Info($"Processing resource upload (hash: {hash})", LogArea.Resources); + LbpFile file = new(await readFromPipeReader(this.Request.BodyReader)); if (!FileHelper.IsFileSafe(file)) { - Logger.Log($"File is unsafe (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); + Logger.Warn($"File is unsafe (hash: {hash}, type: {file.FileType})", LogArea.Resources); return this.Conflict(); } string calculatedHash = file.Hash; if (calculatedHash != hash) { - Logger.Log - ( - $"File hash does not match the uploaded file! (hash: {hash}, calculatedHash: {calculatedHash}, type: {file.FileType})", - LoggerLevelResources.Instance - ); + Logger.Warn + ($"File hash does not match the uploaded file! (hash: {hash}, calculatedHash: {calculatedHash}, type: {file.FileType})", LogArea.Resources); return this.Conflict(); } - Logger.Log($"File is OK! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); + Logger.Success($"File is OK! (hash: {hash}, type: {file.FileType})", LogArea.Resources); await IOFile.WriteAllBytesAsync(path, file.Data); return this.Ok(); } + + // Written with reference from + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-response?view=aspnetcore-5.0 + // Surprisingly doesn't take seconds. (67ms for a 100kb file) + private static async Task readFromPipeReader(PipeReader reader) + { + List data = new(); + while (true) + { + ReadResult readResult = await reader.ReadAsync(); + ReadOnlySequence buffer = readResult.Buffer; + + if (readResult.IsCompleted && buffer.Length > 0) data.AddRange(buffer.ToArray()); + + reader.AdvanceTo(buffer.Start, buffer.End); + + if (readResult.IsCompleted) break; + } + + return data.ToArray(); + } } \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/CollectionController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs similarity index 85% rename from ProjectLighthouse/Controllers/GameApi/Slots/CollectionController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs index 7c08add8..c6ce69db 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/CollectionController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/CollectionController.cs @@ -1,17 +1,15 @@ #nullable enable -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kettu; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.Levels.Categories; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Categories; -using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -35,7 +33,7 @@ public class CollectionController : ControllerBase User? user = await this.database.UserFromGameRequest(this.Request); if (user == null) return this.StatusCode(403, ""); - string categoriesSerialized = CollectionHelper.Categories.Aggregate + string categoriesSerialized = CategoryHelper.Categories.Aggregate ( string.Empty, (current, category) => @@ -64,7 +62,7 @@ public class CollectionController : ControllerBase "hint_start", 1 }, { - "total", CollectionHelper.Categories.Count + "total", CategoryHelper.Categories.Count }, } ) @@ -82,10 +80,10 @@ public class CollectionController : ControllerBase User user = userAndToken.Value.Item1; GameToken gameToken = userAndToken.Value.Item2; - Category? category = CollectionHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName); + Category? category = CategoryHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName); if (category == null) return this.NotFound(); - Logger.Log("Found category " + category, LoggerLevelCategory.Instance); + Logger.Debug("Found category " + category, LogArea.Category); List slots; int totalSlots; diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/LevelTagsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs similarity index 79% rename from ProjectLighthouse/Controllers/GameApi/Slots/LevelTagsController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs index 99943993..a5ecd0bd 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/LevelTagsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/LevelTagsController.cs @@ -1,8 +1,7 @@ -using System; -using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Levels; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/tags")] diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/ListController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs similarity index 97% rename from ProjectLighthouse/Controllers/GameApi/Slots/ListController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs index e51c56b7..e888f4ad 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/ListController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs @@ -1,15 +1,13 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs similarity index 92% rename from ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs index 3a86d5ab..8c7a2eb7 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/PublishController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs @@ -1,20 +1,17 @@ #nullable enable -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Files; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Profiles; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -56,7 +53,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, ""); } @@ -89,9 +86,9 @@ public class PublishController : ControllerBase if (slot.Location == null) return this.BadRequest(); - if (slot.Description.Length > 200) return this.BadRequest(); + if (slot.Description.Length > 500) return this.BadRequest(); - if (slot.Name.Length > 100) return this.BadRequest(); + if (slot.Name.Length > 64) return this.BadRequest(); if (slot.Resources.Any(resource => !FileHelper.ResourceExists(resource))) { @@ -167,7 +164,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 +195,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 +229,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/ReviewController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs similarity index 92% rename from ProjectLighthouse/Controllers/GameApi/Slots/ReviewController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs index ab074769..eb6cb62d 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/ReviewController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs @@ -1,20 +1,18 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Administration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Reviews; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Reviews; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -98,7 +96,7 @@ public class ReviewController : ControllerBase Review? newReview = await this.getReviewFromBody(); if (newReview == null) return this.BadRequest(); - if (newReview.Text.Length > 100) return this.BadRequest(); + if (newReview.Text.Length > 512) return this.BadRequest(); Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId); @@ -167,18 +165,17 @@ public class ReviewController : ControllerBase List reviewList = reviews.ToList(); - string inner = reviewList - .Aggregate - ( - string.Empty, - (current, review) => - { - if (review == null) return current; + string inner = reviewList.Aggregate + ( + string.Empty, + (current, review) => + { + if (review == null) return current; - RatedReview? yourThumb = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); - return current + review.Serialize(null, yourThumb); - } - ); + RatedReview? yourThumb = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); + return current + review.Serialize(null, yourThumb); + } + ); string response = LbpSerializer.TaggedStringElement ( "reviews", diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/ScoreController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs similarity index 93% rename from ProjectLighthouse/Controllers/GameApi/Slots/ScoreController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs index 4db9d290..cc61df46 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/ScoreController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ScoreController.cs @@ -1,18 +1,15 @@ #nullable enable -using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -108,7 +105,15 @@ public class ScoreController : ControllerBase } [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - private string getScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5, string rootName = "scores") + private string getScores + ( + int slotId, + int type, + User user, + int pageStart = -1, + int pageSize = 5, + string rootName = "scores" + ) { // This is hella ugly but it technically assigns the proper rank to a score // var needed for Anonymous type returned from SELECT diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/SearchController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs similarity index 87% rename from ProjectLighthouse/Controllers/GameApi/Slots/SearchController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs index 7292ff67..8e6e43bf 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/SearchController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SearchController.cs @@ -1,15 +1,12 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -47,7 +44,7 @@ public class SearchController : ControllerBase ( s => s.Name.ToLower().Contains(keyword) || s.Description.ToLower().Contains(keyword) || - s.Creator.Username.ToLower().Contains(keyword) || + s.Creator!.Username.ToLower().Contains(keyword) || s.SlotId.ToString().Equals(keyword) ); diff --git a/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs similarity index 93% rename from ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs index fbcd2e66..5fab0b3b 100644 --- a/ProjectLighthouse/Controllers/GameApi/Slots/SlotsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/SlotsController.cs @@ -1,19 +1,17 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Reviews; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Reviews; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi.Slots; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -42,7 +40,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 +54,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 +133,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 +167,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 +198,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 +242,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 +300,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 +344,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 +401,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/Controllers/GameApi/StatisticsController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs similarity index 82% rename from ProjectLighthouse/Controllers/GameApi/StatisticsController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs index 47d4b4f0..fef087b9 100644 --- a/ProjectLighthouse/Controllers/GameApi/StatisticsController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/StatisticsController.cs @@ -1,21 +1,14 @@ -using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Serialization; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] [Produces("text/plain")] public class StatisticsController : ControllerBase { - private readonly Database database; - public StatisticsController(Database database) - { - this.database = database; - } - [HttpGet("playersInPodCount")] [HttpGet("totalPlayerCount")] public async Task TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!); diff --git a/ProjectLighthouse/Controllers/GameApi/StoreController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/StoreController.cs similarity index 77% rename from ProjectLighthouse/Controllers/GameApi/StoreController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/StoreController.cs index 053dbf06..ad214da9 100644 --- a/ProjectLighthouse/Controllers/GameApi/StoreController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/StoreController.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] diff --git a/ProjectLighthouse/Controllers/GameApi/UserController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs similarity index 90% rename from ProjectLighthouse/Controllers/GameApi/UserController.cs rename to ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs index a45419c1..f27e902d 100644 --- a/ProjectLighthouse/Controllers/GameApi/UserController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/UserController.cs @@ -1,19 +1,16 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Text.Json; -using System.Threading.Tasks; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.GameApi; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers; [ApiController] [Route("LITTLEBIGPLANETPS3_XML/")] @@ -37,12 +34,17 @@ public class UserController : ControllerBase { // use an anonymous type to only fetch certain columns var partialUser = await this.database.Users.Where(u => u.Username == username) - .Select(u => new - { - u.Username, - u.IconHash, - }).FirstOrDefaultAsync(); + .Select + ( + u => new + { + u.Username, + u.IconHash, + } + ) + .FirstOrDefaultAsync(); if (partialUser == null) return null; + string user = LbpSerializer.TaggedStringElement("npHandle", partialUser.Username, "icon", partialUser.IconHash); return LbpSerializer.TaggedStringElement("user", user, "type", "user"); } @@ -84,13 +86,12 @@ public class UserController : ControllerBase User user = userAndToken.Value.Item1; GameToken gameToken = userAndToken.Value.Item2; - this.Request.Body.Position = 0; string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync(); // xml hack so we can use one class to deserialize different root names string rootElement = bodyString.Contains("updateUser") ? "updateUser" : "user"; XmlSerializer serializer = new(typeof(UserUpdate), new XmlRootAttribute(rootElement)); - UserUpdate? update = (UserUpdate?) serializer.Deserialize(new StringReader(bodyString)); + UserUpdate? update = (UserUpdate?)serializer.Deserialize(new StringReader(bodyString)); if (update == null) return this.BadRequest(); @@ -103,11 +104,14 @@ public class UserController : ControllerBase user.Biography = update.Biography; } - foreach (string? resource in new[] {update.IconHash, update.YayHash, update.MehHash, update.BooHash, update.PlanetHash,}) + foreach (string? resource in new[] + { + update.IconHash, update.YayHash, update.MehHash, update.BooHash, update.PlanetHash, + }) { if (resource != null && !FileHelper.ResourceExists(resource)) return this.BadRequest(); } - + if (update.IconHash != null) user.IconHash = update.IconHash; if (update.YayHash != null) user.YayHash = update.YayHash; @@ -181,4 +185,4 @@ public class UserController : ControllerBase return this.Ok("[{\"StatusCode\":200}]"); } -} +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/Program.cs b/ProjectLighthouse.Servers.GameServer/Program.cs new file mode 100644 index 00000000..35acb397 --- /dev/null +++ b/ProjectLighthouse.Servers.GameServer/Program.cs @@ -0,0 +1,36 @@ +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Logging.Loggers.AspNet; +using LBPUnion.ProjectLighthouse.Servers.GameServer.Startup; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace LBPUnion.ProjectLighthouse.Servers.GameServer; + +public static class Program +{ + public static void Main(string[] args) + { + StartupTasks.Run(args, ServerType.GameServer); + + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + => Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults + ( + webBuilder => + { + webBuilder.UseStartup(); + webBuilder.UseUrls(ServerConfiguration.Instance.GameApiListenUrl); + } + ) + .ConfigureLogging + ( + logging => + { + logging.ClearProviders(); + logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + } + ); +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/ProjectLighthouse.Servers.GameServer.csproj b/ProjectLighthouse.Servers.GameServer/ProjectLighthouse.Servers.GameServer.csproj new file mode 100644 index 00000000..2636686a --- /dev/null +++ b/ProjectLighthouse.Servers.GameServer/ProjectLighthouse.Servers.GameServer.csproj @@ -0,0 +1,41 @@ + + + + net6.0 + enable + enable + LBPUnion.ProjectLighthouse.Servers.GameServer + LBPUnion.ProjectLighthouse.Servers.GameServer + false + + + + + + + + + + Always + + + + Always + + + + Always + + + + Always + + + + + + + + + + diff --git a/ProjectLighthouse/Startup/Startup.cs b/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs similarity index 55% rename from ProjectLighthouse/Startup/Startup.cs rename to ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs index fb78e597..2c316e72 100644 --- a/ProjectLighthouse/Startup/Startup.cs +++ b/ProjectLighthouse.Servers.GameServer/Startup/GameServerStartup.cs @@ -1,36 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Localization; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Middlewares; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.AspNetCore.Localization; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Primitives; -using Microsoft.OpenApi.Models; -#if RELEASE -using Microsoft.Extensions.Hosting.Internal; -#endif -namespace LBPUnion.ProjectLighthouse.Startup; +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Startup; -public class Startup +public class GameServerStartup { - public Startup(IConfiguration configuration) + public GameServerStartup(IConfiguration configuration) { this.Configuration = configuration; } @@ -41,11 +23,6 @@ public class Startup public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - #if DEBUG - services.AddRazorPages().WithRazorPagesAtContentRoot().AddRazorRuntimeCompilation(); - #else - services.AddRazorPages().WithRazorPagesAtContentRoot(); - #endif services.AddMvc ( @@ -65,51 +42,6 @@ public class Startup options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; } ); - - services.AddSwaggerGen - ( - c => - { - // Give swagger the name and version of our project - c.SwaggerDoc - ( - "v1", - new OpenApiInfo - { - Title = "Project Lighthouse API", - Version = "v1", - } - ); - - // Filter out endpoints not in /api/v1 - c.DocumentFilter(); - - // Add XMLDoc to swagger - string xmlDocs = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; - c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlDocs)); - } - ); - - services.Configure - ( - config => - { - List languages = LocalizationManager.GetAvailableLanguages() - .Select(l => new CultureInfo(LocalizationManager.MapLanguage(l))) - .ToList(); - - config.DefaultRequestCulture = new RequestCulture(new CultureInfo("en-US")); - - config.SupportedCultures = languages; - config.SupportedUICultures = languages; - } - ); - - #if DEBUG - services.AddSingleton(); - #else - services.AddSingleton(); - #endif } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -117,13 +49,13 @@ public class Startup { bool computeDigests = true; - if (string.IsNullOrEmpty(ServerSettings.Instance.ServerDigestKey)) + if (string.IsNullOrEmpty(ServerConfiguration.Instance.DigestKey.PrimaryDigestKey)) { - Logger.Log + Logger.Warn ( "The serverDigestKey configuration option wasn't set, so digest headers won't be set or verified. This will also prevent LBP 1, LBP 2, and LBP Vita from working. " + "To increase security, it is recommended that you find and set this variable.", - LoggerLevelStartup.Instance + LogArea.Startup ); computeDigests = false; } @@ -134,52 +66,7 @@ public class Startup app.UseForwardedHeaders(); - app.UseSwagger(); - app.UseSwaggerUI - ( - c => - { - c.SwaggerEndpoint("v1/swagger.json", "Project Lighthouse API"); - } - ); - - app.UseRequestLocalization(); - - // Logs every request and the response to it - // Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news" - // Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr" - app.Use - ( - async (context, next) => - { - Stopwatch requestStopwatch = new(); - requestStopwatch.Start(); - - context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging - - // Log all headers. -// foreach (KeyValuePair header in context.Request.Headers) Logger.Log($"{header.Key}: {header.Value}"); - - await next(context); // Handle the request so we can get the status code from it - - requestStopwatch.Stop(); - - Logger.Log - ( - $"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}", - LoggerLevelHttp.Instance - ); - - #if DEBUG - // Log post body - if (context.Request.Method == "POST") - { - context.Request.Body.Position = 0; - Logger.Log(await new StreamReader(context.Request.Body).ReadToEndAsync(), LoggerLevelHttp.Instance); - } - #endif - } - ); + app.UseMiddleware(); // Digest check app.Use @@ -187,7 +74,7 @@ public class Startup async (context, next) => { // Client digest check. - if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string authCookie)) authCookie = string.Empty; + if (!context.Request.Cookies.TryGetValue("MM_AUTH", out string? authCookie) || authCookie == null) authCookie = string.Empty; string digestPath = context.Request.Path; Stream body = context.Request.Body; @@ -195,7 +82,8 @@ public class Startup if (computeDigests && digestPath.StartsWith("/LITTLEBIGPLANETPS3_XML")) { - string clientRequestDigest = await HashHelper.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)) @@ -208,13 +96,14 @@ public class Startup // Reset the body stream body.Position = 0; - clientRequestDigest = await HashHelper.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. @@ -241,10 +130,12 @@ 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 HashHelper.ComputeDigest(context.Request.Path, authCookie, responseBuffer, digestKey); + string serverDigest = await CryptoHelper.ComputeDigest(context.Request.Path, authCookie, responseBuffer, digestKey); context.Response.Headers.Add("X-Digest-A", serverDigest); } @@ -283,8 +174,6 @@ public class Startup app.UseRouting(); - app.UseStaticFiles(); - app.UseEndpoints(endpoints => endpoints.MapControllers()); app.UseEndpoints(endpoints => endpoints.MapRazorPages()); } diff --git a/ProjectLighthouse.Servers.GameServer/Startup/GameServerTestStartup.cs b/ProjectLighthouse.Servers.GameServer/Startup/GameServerTestStartup.cs new file mode 100644 index 00000000..0e9ea43b --- /dev/null +++ b/ProjectLighthouse.Servers.GameServer/Startup/GameServerTestStartup.cs @@ -0,0 +1,15 @@ +using LBPUnion.ProjectLighthouse.Middlewares; + +namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Startup; + +public class GameServerTestStartup : GameServerStartup +{ + public GameServerTestStartup(IConfiguration configuration) : base(configuration) + {} + + public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseMiddleware(); + base.Configure(app, env); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.GameServer/appsettings.Development.json b/ProjectLighthouse.Servers.GameServer/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/ProjectLighthouse.Servers.GameServer/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/ProjectLighthouse.Servers.GameServer/appsettings.json b/ProjectLighthouse.Servers.GameServer/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/ProjectLighthouse.Servers.GameServer/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminPanelController.cs b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminPanelController.cs similarity index 79% rename from ProjectLighthouse/Controllers/Website/Admin/AdminPanelController.cs rename to ProjectLighthouse.Servers.Website/Controllers/Admin/AdminPanelController.cs index d736fd44..67f71941 100644 --- a/ProjectLighthouse/Controllers/Website/Admin/AdminPanelController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminPanelController.cs @@ -1,7 +1,7 @@ #nullable enable using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Admin; [ApiController] [Route("/admin")] diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminReportController.cs b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs similarity index 92% rename from ProjectLighthouse/Controllers/Website/Admin/AdminReportController.cs rename to ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs index 3568d169..7557e834 100644 --- a/ProjectLighthouse/Controllers/Website/Admin/AdminReportController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminReportController.cs @@ -1,13 +1,11 @@ #nullable enable -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Administration.Reports; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Reports; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Admin; [ApiController] [Route("admin/report/{id:int}")] diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminSlotController.cs similarity index 91% rename from ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs rename to ProjectLighthouse.Servers.Website/Controllers/Admin/AdminSlotController.cs index 080f6ff9..0ef40305 100644 --- a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminSlotController.cs @@ -1,11 +1,11 @@ #nullable enable -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Admin; [ApiController] [Route("admin/slot/{id:int}")] diff --git a/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminUserController.cs b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminUserController.cs new file mode 100644 index 00000000..a0b0ee4c --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Controllers/Admin/AdminUserController.cs @@ -0,0 +1,103 @@ +#nullable enable +using LBPUnion.ProjectLighthouse.Files; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using IOFile = System.IO.File; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Admin; + +[ApiController] +[Route("admin/user/{id:int}")] +public class AdminUserController : ControllerBase +{ + private readonly Database database; + + public AdminUserController(Database database) + { + this.database = database; + } + + [HttpGet("unban")] + public async Task UnbanUser([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.NotFound(); + + User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + if (targetedUser == null) return this.NotFound(); + + targetedUser.Banned = false; + targetedUser.BannedReason = null; + + await this.database.SaveChangesAsync(); + return this.Redirect($"/user/{targetedUser.UserId}"); + } + + /// + /// Resets the user's earth decorations to a blank state. Useful for users who abuse audio for example. + /// + [HttpGet("wipePlanets")] + public async Task WipePlanets([FromRoute] int id) { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.NotFound(); + + User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + if (targetedUser == null) return this.NotFound(); + + string[] hashes = { + targetedUser.PlanetHashLBP2, + targetedUser.PlanetHashLBP3, + targetedUser.PlanetHashLBPVita, + }; + + // This will also wipe users' earth with the same hashes. + foreach (string hash in hashes) + { + // Don't try to remove empty hashes. That's a horrible idea. + if (string.IsNullOrWhiteSpace(hash)) continue; + + // Find users with a matching hash + List users = await this.database.Users + .Where(u => u.PlanetHashLBP2 == hash || + u.PlanetHashLBP3 == hash || + u.PlanetHashLBPVita == hash) + .ToListAsync(); + + // We should match at least the targeted user... + System.Diagnostics.Debug.Assert(users.Count != 0); + + // Reset each users' hash. + foreach (User userWithPlanet in users) + { + userWithPlanet.PlanetHashLBP2 = ""; + userWithPlanet.PlanetHashLBP3 = ""; + userWithPlanet.PlanetHashLBPVita = ""; + Logger.Success($"Deleted planets for {userWithPlanet.Username} (id:{userWithPlanet.UserId})", LogArea.Admin); + } + + // And finally, attempt to remove the resource from the filesystem. We don't want that taking up space. + try + { + IOFile.Delete(FileHelper.GetResourcePath(hash)); + Logger.Success($"Deleted planet resource {hash}", + LogArea.Admin); + } + catch(DirectoryNotFoundException) + { + // This is certainly a strange case, but it's not worth doing anything about since we were about + // to delete the file anyways. Carry on~ + } + catch(Exception e) + { + // Welp, guess I'll die then. We tried~ + Logger.Error($"Failed to delete planet resource {hash}\n{e}", LogArea.Admin); + } + } + + await this.database.SaveChangesAsync(); + + return this.Redirect($"/user/{targetedUser.UserId}"); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs b/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs similarity index 65% rename from ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs rename to ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs index cc690c93..7039a9bf 100644 --- a/ProjectLighthouse/Controllers/Website/Debug/RoomVisualizerController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/Debug/RoomVisualizerController.cs @@ -1,12 +1,12 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Match.Rooms; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.Debug; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Debug; [ApiController] [Route("debug/roomVisualizer")] @@ -25,12 +25,12 @@ public class RoomVisualizerController : ControllerBase #if !DEBUG return this.NotFound(); #else - List users = await this.database.Users.OrderByDescending(_ => EF.Functions.Random()).Take(2).ToListAsync(); + List users = await this.database.Users.OrderByDescending(_ => EF.Functions.Random()).Take(2).Select(u => u.UserId).ToListAsync(); RoomHelper.CreateRoom(users, GameVersion.LittleBigPlanet2, Platform.PS3); - foreach (User user in users) + foreach (int user in users) { - MatchHelper.SetUserLocation(user.UserId, "127.0.0.1"); + MatchHelper.SetUserLocation(user, "127.0.0.1"); } return this.Redirect("/debug/roomVisualizer"); #endif @@ -42,7 +42,7 @@ public class RoomVisualizerController : ControllerBase #if !DEBUG return this.NotFound(); #else - RoomHelper.Rooms.RemoveAll(_ => true); + lock(RoomHelper.RoomLock) RoomHelper.Rooms.RemoveAll(); return this.Redirect("/debug/roomVisualizer"); #endif } diff --git a/ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs b/ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AuthenticationController.cs similarity index 87% rename from ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs rename to ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AuthenticationController.cs index a33b7ca6..8ae4e279 100644 --- a/ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AuthenticationController.cs @@ -1,13 +1,11 @@ #nullable enable -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.ExternalAuth; [ApiController] [Route("/authentication")] @@ -57,8 +55,6 @@ public class AuthenticationController : ControllerBase this.database.GameTokens.Remove(authAttempt.GameToken); this.database.AuthenticationAttempts.Remove(authAttempt); - DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}"); - await this.database.SaveChangesAsync(); return this.Redirect("~/authentication"); @@ -79,8 +75,6 @@ public class AuthenticationController : ControllerBase { this.database.GameTokens.Remove(authAttempt.GameToken); this.database.AuthenticationAttempts.Remove(authAttempt); - - DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}"); } await this.database.SaveChangesAsync(); diff --git a/ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs b/ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AutoApprovalController.cs similarity index 92% rename from ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs rename to ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AutoApprovalController.cs index 4d889285..48f935ef 100644 --- a/ProjectLighthouse/Controllers/Website/ExternalAuth/AutoApprovalController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/ExternalAuth/AutoApprovalController.cs @@ -1,10 +1,11 @@ #nullable enable -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.ExternalAuth; [ApiController] [Route("/authentication")] diff --git a/ProjectLighthouse.Servers.Website/Controllers/ResourcesController.cs b/ProjectLighthouse.Servers.Website/Controllers/ResourcesController.cs new file mode 100644 index 00000000..61158233 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Controllers/ResourcesController.cs @@ -0,0 +1,29 @@ +using LBPUnion.ProjectLighthouse.Files; +using LBPUnion.ProjectLighthouse.Helpers; +using Microsoft.AspNetCore.Mvc; +using IOFile = System.IO.File; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers; + +[ApiController] +public class ResourcesController : ControllerBase +{ + [ResponseCache(Duration = 86400)] + [HttpGet("/gameAssets/{hash}")] + public IActionResult GetGameImage(string hash) + { + string path = Path.Combine("png", $"{hash}.png"); + + if (IOFile.Exists(path)) + { + return this.File(IOFile.OpenRead(path), "image/png"); + } + + LbpFile? file = LbpFile.FromHash(hash); + if (file != null && FileHelper.LbpFileToPNG(file)) + { + return this.File(IOFile.OpenRead(path), "image/png"); + } + return this.NotFound(); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Website/SlotPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs similarity index 90% rename from ProjectLighthouse/Controllers/Website/SlotPageController.cs rename to ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs index f9ccfc91..414d9a0b 100644 --- a/ProjectLighthouse/Controllers/Website/SlotPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs @@ -1,10 +1,9 @@ #nullable enable -using System.Threading.Tasks; -using Kettu; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -13,7 +12,7 @@ using Microsoft.EntityFrameworkCore; // TODO: Clean up this file // - jvyden -namespace LBPUnion.ProjectLighthouse.Controllers.Website; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers; [ApiController] [Route("slot/{id:int}")] @@ -45,14 +44,14 @@ public class SlotPageController : ControllerBase if (msg == null) { - Logger.Log($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LoggerLevelComments.Instance); + Logger.Error($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments); return this.Redirect("~/slot/" + id); } msg = SanitizationHelper.SanitizeString(msg); await this.database.PostComment(user, id, CommentType.Level, msg); - Logger.Log($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LoggerLevelComments.Instance); + Logger.Success($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LogArea.Comments); return this.Redirect("~/slot/" + id); } diff --git a/ProjectLighthouse/Controllers/Website/UserPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs similarity index 87% rename from ProjectLighthouse/Controllers/Website/UserPageController.cs rename to ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs index a5bb7969..16b25782 100644 --- a/ProjectLighthouse/Controllers/Website/UserPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs @@ -1,13 +1,12 @@ #nullable enable -using System.Threading.Tasks; -using Kettu; -using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Controllers.Website; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers; [ApiController] [Route("user/{id:int}")] @@ -39,14 +38,14 @@ public class UserPageController : ControllerBase if (msg == null) { - Logger.Log($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LoggerLevelComments.Instance); + Logger.Error($"Refusing to post comment from {user.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments); return this.Redirect("~/user/" + id); } msg = SanitizationHelper.SanitizeString(msg); await this.database.PostComment(user, id, CommentType.Profile, msg); - Logger.Log($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LoggerLevelComments.Instance); + Logger.Success($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LogArea.Comments); return this.Redirect("~/user/" + id); } diff --git a/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminBanUserPage.cshtml similarity index 87% rename from ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Admin/AdminBanUserPage.cshtml index 2fd9e27a..e5138ee5 100644 --- a/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminBanUserPage.cshtml @@ -1,5 +1,5 @@ @page "/admin/user/{id:int}/ban" -@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminBanUserPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminBanUserPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminBanUserPage.cshtml.cs similarity index 89% rename from ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/Admin/AdminBanUserPage.cshtml.cs index 3fb07d38..dacee993 100644 --- a/ProjectLighthouse/Pages/Admin/AdminBanUserPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminBanUserPage.cshtml.cs @@ -1,12 +1,11 @@ #nullable enable -using System.Linq; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages.Admin; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin; public class AdminBanUserPage : BaseLayout { diff --git a/ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml similarity index 82% rename from ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml index 58440e43..53e82fcd 100644 --- a/ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml @@ -1,15 +1,27 @@ @page "/admin" +@using LBPUnion.ProjectLighthouse.Administration +@using LBPUnion.ProjectLighthouse.Administration.Maintenance +@using LBPUnion.ProjectLighthouse.Extensions @using LBPUnion.ProjectLighthouse.Helpers -@using LBPUnion.ProjectLighthouse.Helpers.Extensions -@using LBPUnion.ProjectLighthouse.Maintenance @using LBPUnion.ProjectLighthouse.Types -@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminPanelPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminPanelPage @{ Layout = "Layouts/BaseLayout"; Model.Title = "Admin Panel"; } +@if (Model.Log != null) +{ +
+

Command Output

+ @foreach (string line in Model.Log.Split("\n")) + { + @line.TrimEnd()
+ } +
+} + @if (!this.Request.IsMobile()) {
diff --git a/ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs similarity index 67% rename from ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs index d78bdb33..6df20cfc 100644 --- a/ProjectLighthouse/Pages/Admin/AdminPanelPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelPage.cshtml.cs @@ -1,13 +1,15 @@ #nullable enable -using System.Collections.Generic; -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Administration; +using LBPUnion.ProjectLighthouse.Administration.Maintenance; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Maintenance; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages.Admin; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin; public class AdminPanelPage : BaseLayout { @@ -17,7 +19,9 @@ public class AdminPanelPage : BaseLayout public List Statistics = new(); - public async Task OnGet([FromQuery] string? args, [FromQuery] string? command, [FromQuery] string? maintenanceJob) + public string? Log; + + public async Task OnGet([FromQuery] string? args, [FromQuery] string? command, [FromQuery] string? maintenanceJob, [FromQuery] string? log) { User? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); @@ -33,8 +37,9 @@ public class AdminPanelPage : BaseLayout args ??= ""; args = command + " " + args; string[] split = args.Split(" "); - await MaintenanceHelper.RunCommand(split); - return this.Redirect("~/admin"); + + List runCommand = await MaintenanceHelper.RunCommand(split); + return this.Redirect($"~/admin?log={CryptoHelper.ToBase64(runCommand.ToLogString())}"); } if (!string.IsNullOrEmpty(maintenanceJob)) @@ -43,6 +48,11 @@ public class AdminPanelPage : BaseLayout return this.Redirect("~/admin"); } + if (!string.IsNullOrEmpty(log)) + { + this.Log = CryptoHelper.FromBase64(log); + } + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml similarity index 87% rename from ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml index 92822597..5642912f 100644 --- a/ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml @@ -1,6 +1,7 @@ @page "/admin/users" +@using LBPUnion.ProjectLighthouse.PlayerData.Profiles @using LBPUnion.ProjectLighthouse.Types -@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminPanelUsersPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminPanelUsersPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml.cs similarity index 75% rename from ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml.cs index baa515dc..1bde19da 100644 --- a/ProjectLighthouse/Pages/Admin/AdminPanelUsersPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminPanelUsersPage.cshtml.cs @@ -1,20 +1,17 @@ #nullable enable -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages.Admin; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin; public class AdminPanelUsersPage : BaseLayout { - public int UserCount; - public List Users; + public List Users = new(); public AdminPanelUsersPage(Database database) : base(database) {} diff --git a/ProjectLighthouse/Pages/Admin/AdminSetGrantedSlotsPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml similarity index 74% rename from ProjectLighthouse/Pages/Admin/AdminSetGrantedSlotsPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml index b9644d4f..fe2ac408 100644 --- a/ProjectLighthouse/Pages/Admin/AdminSetGrantedSlotsPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml @@ -1,5 +1,5 @@ @page "/admin/user/{id:int}/setGrantedSlots" -@model LBPUnion.ProjectLighthouse.Pages.Admin.AdminSetGrantedSlotsPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminSetGrantedSlotsPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs similarity index 87% rename from ProjectLighthouse/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs index 8dce5e64..9e38340a 100644 --- a/ProjectLighthouse/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Admin/AdminSetGrantedSlotsPage.cshtml.cs @@ -1,11 +1,11 @@ #nullable enable -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages.Admin; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin; public class AdminSetGrantedSlotsPage : BaseLayout { diff --git a/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/CompleteEmailVerificationPage.cshtml similarity index 87% rename from ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/CompleteEmailVerificationPage.cshtml index ea15de22..0cf5d127 100644 --- a/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/CompleteEmailVerificationPage.cshtml @@ -1,5 +1,5 @@ @page "/verifyEmail" -@model LBPUnion.ProjectLighthouse.Pages.CompleteEmailVerificationPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.CompleteEmailVerificationPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/CompleteEmailVerificationPage.cshtml.cs similarity index 68% rename from ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/CompleteEmailVerificationPage.cshtml.cs index 4ec8bd8e..5f7e8752 100644 --- a/ProjectLighthouse/Pages/CompleteEmailVerificationPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/CompleteEmailVerificationPage.cshtml.cs @@ -1,25 +1,24 @@ #nullable enable -using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Profiles.Email; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class CompleteEmailVerificationPage : BaseLayout { - public CompleteEmailVerificationPage([NotNull] Database database) : base(database) + public CompleteEmailVerificationPage(Database database) : base(database) {} - public string? Error = null; + public string? Error; 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/Debug/FilterTestPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml similarity index 85% rename from ProjectLighthouse/Pages/Debug/FilterTestPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml index d648c178..864b9904 100644 --- a/ProjectLighthouse/Pages/Debug/FilterTestPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml @@ -1,5 +1,5 @@ @page "/debug/filter" -@model LBPUnion.ProjectLighthouse.Pages.Debug.FilterTestPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug.FilterTestPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/Debug/FilterTestPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml.cs similarity index 61% rename from ProjectLighthouse/Pages/Debug/FilterTestPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml.cs index 804cf19b..bb305761 100644 --- a/ProjectLighthouse/Pages/Debug/FilterTestPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Debug/FilterTestPage.cshtml.cs @@ -1,20 +1,19 @@ #nullable enable -using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages.Debug; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug; public class FilterTestPage : BaseLayout { public FilterTestPage(Database database) : base(database) {} - public string? FilteredText = null; - public string? Text = null; + public string? FilteredText; + public string? Text; - public async Task OnGet(string? text = null) + public IActionResult OnGet(string? text = null) { #if !DEBUG return this.NotFound(); diff --git a/ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml similarity index 79% rename from ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml index 6a16b67c..9845e193 100644 --- a/ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml @@ -1,8 +1,11 @@ @page "/debug/roomVisualizer" +@using LBPUnion.ProjectLighthouse.Extensions @using LBPUnion.ProjectLighthouse.Helpers +@using LBPUnion.ProjectLighthouse.Match.Rooms +@using LBPUnion.ProjectLighthouse.PlayerData +@using LBPUnion.ProjectLighthouse.PlayerData.Profiles @using LBPUnion.ProjectLighthouse.Types -@using LBPUnion.ProjectLighthouse.Types.Match -@model LBPUnion.ProjectLighthouse.Pages.Debug.RoomVisualizerPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug.RoomVisualizerPage @{ Layout = "Layouts/BaseLayout"; @@ -35,7 +38,7 @@ -

@RoomHelper.Rooms.Count rooms

+

@RoomHelper.Rooms.Count() rooms

Create Fake Room
@@ -50,6 +53,7 @@

Best rooms for each game version

@foreach (GameVersion version in Enum.GetValues()) { +#nullable enable if (version == GameVersion.LittleBigPlanet1 || version == GameVersion.LittleBigPlanetPSP || version == GameVersion.Unknown) continue; FindBestRoomResponse? response = RoomHelper.FindBestRoom(null, version, null, null, null); @@ -62,7 +66,7 @@ @foreach (Room room in RoomHelper.Rooms) { - bool userInRoom = room.Players.Select(p => p.Username).Contains(Model.User?.Username); + bool userInRoom = room.PlayerIds.Contains(Model.User?.UserId ?? -1); string color = userInRoom ? "green" : "blue";

Room @room.RoomId

@@ -72,9 +76,9 @@ You are currently in this room.

} -

@room.Players.Count players, state is @room.State, version is @room.RoomVersion.ToPrettyString()on paltform @room.RoomPlatform

+

@room.PlayerIds.Count players, state is @room.State, version is @room.RoomVersion.ToPrettyString() on platform @room.RoomPlatform

Slot type: @room.Slot.SlotType, slot id: @room.Slot.SlotId

- @foreach (User player in room.Players) + @foreach (User player in room.GetPlayers(Model.Database)) {
@player.Username
} diff --git a/ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml.cs similarity index 52% rename from ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml.cs index 370c2d24..8c8d24e3 100644 --- a/ProjectLighthouse/Pages/Debug/RoomVisualizerPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Debug/RoomVisualizerPage.cshtml.cs @@ -1,26 +1,26 @@ #nullable enable -using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Pages.Layouts; -using LBPUnion.ProjectLighthouse.Types; -using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages.Debug; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using Microsoft.AspNetCore.Mvc; +#if !DEBUG +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Types; +#endif + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug; public class RoomVisualizerPage : BaseLayout { - public RoomVisualizerPage([NotNull] Database database) : base(database) + public RoomVisualizerPage(Database database) : base(database) {} - public async Task OnGet() + public IActionResult OnGet() { #if !DEBUG User? user = this.Database.UserFromWebRequest(this.Request); if (user == null || !user.IsAdmin) return this.NotFound(); + #endif return this.Page(); - #else - return this.Page(); - #endif } } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Debug/VersionInfoPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Debug/VersionInfoPage.cshtml similarity index 90% rename from ProjectLighthouse/Pages/Debug/VersionInfoPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Debug/VersionInfoPage.cshtml index 4bd4cb77..6c437967 100644 --- a/ProjectLighthouse/Pages/Debug/VersionInfoPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Debug/VersionInfoPage.cshtml @@ -1,6 +1,6 @@ @page "/debug/version" @using LBPUnion.ProjectLighthouse.Helpers -@model LBPUnion.ProjectLighthouse.Pages.Debug.VersionInfoPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug.VersionInfoPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/Debug/VersionInfoPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Debug/VersionInfoPage.cshtml.cs similarity index 64% rename from ProjectLighthouse/Pages/Debug/VersionInfoPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/Debug/VersionInfoPage.cshtml.cs index 97b865b9..3b3da0d4 100644 --- a/ProjectLighthouse/Pages/Debug/VersionInfoPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Debug/VersionInfoPage.cshtml.cs @@ -1,8 +1,8 @@ using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages.Debug; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Debug; public class VersionInfoPage : BaseLayout { diff --git a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml similarity index 94% rename from ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml index d5a56773..28c5fb12 100644 --- a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml @@ -1,6 +1,7 @@ @page "/authentication" +@using LBPUnion.ProjectLighthouse.PlayerData @using LBPUnion.ProjectLighthouse.Types -@model LBPUnion.ProjectLighthouse.Pages.ExternalAuth.AuthenticationPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth.AuthenticationPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml.cs similarity index 62% rename from ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml.cs index bb1d745a..6e8cdc7c 100644 --- a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/AuthenticationPage.cshtml.cs @@ -1,28 +1,26 @@ #nullable enable -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth; public class AuthenticationPage : BaseLayout { - public List AuthenticationAttempts; + public List AuthenticationAttempts = new(); public IPAddress? IpAddress; public AuthenticationPage(Database database) : base(database) {} - public async Task OnGet() + 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/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml similarity index 79% rename from ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml index dde2fa8a..8226c086 100644 --- a/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml @@ -1,6 +1,7 @@ @page "/authentication/autoApprovals" +@using LBPUnion.ProjectLighthouse.PlayerData.Profiles @using LBPUnion.ProjectLighthouse.Types -@model LBPUnion.ProjectLighthouse.Pages.ExternalAuth.ManageUserApprovedIpAddressesPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth.ManageUserApprovedIpAddressesPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs similarity index 70% rename from ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs index bd70b6c3..8e399a75 100644 --- a/ProjectLighthouse/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/ExternalAuth/ManageUserApprovedIpAddressesPage.cshtml.cs @@ -1,18 +1,16 @@ #nullable enable -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth; public class ManageUserApprovedIpAddressesPage : BaseLayout { + public List ApprovedIpAddresses = new(); - public List ApprovedIpAddresses; public ManageUserApprovedIpAddressesPage(Database database) : base(database) {} diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml new file mode 100644 index 00000000..0fc14314 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml @@ -0,0 +1,81 @@ +@page "/" +@using LBPUnion.ProjectLighthouse.Configuration +@using LBPUnion.ProjectLighthouse.Extensions +@using LBPUnion.ProjectLighthouse.PlayerData.Profiles +@using LBPUnion.ProjectLighthouse.Levels +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.LandingPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.ShowTitleInPage = false; + bool isMobile = this.Request.IsMobile(); +} +

Welcome to @ServerConfiguration.Instance.Customization.ServerName!

+ +@if (Model.User != null) +{ +

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

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

+ You have @Model.AuthenticationAttemptsCount authentication attempts pending. Click here to view them. +

+ } +} + +@if (Model.PlayersOnlineCount == 1) +{ +

There is 1 user currently online:

+ @foreach (User user in Model.PlayersOnline) + { + @user.Username + } +} + +else if (Model.PlayersOnlineCount == 0) +{ +

There are no users online. Why not hop on?

+} +else +{ +

There are currently @Model.PlayersOnlineCount users online:

+ @foreach (User user in Model.PlayersOnline) + { + @user.Username + } +} + +
+ +
+
+
+

Latest Team Picks

+
+
+ @foreach (Slot slot in Model.LatestTeamPicks!) @* Can't reach a point where this is null *@ + { + @await Html.PartialAsync("Partials/SlotCardPartial", slot, Model.GetSlotViewData(slot.SlotId, isMobile)) +
+ } +
+
+
+ @if (isMobile) + { +
+ } +
+
+

Newest Levels

+
+
+ @foreach (Slot slot in Model.NewestLevels!) @* Can't reach a point where this is null *@ + { + @await Html.PartialAsync("Partials/SlotCardPartial", slot, Model.GetSlotViewData(slot.SlotId, isMobile)) +
+ } +
+
+
+
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs new file mode 100644 index 00000000..20b13dbc --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs @@ -0,0 +1,76 @@ +#nullable enable +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; + +public class LandingPage : BaseLayout +{ + public LandingPage(Database database) : base(database) + {} + + public int AuthenticationAttemptsCount; + public List PlayersOnline = new(); + + public int PlayersOnlineCount; + + public List? LatestTeamPicks; + public List? NewestLevels; + + [UsedImplicitly] + public async Task OnGet() + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); + + this.PlayersOnlineCount = await StatisticsHelper.RecentMatches(); + + if (user != null) + this.AuthenticationAttemptsCount = await this.Database.AuthenticationAttempts.Include + (a => a.GameToken) + .CountAsync(a => a.GameToken.UserId == user.UserId); + + List userIds = await this.Database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync(); + + this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync(); + + const int maxShownLevels = 5; + + this.LatestTeamPicks = await this.Database.Slots.Where + (s => s.TeamPick) + .OrderByDescending(s => s.FirstUploaded) + .Take(maxShownLevels) + .Include(s => s.Creator) + .ToListAsync(); + + this.NewestLevels = await this.Database.Slots.OrderByDescending(s => s.FirstUploaded).Take(maxShownLevels).Include(s => s.Creator).ToListAsync(); + + return this.Page(); + } + + public ViewDataDictionary GetSlotViewData(int slotId, bool isMobile = false) + => new(ViewData) + { + { + "User", this.User + }, + { + "CallbackUrl", $"~/slot/{slotId}" + }, + { + "ShowLink", true + }, + { + "IsMini", true + }, + { + "IsMobile", isMobile + }, + }; +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml similarity index 91% rename from ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml index ebc4355c..fca0b6a8 100644 --- a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml @@ -1,9 +1,9 @@ +@using LBPUnion.ProjectLighthouse.Configuration +@using LBPUnion.ProjectLighthouse.Extensions @using LBPUnion.ProjectLighthouse.Helpers -@using LBPUnion.ProjectLighthouse.Helpers.Extensions @using LBPUnion.ProjectLighthouse.Localization.StringLists @using LBPUnion.ProjectLighthouse.Types -@using LBPUnion.ProjectLighthouse.Types.Settings -@model LBPUnion.ProjectLighthouse.Pages.Layouts.BaseLayout +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts.BaseLayout @{ if (Model!.User == null) @@ -12,7 +12,7 @@ } else { - if (ServerSettings.Instance.UseExternalAuth) + if (ServerConfiguration.Instance.Authentication.UseExternalAuth) { Model.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderAuthentication, "/authentication", "key")); } @@ -26,7 +26,6 @@ } Model.IsMobile = Model.Request.IsMobile(); - long timeStarted = TimestampHelper.TimestampMillis; } @@ -35,11 +34,11 @@ @if (Model.Title == string.Empty) { - Project Lighthouse + @ServerConfiguration.Instance.Customization.ServerName } else { - Project Lighthouse - @Model.Title + @ServerConfiguration.Instance.Customization.ServerName - @Model.Title } @@ -54,7 +53,7 @@ @* Embed Stuff *@ - + @if (!string.IsNullOrEmpty(Model.Description)) { @@ -63,16 +62,16 @@ @* Google Analytics *@ - @if (ServerSettings.Instance.GoogleAnalyticsEnabled) + @if (ServerConfiguration.Instance.GoogleAnalytics.AnalyticsEnabled) { - + } @@ -180,7 +179,6 @@

Model.Title: @Model.Title

Model.Description: @Model.Description

Model.User.UserId: @(Model.User?.UserId.ToString() ?? "(not logged in)")

-

Render time: ~@(TimestampHelper.TimestampMillis - timeStarted)ms

diff --git a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs similarity index 94% rename from ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs index 1d5dcd29..9ea0510f 100644 --- a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs @@ -3,11 +3,12 @@ using System; using System.Collections.Generic; using LBPUnion.ProjectLighthouse.Localization; using LBPUnion.ProjectLighthouse.Localization.StringLists; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc.RazorPages; -namespace LBPUnion.ProjectLighthouse.Pages.Layouts; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; public class BaseLayout : PageModel { diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml b/ProjectLighthouse.Servers.Website/Pages/LoginForm.cshtml similarity index 87% rename from ProjectLighthouse/Pages/LoginForm.cshtml rename to ProjectLighthouse.Servers.Website/Pages/LoginForm.cshtml index f07daa9f..9dd0dfa0 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/LoginForm.cshtml @@ -1,6 +1,6 @@ @page "/login" -@using LBPUnion.ProjectLighthouse.Types.Settings -@model LBPUnion.ProjectLighthouse.Pages.LoginForm +@using LBPUnion.ProjectLighthouse.Configuration +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.LoginForm @{ Layout = "Layouts/BaseLayout"; @@ -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.Servers.Website/Pages/LoginForm.cshtml.cs similarity index 62% rename from ProjectLighthouse/Pages/LoginForm.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/LoginForm.cshtml.cs index 86cb9b9f..656a0bf7 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/LoginForm.cshtml.cs @@ -1,27 +1,25 @@ #nullable enable -using System; -using System.Threading.Tasks; using JetBrains.Annotations; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Profiles.Email; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class LoginForm : BaseLayout { public LoginForm(Database database) : base(database) {} - public string Error { get; private set; } + public string? Error { get; private set; } [UsedImplicitly] public async Task OnPost(string username, string password) @@ -38,7 +36,7 @@ public class LoginForm : BaseLayout return this.Page(); } - if (!await Request.CheckCaptchaValidity()) + if (!await this.Request.CheckCaptchaValidity()) { this.Error = "You must complete the captcha correctly."; return this.Page(); @@ -47,34 +45,34 @@ public class LoginForm : BaseLayout User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); if (user == null) { - Logger.Log($"User {username} failed to login on web due to invalid username", LoggerLevelLogin.Instance); + Logger.Warn($"User {username} failed to login on web due to invalid username", LogArea.Login); this.Error = "The username or password you entered is invalid."; return this.Page(); } if (!BCrypt.Net.BCrypt.Verify(password, user.Password)) { - Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LoggerLevelLogin.Instance); + Logger.Warn($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LogArea.Login); this.Error = "The username or password you entered is invalid."; return this.Page(); } if (user.Banned) { - Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LoggerLevelLogin.Instance); + Logger.Warn($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LogArea.Login); this.Error = "You have been banned. Please contact an administrator for more information.\nReason: " + user.BannedReason; return this.Page(); } - if (user.EmailAddress == null && ServerSettings.Instance.SMTPEnabled) + if (user.EmailAddress == null && ServerConfiguration.Instance.Mail.MailEnabled) { - Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login; email not set", LoggerLevelLogin.Instance); + Logger.Warn($"User {user.Username} (id: {user.UserId}) failed to login; email not set", LogArea.Login); EmailSetToken emailSetToken = new() { UserId = user.UserId, User = user, - EmailToken = HashHelper.GenerateAuthToken(), + EmailToken = CryptoHelper.GenerateAuthToken(), }; this.Database.EmailSetTokens.Add(emailSetToken); @@ -86,7 +84,7 @@ public class LoginForm : BaseLayout WebToken webToken = new() { UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), + UserToken = CryptoHelper.GenerateAuthToken(), }; this.Database.WebTokens.Add(webToken); @@ -102,18 +100,14 @@ public class LoginForm : BaseLayout } ); - Logger.Log($"User {user.Username} (id: {user.UserId}) successfully logged in on web", LoggerLevelLogin.Instance); + Logger.Success($"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)); } [UsedImplicitly] - public async Task OnGet() - { - this.Error = string.Empty; - return this.Page(); - } + public IActionResult OnGet() => this.Page(); } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LogoutPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/LogoutPage.cshtml similarity index 80% rename from ProjectLighthouse/Pages/LogoutPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/LogoutPage.cshtml index 589ed4f5..62f902c1 100644 --- a/ProjectLighthouse/Pages/LogoutPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/LogoutPage.cshtml @@ -1,5 +1,5 @@ @page "/logout" -@model LBPUnion.ProjectLighthouse.Pages.LogoutPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.LogoutPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/LogoutPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/LogoutPage.cshtml.cs similarity index 76% rename from ProjectLighthouse/Pages/LogoutPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/LogoutPage.cshtml.cs index ad8dd64c..bf28f740 100644 --- a/ProjectLighthouse/Pages/LogoutPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/LogoutPage.cshtml.cs @@ -1,10 +1,10 @@ #nullable enable -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class LogoutPage : BaseLayout { diff --git a/ProjectLighthouse/Pages/Partials/AdminPanelStatisticPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/AdminPanelStatisticPartial.cshtml similarity index 85% rename from ProjectLighthouse/Pages/Partials/AdminPanelStatisticPartial.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Partials/AdminPanelStatisticPartial.cshtml index 4acff434..49db6781 100644 --- a/ProjectLighthouse/Pages/Partials/AdminPanelStatisticPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/AdminPanelStatisticPartial.cshtml @@ -1,4 +1,4 @@ -@model LBPUnion.ProjectLighthouse.Types.AdminPanelStatistic +@model LBPUnion.ProjectLighthouse.Administration.AdminPanelStatistic
diff --git a/ProjectLighthouse/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml similarity index 87% rename from ProjectLighthouse/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml index bbec5bec..fa165cdf 100644 --- a/ProjectLighthouse/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/AdminSetGrantedSlotsFormPartial.cshtml @@ -1,4 +1,4 @@ -@model LBPUnion.ProjectLighthouse.Types.User +@model LBPUnion.ProjectLighthouse.PlayerData.Profiles.User
@Html.AntiForgeryToken() diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/CaptchaPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/CaptchaPartial.cshtml new file mode 100644 index 00000000..d54cebce --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/CaptchaPartial.cshtml @@ -0,0 +1,6 @@ +@using LBPUnion.ProjectLighthouse.Configuration +@if (ServerConfiguration.Instance.Captcha.CaptchaEnabled) +{ +
+ +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Partials/CommentsPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml similarity index 91% rename from ProjectLighthouse/Pages/Partials/CommentsPartial.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml index f921e5ad..e771e409 100644 --- a/ProjectLighthouse/Pages/Partials/CommentsPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml @@ -1,6 +1,5 @@ -@using System.IO -@using System.Web -@using LBPUnion.ProjectLighthouse.Types.Profiles +@using System.Web +@using LBPUnion.ProjectLighthouse.PlayerData.Profiles

Comments

@if (Model.Comments.Count == 0 && Model.CommentsEnabled) @@ -28,6 +27,10 @@
+ @if (Model.Comments.Count > 0) + { +
+ } } @for(int i = 0; i < Model.Comments.Count; i++) @@ -38,7 +41,8 @@ HttpUtility.HtmlDecode(comment.getComment(), messageWriter); string decodedMessage = messageWriter.ToString(); - string url = Url.RouteUrl(ViewContext.RouteData.Values); + string? url = Url.RouteUrl(ViewContext.RouteData.Values); + if (url == null) continue; int rating = comment.ThumbsUp - comment.ThumbsDown; diff --git a/ProjectLighthouse/Pages/Partials/PhotoPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml similarity index 97% rename from ProjectLighthouse/Pages/Partials/PhotoPartial.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml index 2b422c5a..66c8b0fa 100644 --- a/ProjectLighthouse/Pages/Partials/PhotoPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml @@ -1,5 +1,6 @@ +@using LBPUnion.ProjectLighthouse.PlayerData @using LBPUnion.ProjectLighthouse.Types -@model LBPUnion.ProjectLighthouse.Types.Photo +@model LBPUnion.ProjectLighthouse.PlayerData.Photo
diff --git a/ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/SlotCardPartial.cshtml similarity index 74% rename from ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Partials/SlotCardPartial.cshtml index 3852e600..371d5920 100644 --- a/ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/SlotCardPartial.cshtml @@ -1,16 +1,19 @@ @using LBPUnion.ProjectLighthouse -@using LBPUnion.ProjectLighthouse.Types -@using LBPUnion.ProjectLighthouse.Types.Settings +@using LBPUnion.ProjectLighthouse.Configuration +@using LBPUnion.ProjectLighthouse.PlayerData +@using LBPUnion.ProjectLighthouse.PlayerData.Profiles @using Microsoft.EntityFrameworkCore -@model LBPUnion.ProjectLighthouse.Types.Levels.Slot +@model LBPUnion.ProjectLighthouse.Levels.Slot @{ - User user = (User)ViewData["User"]; + User? user = (User?)ViewData["User"]; await using Database database = new(); string slotName = string.IsNullOrEmpty(Model.Name) ? "Unnamed Level" : Model.Name; + bool isMobile = (bool?)ViewData["IsMobile"] ?? false; + bool mini = (bool?)ViewData["IsMini"] ?? false; bool isQueued = false; bool isHearted = false; @@ -18,37 +21,55 @@ if (user != null) { isQueued = await database.QueuedLevels.FirstOrDefaultAsync(h => h.SlotId == Model.SlotId && h.UserId == user.UserId) != null; - isHearted = await database.HeartedLevels.FirstOrDefaultAsync(h => h.SlotId == Model.SlotId && h.UserId == user.UserId) != null; } - string callbackUrl = (string)ViewData["CallbackUrl"]; + string callbackUrl = (string)ViewData["CallbackUrl"]!; 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; }
@{ - int size = isMobile ? 50 : 100; + int size = isMobile || mini ? 50 : 100; }
- @if (showLink) + @if (!mini) { -

- @slotName -

+ @if (showLink) + { +

+ @slotName +

+ } + else + { +

+ @slotName +

+ } } else { -

- @slotName -

+ @if (showLink) + { +

+ @slotName +

+ } + else + { +

+ @slotName +

+ } } +
@Model.Hearts @Model.PlaysUnique @@ -67,7 +88,7 @@

- @if (user != null) + @if (user != null && !mini) { if (isHearted) { diff --git a/ProjectLighthouse/Pages/Partials/UserCardPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/UserCardPartial.cshtml similarity index 95% rename from ProjectLighthouse/Pages/Partials/UserCardPartial.cshtml rename to ProjectLighthouse.Servers.Website/Pages/Partials/UserCardPartial.cshtml index b2751508..8e3386ea 100644 --- a/ProjectLighthouse/Pages/Partials/UserCardPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/UserCardPartial.cshtml @@ -1,4 +1,4 @@ -@model LBPUnion.ProjectLighthouse.Types.User +@model LBPUnion.ProjectLighthouse.PlayerData.Profiles.User @{ bool showLink = (bool?)ViewData["ShowLink"] ?? false; diff --git a/ProjectLighthouse/Pages/PasswordResetPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/PasswordResetPage.cshtml similarity index 95% rename from ProjectLighthouse/Pages/PasswordResetPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/PasswordResetPage.cshtml index c6e0f631..2f8a3ff3 100644 --- a/ProjectLighthouse/Pages/PasswordResetPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/PasswordResetPage.cshtml @@ -1,5 +1,5 @@ @page "/passwordReset" -@model LBPUnion.ProjectLighthouse.Pages.PasswordResetPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.PasswordResetPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/PasswordResetPage.cshtml.cs similarity index 70% rename from ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/PasswordResetPage.cshtml.cs index 62b5129e..6f744979 100644 --- a/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/PasswordResetPage.cshtml.cs @@ -1,19 +1,20 @@ #nullable enable -using System.Threading.Tasks; using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class PasswordResetPage : BaseLayout { public PasswordResetPage(Database database) : base(database) {} - public string Error { get; private set; } + public string? Error { get; private set; } [UsedImplicitly] public async Task OnPost(string password, string confirmPassword) @@ -33,12 +34,13 @@ public class PasswordResetPage : BaseLayout return this.Page(); } - user.Password = HashHelper.BCryptHash(password); + user.Password = CryptoHelper.BCryptHash(password); user.PasswordResetRequired = false; await this.Database.SaveChangesAsync(); - if (!user.EmailAddressVerified) return this.Redirect("~/login/sendVerificationEmail"); + if (!user.EmailAddressVerified && ServerConfiguration.Instance.Mail.MailEnabled) + return this.Redirect("~/login/sendVerificationEmail"); return this.Redirect("~/"); } diff --git a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/PasswordResetRequiredPage.cshtml similarity index 78% rename from ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/PasswordResetRequiredPage.cshtml index afe20489..6724cc96 100644 --- a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/PasswordResetRequiredPage.cshtml @@ -1,5 +1,5 @@ @page "/passwordResetRequired" -@model LBPUnion.ProjectLighthouse.Pages.PasswordResetRequiredPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.PasswordResetRequiredPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/PasswordResetRequiredPage.cshtml.cs similarity index 61% rename from ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/PasswordResetRequiredPage.cshtml.cs index 9739b4d3..ff041a7a 100644 --- a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/PasswordResetRequiredPage.cshtml.cs @@ -1,20 +1,19 @@ #nullable enable -using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class PasswordResetRequiredPage : BaseLayout { - public PasswordResetRequiredPage([NotNull] Database database) : base(database) + public PasswordResetRequiredPage(Database database) : base(database) {} public bool WasResetRequest { get; private set; } - public async Task OnGet() + public IActionResult OnGet() { User? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); diff --git a/ProjectLighthouse/Pages/PhotosPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/PhotosPage.cshtml similarity index 65% rename from ProjectLighthouse/Pages/PhotosPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/PhotosPage.cshtml index cf46f3bf..bbcf2cd3 100644 --- a/ProjectLighthouse/Pages/PhotosPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/PhotosPage.cshtml @@ -1,6 +1,7 @@ @page "/photos/{pageNumber:int}" +@using LBPUnion.ProjectLighthouse.PlayerData @using LBPUnion.ProjectLighthouse.Types -@model LBPUnion.ProjectLighthouse.Pages.PhotosPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.PhotosPage @{ Layout = "Layouts/BaseLayout"; @@ -26,10 +27,10 @@ @if (Model.PageNumber != 0) { - Previous Page + Previous Page } @(Model.PageNumber + 1) / @(Model.PageAmount) @if (Model.PageNumber < Model.PageAmount - 1) { - Next Page + Next Page } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/PhotosPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/PhotosPage.cshtml.cs similarity index 55% rename from ProjectLighthouse/Pages/PhotosPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/PhotosPage.cshtml.cs index dcf8cef0..b636205e 100644 --- a/ProjectLighthouse/Pages/PhotosPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/PhotosPage.cshtml.cs @@ -1,16 +1,12 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class PhotosPage : BaseLayout { @@ -21,10 +17,10 @@ public class PhotosPage : BaseLayout public int PhotoCount; - public List Photos; + public List Photos = new(); - public string SearchValue; - public PhotosPage([NotNull] Database database) : base(database) + public string? SearchValue; + public PhotosPage(Database database) : base(database) {} public async Task OnGet([FromRoute] int pageNumber, [FromQuery] string? name) @@ -33,15 +29,18 @@ public class PhotosPage : BaseLayout this.SearchValue = name.Replace(" ", string.Empty); - this.PhotoCount = await this.Database.Photos.CountAsync(p => p.Creator.Username.Contains(this.SearchValue) || p.PhotoSubjectCollection.Contains(this.SearchValue)); + this.PhotoCount = await this.Database.Photos.Include + (p => p.Creator) + .CountAsync(p => p.Creator!.Username.Contains(this.SearchValue) || p.PhotoSubjectCollection.Contains(this.SearchValue)); this.PageNumber = pageNumber; this.PageAmount = Math.Max(1, (int)Math.Ceiling((double)this.PhotoCount / ServerStatics.PageSize)); if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/photos/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); - this.Photos = await this.Database.Photos.Include(p => p.Creator) - .Where(p => p.Creator.Username.Contains(this.SearchValue) || p.PhotoSubjectCollection.Contains(this.SearchValue)) + this.Photos = await this.Database.Photos.Include + (p => p.Creator) + .Where(p => p.Creator!.Username.Contains(this.SearchValue) || p.PhotoSubjectCollection.Contains(this.SearchValue)) .OrderByDescending(p => p.Timestamp) .Skip(pageNumber * ServerStatics.PageSize) .Take(ServerStatics.PageSize) diff --git a/ProjectLighthouse/Pages/RegisterForm.cshtml b/ProjectLighthouse.Servers.Website/Pages/RegisterForm.cshtml similarity index 93% rename from ProjectLighthouse/Pages/RegisterForm.cshtml rename to ProjectLighthouse.Servers.Website/Pages/RegisterForm.cshtml index 9cbbe64e..1a08875b 100644 --- a/ProjectLighthouse/Pages/RegisterForm.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/RegisterForm.cshtml @@ -1,6 +1,6 @@ @page "/register" -@using LBPUnion.ProjectLighthouse.Types.Settings -@model LBPUnion.ProjectLighthouse.Pages.RegisterForm +@using LBPUnion.ProjectLighthouse.Configuration +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.RegisterForm @{ Layout = "Layouts/BaseLayout"; @@ -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.Servers.Website/Pages/RegisterForm.cshtml.cs similarity index 65% rename from ProjectLighthouse/Pages/RegisterForm.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/RegisterForm.cshtml.cs index 28a70ee3..fa49b753 100644 --- a/ProjectLighthouse/Pages/RegisterForm.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/RegisterForm.cshtml.cs @@ -1,28 +1,29 @@ using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class RegisterForm : BaseLayout { public RegisterForm(Database database) : base(database) {} - public string Error { get; private set; } + public string? Error { get; private set; } [UsedImplicitly] [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 +37,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,25 +55,25 @@ public class RegisterForm : BaseLayout return this.Page(); } - if (ServerSettings.Instance.SMTPEnabled && - await this.Database.Users.FirstOrDefaultAsync(u => u.EmailAddress.ToLower() == emailAddress.ToLower()) != null) + if (ServerConfiguration.Instance.Mail.MailEnabled && + await this.Database.Users.FirstOrDefaultAsync(u => u.EmailAddress != null && u.EmailAddress.ToLower() == emailAddress.ToLower()) != null) { this.Error = "The email address you've chosen is already taken."; return this.Page(); } - if (!await Request.CheckCaptchaValidity()) + if (!await this.Request.CheckCaptchaValidity()) { this.Error = "You must complete the captcha correctly."; return this.Page(); } - User user = await this.Database.CreateUser(username, HashHelper.BCryptHash(password), emailAddress); + User user = await this.Database.CreateUser(username, CryptoHelper.BCryptHash(password), emailAddress); WebToken webToken = new() { UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), + UserToken = CryptoHelper.GenerateAuthToken(), }; this.Database.WebTokens.Add(webToken); @@ -80,7 +81,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 +91,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/ReportsPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/ReportsPage.cshtml similarity index 98% rename from ProjectLighthouse/Pages/ReportsPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/ReportsPage.cshtml index bd8ea8f4..993074dd 100644 --- a/ProjectLighthouse/Pages/ReportsPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/ReportsPage.cshtml @@ -1,6 +1,6 @@ @page "/admin/reports/{pageNumber:int}" -@using LBPUnion.ProjectLighthouse.Types.Reports -@model LBPUnion.ProjectLighthouse.Pages.ReportsPage +@using LBPUnion.ProjectLighthouse.Administration.Reports +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.ReportsPage @{ Layout = "Layouts/BaseLayout"; diff --git a/ProjectLighthouse/Pages/ReportsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/ReportsPage.cshtml.cs similarity index 67% rename from ProjectLighthouse/Pages/ReportsPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/ReportsPage.cshtml.cs index 54c7423d..50f21c3c 100644 --- a/ProjectLighthouse/Pages/ReportsPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/ReportsPage.cshtml.cs @@ -1,18 +1,14 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Linq; using System.Text.Json; -using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Administration.Reports; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Reports; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class ReportsPage : BaseLayout { @@ -23,12 +19,12 @@ public class ReportsPage : BaseLayout public int ReportCount; - public List Reports; + public List Reports = new(); - public string SearchValue; + public string SearchValue = ""; - public ReportsPage([NotNull] Database database) : base(database) - { } + public ReportsPage(Database database) : base(database) + {} public async Task OnGet([FromRoute] int pageNumber, [FromQuery] string? name) { @@ -43,7 +39,7 @@ public class ReportsPage : BaseLayout this.ReportCount = await this.Database.Reports.Include(r => r.ReportingPlayer).CountAsync(r => r.ReportingPlayer.Username.Contains(this.SearchValue)); this.PageNumber = pageNumber; - this.PageAmount = Math.Max(1, (int) Math.Ceiling((double) this.ReportCount / ServerStatics.PageSize)); + this.PageAmount = Math.Max(1, (int)Math.Ceiling((double)this.ReportCount / ServerStatics.PageSize)); if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/admin/reports/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); @@ -57,11 +53,11 @@ public class ReportsPage : BaseLayout foreach (GriefReport r in this.Reports) { - r.XmlPlayers = (ReportPlayer[]) JsonSerializer.Deserialize(r.Players, typeof(ReportPlayer[]))!; + r.XmlPlayers = (ReportPlayer[])JsonSerializer.Deserialize(r.Players, typeof(ReportPlayer[]))!; r.XmlBounds = new Marqee() { - Rect = (Rectangle) JsonSerializer.Deserialize(r.Bounds, typeof(Rectangle))!, + Rect = (Rectangle)JsonSerializer.Deserialize(r.Bounds, typeof(Rectangle))!, }; } diff --git a/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/SendVerificationEmailPage.cshtml similarity index 68% rename from ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/SendVerificationEmailPage.cshtml index 09e0f2dc..cae13524 100644 --- a/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/SendVerificationEmailPage.cshtml @@ -1,5 +1,5 @@ @page "/login/sendVerificationEmail" -@model LBPUnion.ProjectLighthouse.Pages.SendVerificationEmailPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.SendVerificationEmailPage @{ Layout = "Layouts/BaseLayout"; @@ -7,7 +7,7 @@ }

An email address on your account has been set, but hasn't been verified yet.

-

To verify it, check the email sent to @Model.User.EmailAddress and click the link in the email.

+

To verify it, check the email sent to @Model.User?.EmailAddress and click the link in the email.

Resend email
diff --git a/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/SendVerificationEmailPage.cshtml.cs similarity index 76% rename from ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/SendVerificationEmailPage.cshtml.cs index 04c9dc9b..fae0a1d4 100644 --- a/ProjectLighthouse/Pages/SendVerificationEmailPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/SendVerificationEmailPage.cshtml.cs @@ -1,14 +1,13 @@ #nullable enable -using System; -using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Profiles.Email; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class SendVerificationEmailPage : BaseLayout { @@ -17,7 +16,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"); @@ -38,7 +37,7 @@ public class SendVerificationEmailPage : BaseLayout { UserId = user.UserId, User = user, - EmailToken = HashHelper.GenerateAuthToken(), + EmailToken = CryptoHelper.GenerateAuthToken(), }; this.Database.EmailVerificationTokens.Add(verifyToken); @@ -47,7 +46,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.Servers.Website/Pages/SetEmailForm.cshtml similarity index 81% rename from ProjectLighthouse/Pages/SetEmailForm.cshtml rename to ProjectLighthouse.Servers.Website/Pages/SetEmailForm.cshtml index 3bac01b4..38782a30 100644 --- a/ProjectLighthouse/Pages/SetEmailForm.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/SetEmailForm.cshtml @@ -1,6 +1,6 @@ @page "/login/setEmail" -@using LBPUnion.ProjectLighthouse.Types.Settings -@model LBPUnion.ProjectLighthouse.Pages.SetEmailForm +@using LBPUnion.ProjectLighthouse.Configuration +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.SetEmailForm @{ Layout = "Layouts/BaseLayout"; @@ -12,7 +12,7 @@
@Html.AntiForgeryToken() - @if (ServerSettings.Instance.SMTPEnabled) + @if (ServerConfiguration.Instance.Mail.MailEnabled) {
@@ -21,7 +21,7 @@
- +
} diff --git a/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/SetEmailForm.cshtml.cs similarity index 71% rename from ProjectLighthouse/Pages/SetEmailForm.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/SetEmailForm.cshtml.cs index 1b39d592..05d57ee3 100644 --- a/ProjectLighthouse/Pages/SetEmailForm.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/SetEmailForm.cshtml.cs @@ -1,29 +1,27 @@ #nullable enable -using System; -using System.Threading.Tasks; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Profiles.Email; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class SetEmailForm : BaseLayout { public SetEmailForm(Database database) : base(database) {} - public EmailSetToken EmailToken; + public EmailSetToken? EmailToken; 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); @@ -36,7 +34,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"); @@ -50,7 +48,7 @@ public class SetEmailForm : BaseLayout { UserId = user.UserId, User = user, - EmailToken = HashHelper.GenerateAuthToken(), + EmailToken = CryptoHelper.GenerateAuthToken(), }; this.Database.EmailVerificationTokens.Add(emailVerifyToken); @@ -59,7 +57,7 @@ public class SetEmailForm : BaseLayout WebToken webToken = new() { UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), + UserToken = CryptoHelper.GenerateAuthToken(), }; this.Response.Cookies.Append @@ -72,7 +70,7 @@ public class SetEmailForm : BaseLayout } ); - Logger.Log($"User {user.Username} (id: {user.UserId}) successfully logged in on web after setting an email address", LoggerLevelLogin.Instance); + Logger.Success($"User {user.Username} (id: {user.UserId}) successfully logged in on web after setting an email address", LogArea.Login); this.Database.WebTokens.Add(webToken); await this.Database.SaveChangesAsync(); diff --git a/ProjectLighthouse/Pages/SlotPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml similarity index 80% rename from ProjectLighthouse/Pages/SlotPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml index 1f8b9a96..0722a616 100644 --- a/ProjectLighthouse/Pages/SlotPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml @@ -1,17 +1,18 @@ @page "/slot/{id:int}" @using System.Web -@using LBPUnion.ProjectLighthouse.Helpers.Extensions +@using LBPUnion.ProjectLighthouse.Administration +@using LBPUnion.ProjectLighthouse.Configuration +@using LBPUnion.ProjectLighthouse.Extensions +@using LBPUnion.ProjectLighthouse.PlayerData.Reviews @using LBPUnion.ProjectLighthouse.Types -@using LBPUnion.ProjectLighthouse.Types.Reviews -@using LBPUnion.ProjectLighthouse.Types.Settings -@model LBPUnion.ProjectLighthouse.Pages.SlotPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.SlotPage @{ Layout = "Layouts/BaseLayout"; Model.ShowTitleInPage = false; - Model.Title = Model.Slot.Name; - Model.Description = Model.Slot.Description; + Model.Title = Model.Slot?.Name ?? ""; + Model.Description = Model.Slot?.Description ?? ""; bool isMobile = this.Request.IsMobile(); } @@ -22,7 +23,7 @@ "User", Model.User }, { - "CallbackUrl", $"~/slot/{Model.Slot.SlotId}" + "CallbackUrl", $"~/slot/{Model.Slot?.SlotId}" }, { "ShowLink", false @@ -37,14 +38,15 @@

Description

-

@HttpUtility.HtmlDecode(string.IsNullOrEmpty(Model.Slot.Description) ? "This level has no description." : Model.Slot.Description)

+

@HttpUtility.HtmlDecode(string.IsNullOrEmpty(Model.Slot?.Description) ? "This level has no description." : Model.Slot.Description)

Tags

@{ - string[] authorLabels = Model.Slot.AuthorLabels.Split(","); + string[] authorLabels = Model.Slot?.AuthorLabels.Split(",") ?? new string[] + {}; if (authorLabels.Length == 1) // ..?? ok c# {

This level has no tags.

@@ -79,26 +81,26 @@ { int count = Model.Reviews.Count;

There @(count == 1 ? "is" : "are") @count review@(count == 1 ? "" : "s").

+
} -
- @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 { + string faceHash = (review.Thumb switch { -1 => review.Reviewer?.BooHash, 0 => review.Reviewer?.MehHash, 1 => review.Reviewer?.YayHash, _ => throw new ArgumentOutOfRangeException(), - }; + }) ?? ""; if (string.IsNullOrWhiteSpace(faceHash)) { - faceHash = ServerSettings.Instance.MissingIconHash; + faceHash = ServerConfiguration.Instance.WebsiteConfiguration.MissingIconHash; } - string faceAlt = review.Thumb switch { -1 => "Boo!", 0 => "Meh.", @@ -111,7 +113,7 @@
- @faceAlt + @faceAlt

@review.Reviewer?.Username

@@ -148,7 +150,7 @@ else { { -

@review.Text

+

@HttpUtility.HtmlDecode(review.Text)

} } } @@ -168,7 +170,7 @@

Admin Options

- @if (Model.Slot.TeamPick) + @if (Model.Slot?.TeamPick ?? false) {
@@ -179,7 +181,7 @@ } else { - +
Team Pick @@ -187,7 +189,7 @@ } - +
Delete diff --git a/ProjectLighthouse/Pages/SlotPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs similarity index 70% rename from ProjectLighthouse/Pages/SlotPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs index 893683db..47d8f265 100644 --- a/ProjectLighthouse/Pages/SlotPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs @@ -1,27 +1,25 @@ #nullable enable -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Reviews; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Profiles; -using LBPUnion.ProjectLighthouse.Types.Reviews; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class SlotPage : BaseLayout { - public List Comments; - public List Reviews; + 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 Slot? Slot; public SlotPage(Database database) : base(database) {} diff --git a/ProjectLighthouse/Pages/SlotsPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml similarity index 71% rename from ProjectLighthouse/Pages/SlotsPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml index 610838ea..e92606c6 100644 --- a/ProjectLighthouse/Pages/SlotsPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml @@ -1,7 +1,7 @@ @page "/slots/{pageNumber:int}" -@using LBPUnion.ProjectLighthouse.Helpers.Extensions -@using LBPUnion.ProjectLighthouse.Types.Levels -@model LBPUnion.ProjectLighthouse.Pages.SlotsPage +@using LBPUnion.ProjectLighthouse.Extensions +@using LBPUnion.ProjectLighthouse.Levels +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.SlotsPage @{ Layout = "Layouts/BaseLayout"; @@ -42,10 +42,10 @@ @if (Model.PageNumber != 0) { - Previous Page + Previous Page } @(Model.PageNumber + 1) / @(Model.PageAmount) @if (Model.PageNumber < Model.PageAmount - 1) { - Next Page + Next Page } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml.cs new file mode 100644 index 00000000..2bc11efa --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/SlotsPage.cshtml.cs @@ -0,0 +1,79 @@ +#nullable enable +using System.Text; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; + +public class SlotsPage : BaseLayout +{ + + public int PageAmount; + + public int PageNumber; + + public int SlotCount; + + public List Slots = new(); + + public string? SearchValue; + + public SlotsPage(Database database) : base(database) + {} + + public async Task OnGet([FromRoute] int pageNumber, [FromQuery] string? name) + { + if (string.IsNullOrWhiteSpace(name)) name = ""; + + string? targetAuthor = null; + GameVersion? targetGame = null; + StringBuilder finalSearch = new(); + foreach (string part in name.Split(" ")) + { + if (part.Contains("by:")) + { + targetAuthor = part.Replace("by:", ""); + } + else if (part.Contains("game:")) + { + if (part.Contains('1')) targetGame = GameVersion.LittleBigPlanet1; + else if (part.Contains('2')) targetGame = GameVersion.LittleBigPlanet2; + else if (part.Contains('3')) targetGame = GameVersion.LittleBigPlanet3; + else if (part.Contains('v')) targetGame = GameVersion.LittleBigPlanetVita; + } + else + { + finalSearch.Append(part); + } + } + + this.SearchValue = name.Trim(); + + this.SlotCount = await this.Database.Slots.Include(p => p.Creator) + .Where(p => p.Name.Contains(finalSearch.ToString())) + .Where(p => p.Creator != null && (targetAuthor == null || string.Equals(p.Creator.Username.ToLower(), targetAuthor.ToLower()))) + .Where(p => targetGame == null || p.GameVersion == targetGame) + .CountAsync(); + + this.PageNumber = pageNumber; + this.PageAmount = Math.Max(1, (int)Math.Ceiling((double)this.SlotCount / ServerStatics.PageSize)); + + if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); + + this.Slots = await this.Database.Slots.Include(p => p.Creator) + .Where(p => p.Name.Contains(finalSearch.ToString())) + .Where(p => p.Creator != null && (targetAuthor == null || string.Equals(p.Creator.Username.ToLower(), targetAuthor.ToLower()))) + .Where(p => targetGame == null || p.GameVersion == targetGame) + .OrderByDescending(p => p.FirstUploaded) + .Skip(pageNumber * ServerStatics.PageSize) + .Take(ServerStatics.PageSize) + .ToListAsync(); + + return this.Page(); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/UserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml similarity index 89% rename from ProjectLighthouse/Pages/UserPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml index 2ef24243..e3761284 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml @@ -1,8 +1,9 @@ @page "/user/{userId:int}" @using System.Web -@using LBPUnion.ProjectLighthouse.Helpers.Extensions +@using LBPUnion.ProjectLighthouse.Extensions +@using LBPUnion.ProjectLighthouse.PlayerData @using LBPUnion.ProjectLighthouse.Types -@model LBPUnion.ProjectLighthouse.Pages.UserPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.UserPage @{ Layout = "Layouts/BaseLayout"; @@ -129,6 +130,15 @@
} + + + + @await Html.PartialAsync("Partials/AdminSetGrantedSlotsFormPartial", Model.ProfileUser)
} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/UserPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs similarity index 64% rename from ProjectLighthouse/Pages/UserPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs index 04562e55..067ee0a2 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs @@ -1,21 +1,19 @@ #nullable enable -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Profiles; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; 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 +43,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/Pages/UsersPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UsersPage.cshtml similarity index 68% rename from ProjectLighthouse/Pages/UsersPage.cshtml rename to ProjectLighthouse.Servers.Website/Pages/UsersPage.cshtml index ea6ae9ec..32195a8f 100644 --- a/ProjectLighthouse/Pages/UsersPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/UsersPage.cshtml @@ -1,7 +1,8 @@ @page "/users/{pageNumber:int}" -@using LBPUnion.ProjectLighthouse.Helpers.Extensions +@using LBPUnion.ProjectLighthouse.Extensions +@using LBPUnion.ProjectLighthouse.PlayerData.Profiles @using LBPUnion.ProjectLighthouse.Types -@model LBPUnion.ProjectLighthouse.Pages.UsersPage +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.UsersPage @{ Layout = "Layouts/BaseLayout"; @@ -9,7 +10,7 @@ }

There are @Model.UserCount total users.

- +
@@ -36,10 +37,10 @@ @if (Model.PageNumber != 0) { - Previous Page + Previous Page } @(Model.PageNumber + 1) / @(Model.PageAmount) @if (Model.PageNumber < Model.PageAmount - 1) { - Next Page + Next Page } \ No newline at end of file diff --git a/ProjectLighthouse/Pages/UsersPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/UsersPage.cshtml.cs similarity index 75% rename from ProjectLighthouse/Pages/UsersPage.cshtml.cs rename to ProjectLighthouse.Servers.Website/Pages/UsersPage.cshtml.cs index ce2a3e22..fadb934e 100644 --- a/ProjectLighthouse/Pages/UsersPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/UsersPage.cshtml.cs @@ -1,16 +1,12 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Pages; +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class UsersPage : BaseLayout { @@ -20,11 +16,11 @@ public class UsersPage : BaseLayout public int UserCount; - public List Users; + public List Users = new(); - public string SearchValue; + public string? SearchValue; - public UsersPage([NotNull] Database database) : base(database) + public UsersPage(Database database) : base(database) {} public async Task OnGet([FromRoute] int pageNumber, [FromQuery] string? name) diff --git a/ProjectLighthouse.Servers.Website/Program.cs b/ProjectLighthouse.Servers.Website/Program.cs new file mode 100644 index 00000000..21901602 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Program.cs @@ -0,0 +1,38 @@ +#nullable enable +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Logging.Loggers.AspNet; +using LBPUnion.ProjectLighthouse.Servers.Website.Startup; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace LBPUnion.ProjectLighthouse.Servers.Website; + +public static class Program +{ + public static void Main(string[] args) + { + StartupTasks.Run(args, ServerType.Website); + + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + => Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults + ( + webBuilder => + { + webBuilder.UseStartup(); + webBuilder.UseWebRoot("StaticFiles"); + webBuilder.UseUrls(ServerConfiguration.Instance.WebsiteListenUrl); + } + ) + .ConfigureLogging + ( + logging => + { + logging.ClearProviders(); + logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + } + ); +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/ProjectLighthouse.Servers.Website.csproj b/ProjectLighthouse.Servers.Website/ProjectLighthouse.Servers.Website.csproj new file mode 100644 index 00000000..86907bb9 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/ProjectLighthouse.Servers.Website.csproj @@ -0,0 +1,46 @@ + + + + net6.0 + enable + enable + LBPUnion.ProjectLighthouse.Servers.Website + LBPUnion.ProjectLighthouse.Servers.Website + false + + + + + + + + + + + Always + + + + Always + + + + Always + + + + Always + + + + + + + + + + + + + + diff --git a/ProjectLighthouse.Servers.Website/Startup/WebsiteStartup.cs b/ProjectLighthouse.Servers.Website/Startup/WebsiteStartup.cs new file mode 100644 index 00000000..dabe84c2 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Startup/WebsiteStartup.cs @@ -0,0 +1,66 @@ +using LBPUnion.ProjectLighthouse.Middlewares; +using Microsoft.AspNetCore.HttpOverrides; + +#if !DEBUG +using Microsoft.Extensions.Hosting.Internal; +#else +using LBPUnion.ProjectLighthouse.Startup; +#endif + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Startup; + +public class WebsiteStartup +{ + public WebsiteStartup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + #if DEBUG + services.AddRazorPages().WithRazorPagesAtContentRoot().AddRazorRuntimeCompilation(); + #else + services.AddRazorPages().WithRazorPagesAtContentRoot(); + #endif + + services.AddDbContext(); + + services.Configure + ( + options => + { + options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + } + ); + + #if DEBUG + services.AddSingleton(); + #else + services.AddSingleton(); + #endif + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + #if DEBUG + app.UseDeveloperExceptionPage(); + #endif + + app.UseForwardedHeaders(); + + app.UseMiddleware(); + + app.UseRouting(); + + app.UseStaticFiles(); + + app.UseEndpoints(endpoints => endpoints.MapControllers()); + app.UseEndpoints(endpoints => endpoints.MapRazorPages()); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Startup/WebsiteTestStartup.cs b/ProjectLighthouse.Servers.Website/Startup/WebsiteTestStartup.cs new file mode 100644 index 00000000..d33eda15 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Startup/WebsiteTestStartup.cs @@ -0,0 +1,15 @@ +using LBPUnion.ProjectLighthouse.Middlewares; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Startup; + +public class WebsiteTestStartup : WebsiteStartup +{ + public WebsiteTestStartup(IConfiguration configuration) : base(configuration) + {} + + public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseMiddleware(); + base.Configure(app, env); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/appsettings.Development.json b/ProjectLighthouse.Servers.Website/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/ProjectLighthouse.Servers.Website/appsettings.json b/ProjectLighthouse.Servers.Website/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/ProjectLighthouse.Servers.Website/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/ProjectLighthouse.Tests.GameApiTests/ProjectLighthouse.Tests.GameApiTests.csproj b/ProjectLighthouse.Tests.GameApiTests/ProjectLighthouse.Tests.GameApiTests.csproj index 38e1303a..086efd76 100644 --- a/ProjectLighthouse.Tests.GameApiTests/ProjectLighthouse.Tests.GameApiTests.csproj +++ b/ProjectLighthouse.Tests.GameApiTests/ProjectLighthouse.Tests.GameApiTests.csproj @@ -8,15 +8,15 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -27,15 +27,7 @@ - - + + - - - - PreserveNewest - - - - diff --git a/ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs b/ProjectLighthouse.Tests.GameApiTests/Tests/AuthenticationTests.cs similarity index 80% rename from ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs rename to ProjectLighthouse.Tests.GameApiTests/Tests/AuthenticationTests.cs index 68a10d13..acfbfb6b 100644 --- a/ProjectLighthouse.Tests.GameApiTests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/Tests/AuthenticationTests.cs @@ -1,12 +1,13 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Tests; -using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests; +namespace ProjectLighthouse.Tests.GameApiTests.Tests; public class AuthenticationTests : LighthouseServerTest { @@ -25,7 +26,7 @@ public class AuthenticationTests : LighthouseServerTest Assert.True(response.IsSuccessStatusCode); string responseContent = await response.Content.ReadAsStringAsync(); Assert.Contains("MM_AUTH=", responseContent); - Assert.Contains(ServerStatics.ServerName, responseContent); + Assert.Contains(VersionHelper.FullVersion, responseContent); } [DatabaseFact] @@ -35,10 +36,10 @@ public class AuthenticationTests : LighthouseServerTest Assert.NotNull(loginResult); Assert.NotNull(loginResult.AuthTicket); - Assert.NotNull(loginResult.LbpEnvVer); + Assert.NotNull(loginResult.ServerBrand); Assert.Contains("MM_AUTH=", loginResult.AuthTicket); - Assert.Equal(ServerStatics.ServerName, loginResult.LbpEnvVer); + Assert.Equal(VersionHelper.FullVersion, loginResult.ServerBrand); } [DatabaseFact] @@ -47,7 +48,7 @@ public class AuthenticationTests : LighthouseServerTest LoginResult loginResult = await this.Authenticate(); HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/enterLevel/420", loginResult.AuthTicket); - string responseContent = await response.Content.ReadAsStringAsync(); + await response.Content.ReadAsStringAsync(); Assert.False(response.StatusCode == HttpStatusCode.Forbidden); } @@ -59,4 +60,4 @@ public class AuthenticationTests : LighthouseServerTest Assert.False(response.IsSuccessStatusCode); Assert.True(response.StatusCode == HttpStatusCode.Forbidden); } -} +} \ No newline at end of file diff --git a/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs b/ProjectLighthouse.Tests.GameApiTests/Tests/DatabaseTests.cs similarity index 78% rename from ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs rename to ProjectLighthouse.Tests.GameApiTests/Tests/DatabaseTests.cs index 33da6dcb..ef3fd8dd 100644 --- a/ProjectLighthouse.Tests.GameApiTests/DatabaseTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/Tests/DatabaseTests.cs @@ -2,11 +2,11 @@ using System; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Tests; -using LBPUnion.ProjectLighthouse.Types; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests; +namespace ProjectLighthouse.Tests.GameApiTests.Tests; public class DatabaseTests : LighthouseServerTest { @@ -16,8 +16,8 @@ public class DatabaseTests : LighthouseServerTest await using Database database = new(); int rand = new Random().Next(); - User userA = await database.CreateUser("unitTestUser" + rand, HashHelper.GenerateAuthToken()); - User userB = await database.CreateUser("unitTestUser" + rand, HashHelper.GenerateAuthToken()); + User userA = await database.CreateUser("unitTestUser" + rand, CryptoHelper.GenerateAuthToken()); + User userB = await database.CreateUser("unitTestUser" + rand, CryptoHelper.GenerateAuthToken()); Assert.NotNull(userA); Assert.NotNull(userB); diff --git a/ProjectLighthouse.Tests.GameApiTests/MatchTests.cs b/ProjectLighthouse.Tests.GameApiTests/Tests/MatchTests.cs similarity index 95% rename from ProjectLighthouse.Tests.GameApiTests/MatchTests.cs rename to ProjectLighthouse.Tests.GameApiTests/Tests/MatchTests.cs index d5ac8e69..4bcf3324 100644 --- a/ProjectLighthouse.Tests.GameApiTests/MatchTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/Tests/MatchTests.cs @@ -4,11 +4,11 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Tests; -using LBPUnion.ProjectLighthouse.Types; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests; +namespace ProjectLighthouse.Tests.GameApiTests.Tests; public class MatchTests : LighthouseServerTest { diff --git a/ProjectLighthouse.Tests.GameApiTests/SlotTests.cs b/ProjectLighthouse.Tests.GameApiTests/Tests/SlotTests.cs similarity index 90% rename from ProjectLighthouse.Tests.GameApiTests/SlotTests.cs rename to ProjectLighthouse.Tests.GameApiTests/Tests/SlotTests.cs index dcd4779d..f9d96442 100644 --- a/ProjectLighthouse.Tests.GameApiTests/SlotTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/Tests/SlotTests.cs @@ -3,13 +3,13 @@ using System.Net.Http; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Tests; -using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Profiles; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests; +namespace ProjectLighthouse.Tests.GameApiTests.Tests; public class SlotTests : LighthouseServerTest { @@ -20,8 +20,8 @@ public class SlotTests : LighthouseServerTest Random r = new(); - User userA = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken()); - User userB = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken()); + User userA = await database.CreateUser($"unitTestUser{r.Next()}", CryptoHelper.GenerateAuthToken()); + User userB = await database.CreateUser($"unitTestUser{r.Next()}", CryptoHelper.GenerateAuthToken()); Location l = new() { diff --git a/ProjectLighthouse.Tests.GameApiTests/UploadTests.cs b/ProjectLighthouse.Tests.GameApiTests/Tests/UploadTests.cs similarity index 96% rename from ProjectLighthouse.Tests.GameApiTests/UploadTests.cs rename to ProjectLighthouse.Tests.GameApiTests/Tests/UploadTests.cs index 7ac1f72c..ddfa5043 100644 --- a/ProjectLighthouse.Tests.GameApiTests/UploadTests.cs +++ b/ProjectLighthouse.Tests.GameApiTests/Tests/UploadTests.cs @@ -3,11 +3,11 @@ using System.IO; using System.Net; using System.Net.Http; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Tests; -using LBPUnion.ProjectLighthouse.Types; using Xunit; -namespace ProjectLighthouse.Tests.GameApiTests; +namespace ProjectLighthouse.Tests.GameApiTests.Tests; public class UploadTests : LighthouseServerTest { diff --git a/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj b/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj index e72ef6ae..4483c810 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj +++ b/ProjectLighthouse.Tests.WebsiteTests/ProjectLighthouse.Tests.WebsiteTests.csproj @@ -8,17 +8,17 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -29,8 +29,8 @@ - - + + diff --git a/ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs b/ProjectLighthouse.Tests.WebsiteTests/Tests/AdminTests.cs similarity index 81% rename from ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs rename to ProjectLighthouse.Tests.WebsiteTests/Tests/AdminTests.cs index f76fb47d..9c137086 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/AdminTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/Tests/AdminTests.cs @@ -2,12 +2,13 @@ using System; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Tests; -using LBPUnion.ProjectLighthouse.Types; using OpenQA.Selenium; using Xunit; -namespace ProjectLighthouse.Tests.WebsiteTests; +namespace ProjectLighthouse.Tests.WebsiteTests.Tests; public class AdminTests : LighthouseWebTest { @@ -18,12 +19,12 @@ public class AdminTests : LighthouseWebTest { await using Database database = new(); Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure")); WebToken webToken = new() { UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), + UserToken = CryptoHelper.GenerateAuthToken(), }; database.WebTokens.Add(webToken); @@ -42,12 +43,12 @@ public class AdminTests : LighthouseWebTest { await using Database database = new(); Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure")); WebToken webToken = new() { UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), + UserToken = CryptoHelper.GenerateAuthToken(), }; database.WebTokens.Add(webToken); diff --git a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs b/ProjectLighthouse.Tests.WebsiteTests/Tests/AuthenticationTests.cs similarity index 84% rename from ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs rename to ProjectLighthouse.Tests.WebsiteTests/Tests/AuthenticationTests.cs index 1b482075..35a20bb4 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/Tests/AuthenticationTests.cs @@ -3,13 +3,14 @@ using System.Linq; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Tests; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.EntityFrameworkCore; using OpenQA.Selenium; using Xunit; -namespace ProjectLighthouse.Tests.WebsiteTests; +namespace ProjectLighthouse.Tests.WebsiteTests.Tests; public class AuthenticationTests : LighthouseWebTest { @@ -19,8 +20,8 @@ public class AuthenticationTests : LighthouseWebTest await using Database database = new(); Random random = new(); - string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash(HashHelper.Sha256Hash(password))); + string password = CryptoHelper.Sha256Hash(CryptoHelper.GenerateRandomBytes(64).ToArray()); + User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash(CryptoHelper.Sha256Hash(password))); this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); @@ -40,7 +41,7 @@ public class AuthenticationTests : LighthouseWebTest { await using Database database = new(); Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("just like the hindenberg,")); + User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("just like the hindenberg,")); this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); @@ -59,7 +60,7 @@ public class AuthenticationTests : LighthouseWebTest { await using Database database = new(); Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure")); this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login"); @@ -81,12 +82,12 @@ public class AuthenticationTests : LighthouseWebTest await using Database database = new(); Random random = new(); - User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure")); + User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure")); WebToken webToken = new() { UserId = user.UserId, - UserToken = HashHelper.GenerateAuthToken(), + UserToken = CryptoHelper.GenerateAuthToken(), }; database.WebTokens.Add(webToken); diff --git a/ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs b/ProjectLighthouse.Tests.WebsiteTests/Tests/LighthouseWebTest.cs similarity index 88% rename from ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs rename to ProjectLighthouse.Tests.WebsiteTests/Tests/LighthouseWebTest.cs index 874ea3d0..59a464fc 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/LighthouseWebTest.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/Tests/LighthouseWebTest.cs @@ -1,13 +1,13 @@ using System; using System.Linq; -using LBPUnion.ProjectLighthouse.Startup; +using LBPUnion.ProjectLighthouse.Servers.Website.Startup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server.Features; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; using Xunit; -namespace ProjectLighthouse.Tests.WebsiteTests; +namespace ProjectLighthouse.Tests.WebsiteTests.Tests; [Collection(nameof(LighthouseWebTest))] public class LighthouseWebTest : IDisposable @@ -15,7 +15,7 @@ public class LighthouseWebTest : IDisposable public readonly string BaseAddress; public readonly IWebDriver Driver; - public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup().UseWebRoot("StaticFiles").Build(); + public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup().UseWebRoot("StaticFiles").Build(); public LighthouseWebTest() { diff --git a/ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs b/ProjectLighthouse.Tests.WebsiteTests/Tests/RegisterTests.cs similarity index 83% rename from ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs rename to ProjectLighthouse.Tests.WebsiteTests/Tests/RegisterTests.cs index 5be2d31d..7624ca83 100644 --- a/ProjectLighthouse.Tests.WebsiteTests/RegisterTests.cs +++ b/ProjectLighthouse.Tests.WebsiteTests/Tests/RegisterTests.cs @@ -3,13 +3,13 @@ using System.Linq; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Tests; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.EntityFrameworkCore; using OpenQA.Selenium; using Xunit; -namespace ProjectLighthouse.Tests.WebsiteTests; +namespace ProjectLighthouse.Tests.WebsiteTests.Tests; public class RegisterTests : LighthouseWebTest { @@ -19,7 +19,7 @@ public class RegisterTests : LighthouseWebTest await using Database database = new(); string username = ("unitTestUser" + new Random().Next()).Substring(0, 16); - string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + string password = CryptoHelper.Sha256Hash(CryptoHelper.GenerateRandomBytes(64).ToArray()); this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); @@ -42,7 +42,7 @@ public class RegisterTests : LighthouseWebTest await using Database database = new(); string username = ("unitTestUser" + new Random().Next()).Substring(0, 16); - string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + string password = CryptoHelper.Sha256Hash(CryptoHelper.GenerateRandomBytes(64).ToArray()); this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register"); @@ -63,9 +63,9 @@ public class RegisterTests : LighthouseWebTest await using Database database = new(); string username = ("unitTestUser" + new Random().Next()).Substring(0, 16); - string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray()); + string password = CryptoHelper.Sha256Hash(CryptoHelper.GenerateRandomBytes(64).ToArray()); - await database.CreateUser(username, HashHelper.BCryptHash(password)); + await database.CreateUser(username, CryptoHelper.BCryptHash(password)); User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username); Assert.NotNull(user); diff --git a/ProjectLighthouse.Tests/DatabaseFactAttribute.cs b/ProjectLighthouse.Tests/DatabaseFactAttribute.cs index e9e177f8..5af29a56 100644 --- a/ProjectLighthouse.Tests/DatabaseFactAttribute.cs +++ b/ProjectLighthouse.Tests/DatabaseFactAttribute.cs @@ -1,4 +1,4 @@ -using LBPUnion.ProjectLighthouse.Types.Settings; +using LBPUnion.ProjectLighthouse.Configuration; using Microsoft.EntityFrameworkCore; using Xunit; @@ -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.Tests/LighthouseServerTest.cs b/ProjectLighthouse.Tests/LighthouseServerTest.cs index 02aa19fd..bd18d63d 100644 --- a/ProjectLighthouse.Tests/LighthouseServerTest.cs +++ b/ProjectLighthouse.Tests/LighthouseServerTest.cs @@ -5,8 +5,9 @@ using System.Net.Http; using System.Threading.Tasks; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Startup; +using LBPUnion.ProjectLighthouse.Servers.GameServer.Startup; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; @@ -22,7 +23,7 @@ public class LighthouseServerTest public LighthouseServerTest() { - this.Server = new TestServer(new WebHostBuilder().UseStartup()); + this.Server = new TestServer(new WebHostBuilder().UseStartup()); this.Client = this.Server.CreateClient(); } public async Task AuthenticateResponse(int number = -1, bool createUser = true) @@ -34,7 +35,7 @@ public class LighthouseServerTest { await using Database database = new(); if (await database.Users.FirstOrDefaultAsync(u => u.Username == $"{username}{number}") == null) - await database.CreateUser($"{username}{number}", HashHelper.BCryptHash($"unitTestPassword{number}")); + await database.CreateUser($"{username}{number}", CryptoHelper.BCryptHash($"unitTestPassword{number}")); } //TODO: generate actual tickets @@ -68,7 +69,7 @@ public class LighthouseServerTest public async Task UploadFileEndpointRequest(string filePath) { byte[] bytes = await File.ReadAllBytesAsync(filePath); - string hash = HashHelper.Sha1Hash(bytes).ToLower(); + string hash = CryptoHelper.Sha1Hash(bytes).ToLower(); return await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/upload/{hash}", new ByteArrayContent(bytes)); } @@ -76,7 +77,7 @@ public class LighthouseServerTest public async Task AuthenticatedUploadFileEndpointRequest(string filePath, string mmAuth) { byte[] bytes = await File.ReadAllBytesAsync(filePath); - string hash = HashHelper.Sha1Hash(bytes).ToLower(); + string hash = CryptoHelper.Sha1Hash(bytes).ToLower(); using HttpRequestMessage requestMessage = new(HttpMethod.Post, $"/LITTLEBIGPLANETPS3_XML/upload/{hash}"); requestMessage.Headers.Add("Cookie", mmAuth); requestMessage.Content = new ByteArrayContent(bytes); diff --git a/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj b/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj index 9e4a73e8..0f9fffbf 100644 --- a/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj +++ b/ProjectLighthouse.Tests/ProjectLighthouse.Tests.csproj @@ -13,15 +13,15 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -35,6 +35,9 @@ PreserveNewest - + + + + diff --git a/ProjectLighthouse.Tests/FileTypeTests.cs b/ProjectLighthouse.Tests/Tests/FileTypeTests.cs similarity index 97% rename from ProjectLighthouse.Tests/FileTypeTests.cs rename to ProjectLighthouse.Tests/Tests/FileTypeTests.cs index aa0452de..611ec632 100644 --- a/ProjectLighthouse.Tests/FileTypeTests.cs +++ b/ProjectLighthouse.Tests/Tests/FileTypeTests.cs @@ -1,7 +1,7 @@ using System; using System.IO; using System.Text; -using LBPUnion.ProjectLighthouse.Types.Files; +using LBPUnion.ProjectLighthouse.Files; using Xunit; namespace LBPUnion.ProjectLighthouse.Tests; diff --git a/ProjectLighthouse.Tests/SerializerTests.cs b/ProjectLighthouse.Tests/Tests/SerializerTests.cs similarity index 100% rename from ProjectLighthouse.Tests/SerializerTests.cs rename to ProjectLighthouse.Tests/Tests/SerializerTests.cs diff --git a/ProjectLighthouse.sln b/ProjectLighthouse.sln index fde4d678..4f163398 100644 --- a/ProjectLighthouse.sln +++ b/ProjectLighthouse.sln @@ -10,7 +10,17 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Tests.Gam EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Tests.WebsiteTests", "ProjectLighthouse.Tests.WebsiteTests\ProjectLighthouse.Tests.WebsiteTests.csproj", "{CF65EB5B-5364-4D2A-8639-F147A67F08E7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Localization", "ProjectLighthouse.Localization\ProjectLighthouse.Localization.csproj", "{CDB81465-F758-4842-B843-F77ABEE7C3BF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Servers.API", "ProjectLighthouse.Servers.API\ProjectLighthouse.Servers.API.csproj", "{5593825E-F5C9-467F-9125-3E3249CFEEAB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Servers.GameServer", "ProjectLighthouse.Servers.GameServer\ProjectLighthouse.Servers.GameServer.csproj", "{0CD7F64B-7827-4AC9-B7D8-CE371D505544}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Servers.Website", "ProjectLighthouse.Servers.Website\ProjectLighthouse.Servers.Website.csproj", "{FA9AEA06-D6B5-4E68-8370-DB9188108635}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Servers", "Servers", "{1DE7A758-1F4F-4BA5-BE1C-74F9D0AB9EA3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source Code", "Source Code", "{7805B410-9260-4907-A7C6-D739369B2F25}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectLighthouse.Localization", "ProjectLighthouse.Localization\ProjectLighthouse.Localization.csproj", "{18B76DAC-5DCB-44EA-B74D-0B4554BB161C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -31,14 +41,32 @@ Global {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Debug|Any CPU.Build.0 = Debug|Any CPU {CF65EB5B-5364-4D2A-8639-F147A67F08E7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CDB81465-F758-4842-B843-F77ABEE7C3BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CDB81465-F758-4842-B843-F77ABEE7C3BF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CDB81465-F758-4842-B843-F77ABEE7C3BF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CDB81465-F758-4842-B843-F77ABEE7C3BF}.Release|Any CPU.Build.0 = Release|Any CPU + {5593825E-F5C9-467F-9125-3E3249CFEEAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5593825E-F5C9-467F-9125-3E3249CFEEAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5593825E-F5C9-467F-9125-3E3249CFEEAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5593825E-F5C9-467F-9125-3E3249CFEEAB}.Release|Any CPU.Build.0 = Release|Any CPU + {0CD7F64B-7827-4AC9-B7D8-CE371D505544}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CD7F64B-7827-4AC9-B7D8-CE371D505544}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CD7F64B-7827-4AC9-B7D8-CE371D505544}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CD7F64B-7827-4AC9-B7D8-CE371D505544}.Release|Any CPU.Build.0 = Release|Any CPU + {FA9AEA06-D6B5-4E68-8370-DB9188108635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA9AEA06-D6B5-4E68-8370-DB9188108635}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA9AEA06-D6B5-4E68-8370-DB9188108635}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA9AEA06-D6B5-4E68-8370-DB9188108635}.Release|Any CPU.Build.0 = Release|Any CPU + {18B76DAC-5DCB-44EA-B74D-0B4554BB161C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18B76DAC-5DCB-44EA-B74D-0B4554BB161C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18B76DAC-5DCB-44EA-B74D-0B4554BB161C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18B76DAC-5DCB-44EA-B74D-0B4554BB161C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {AFC74569-B289-4ACC-B21C-313A3A62C017} = {D360C08E-EA47-43AC-A566-FDF413442980} {200EED99-FE3E-45C6-A51E-76ED9819CA2B} = {D360C08E-EA47-43AC-A566-FDF413442980} {CF65EB5B-5364-4D2A-8639-F147A67F08E7} = {D360C08E-EA47-43AC-A566-FDF413442980} + {5593825E-F5C9-467F-9125-3E3249CFEEAB} = {1DE7A758-1F4F-4BA5-BE1C-74F9D0AB9EA3} + {0CD7F64B-7827-4AC9-B7D8-CE371D505544} = {1DE7A758-1F4F-4BA5-BE1C-74F9D0AB9EA3} + {FA9AEA06-D6B5-4E68-8370-DB9188108635} = {1DE7A758-1F4F-4BA5-BE1C-74F9D0AB9EA3} + {1DE7A758-1F4F-4BA5-BE1C-74F9D0AB9EA3} = {7805B410-9260-4907-A7C6-D739369B2F25} + {C6CFD4AD-47ED-4C86-B0C4-A4216D82E0DC} = {7805B410-9260-4907-A7C6-D739369B2F25} + {18B76DAC-5DCB-44EA-B74D-0B4554BB161C} = {7805B410-9260-4907-A7C6-D739369B2F25} EndGlobalSection EndGlobal diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings index 5d18923a..524940d2 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" /> @@ -105,12 +107,15 @@ True True True + True + True True True True True True True + True True True True @@ -125,7 +130,14 @@ True True True + True + True True + True + True + True + True + True True True True @@ -145,5 +157,8 @@ True True True + True + True + True True True \ No newline at end of file diff --git a/ProjectLighthouse/Types/AdminPanelStatistic.cs b/ProjectLighthouse/Administration/AdminPanelStatistic.cs similarity index 88% rename from ProjectLighthouse/Types/AdminPanelStatistic.cs rename to ProjectLighthouse/Administration/AdminPanelStatistic.cs index eaa668a7..15830b3f 100644 --- a/ProjectLighthouse/Types/AdminPanelStatistic.cs +++ b/ProjectLighthouse/Administration/AdminPanelStatistic.cs @@ -1,6 +1,6 @@ #nullable enable -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.Administration; public struct AdminPanelStatistic { diff --git a/ProjectLighthouse/Administration/CompletedMigration.cs b/ProjectLighthouse/Administration/CompletedMigration.cs new file mode 100644 index 00000000..3ddd3b25 --- /dev/null +++ b/ProjectLighthouse/Administration/CompletedMigration.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel.DataAnnotations; +using LBPUnion.ProjectLighthouse.Administration.Maintenance; + +namespace LBPUnion.ProjectLighthouse.Administration; + +/// +/// A record of the completion of a . +/// +public class CompletedMigration +{ + /// + /// The name of the migration. + /// + /// + /// Do not use the user-friendly name when setting this. + /// + [Key] + public string MigrationName { get; set; } + + /// + /// The moment the migration was ran. + /// + public DateTime RanAt { get; set; } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/DeletedBy.cs b/ProjectLighthouse/Administration/DeletedBy.cs similarity index 84% rename from ProjectLighthouse/Types/DeletedBy.cs rename to ProjectLighthouse/Administration/DeletedBy.cs index c0e57733..a5e1ca36 100644 --- a/ProjectLighthouse/Types/DeletedBy.cs +++ b/ProjectLighthouse/Administration/DeletedBy.cs @@ -1,6 +1,6 @@ using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.Administration; [XmlRoot("deleted_by")] public enum DeletedBy diff --git a/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/CreateUserCommand.cs similarity index 53% rename from ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs rename to ProjectLighthouse/Administration/Maintenance/Commands/CreateUserCommand.cs index 43f479df..39b86e04 100644 --- a/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/CreateUserCommand.cs @@ -1,41 +1,38 @@ #nullable enable using System.Threading.Tasks; -using JetBrains.Annotations; -using Kettu; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; -[UsedImplicitly] public class CreateUserCommand : ICommand { private readonly Database _database = new(); - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { string onlineId = args[0]; string password = args[1]; - password = HashHelper.Sha256Hash(password); + password = CryptoHelper.Sha256Hash(password); User? user = await this._database.Users.FirstOrDefaultAsync(u => u.Username == onlineId); if (user == null) { - user = await this._database.CreateUser(onlineId, HashHelper.BCryptHash(password)); - Logger.Log($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LoggerLevelLogin.Instance); + user = await this._database.CreateUser(onlineId, CryptoHelper.BCryptHash(password)); + logger.LogSuccess($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LogArea.Command); user.PasswordResetRequired = true; - Logger.Log("This user will need to reset their password when they log in.", LoggerLevelLogin.Instance); + logger.LogInfo("This user will need to reset their password when they log in.", LogArea.Command); await this._database.SaveChangesAsync(); - Logger.Log("Database changes saved.", LoggerLevelDatabase.Instance); + logger.LogInfo("Database changes saved.", LogArea.Command); } else { - Logger.Log("A user with this username already exists.", LoggerLevelLogin.Instance); + logger.LogError("A user with this username already exists.", LogArea.Command); } } diff --git a/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/DeleteUserCommand.cs similarity index 73% rename from ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs rename to ProjectLighthouse/Administration/Maintenance/Commands/DeleteUserCommand.cs index fc7c522f..cb783dcc 100644 --- a/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/DeleteUserCommand.cs @@ -1,13 +1,12 @@ #nullable enable using System; using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; -[UsedImplicitly] public class DeleteUserCommand : ICommand { private readonly Database database = new(); @@ -19,7 +18,7 @@ public class DeleteUserCommand : ICommand }; public string Arguments() => ""; public int RequiredArgs() => 1; - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) @@ -30,7 +29,7 @@ public class DeleteUserCommand : ICommand } catch { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + logger.LogError($"Could not find user by parameter '{args[0]}'", LogArea.Command); return; } diff --git a/ProjectLighthouse/Administration/Maintenance/Commands/FlushRedisCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/FlushRedisCommand.cs new file mode 100644 index 00000000..85fde257 --- /dev/null +++ b/ProjectLighthouse/Administration/Maintenance/Commands/FlushRedisCommand.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.StorableLists; + +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; + +public class FlushRedisCommand : ICommand +{ + public string Name() => "Flush Redis"; + public string[] Aliases() => new[] { + "flush", "flush-redis", + }; + public string Arguments() => ""; + public int RequiredArgs() => 0; + + public async Task Run(string[] args, Logger logger) + { + await RedisDatabase.FlushAll(); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/MakeUserAdminCommand.cs similarity index 68% rename from ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs rename to ProjectLighthouse/Administration/Maintenance/Commands/MakeUserAdminCommand.cs index ec526e1d..0cc0f85f 100644 --- a/ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/MakeUserAdminCommand.cs @@ -1,13 +1,12 @@ #nullable enable using System; using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; -[UsedImplicitly] public class MakeUserAdminCommand : ICommand { private readonly Database database = new(); @@ -21,7 +20,7 @@ public class MakeUserAdminCommand : ICommand public string Arguments() => ""; public int RequiredArgs() => 1; - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) @@ -32,13 +31,13 @@ public class MakeUserAdminCommand : ICommand } catch { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + logger.LogError($"Could not find user by parameter '{args[0]}'", LogArea.Command); return; } user.IsAdmin = true; await this.database.SaveChangesAsync(); - Console.WriteLine($"The user {user.Username} (id: {user.UserId}) is now an admin."); + logger.LogSuccess($"The user {user.Username} (id: {user.UserId}) is now an admin.", LogArea.Command); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/Commands/RenameUserCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/RenameUserCommand.cs similarity index 67% rename from ProjectLighthouse/Maintenance/Commands/RenameUserCommand.cs rename to ProjectLighthouse/Administration/Maintenance/Commands/RenameUserCommand.cs index 6ba41f52..3995e023 100644 --- a/ProjectLighthouse/Maintenance/Commands/RenameUserCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/RenameUserCommand.cs @@ -1,15 +1,16 @@ #nullable enable using System; using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; public class RenameUserCommand : ICommand { private readonly Database database = new(); - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) @@ -20,14 +21,14 @@ public class RenameUserCommand : ICommand } catch { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + logger.LogError($"Could not find user by parameter '{args[0]}'", LogArea.Command); return; } user.Username = args[1]; await this.database.SaveChangesAsync(); - Console.WriteLine($"The username for user {user.Username} (id: {user.UserId}) has been changed."); + logger.LogSuccess($"The username for user {user.Username} (id: {user.UserId}) has been changed.", LogArea.Command); } public string Name() => "Rename User"; public string[] Aliases() diff --git a/ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/ResetPasswordCommand.cs similarity index 64% rename from ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs rename to ProjectLighthouse/Administration/Maintenance/Commands/ResetPasswordCommand.cs index ef922afe..0433f866 100644 --- a/ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/ResetPasswordCommand.cs @@ -1,14 +1,13 @@ #nullable enable using System; using System.Threading.Tasks; -using JetBrains.Annotations; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; -[UsedImplicitly] public class ResetPasswordCommand : ICommand { private readonly Database database = new(); @@ -21,7 +20,7 @@ public class ResetPasswordCommand : ICommand public string Arguments() => " "; public int RequiredArgs() => 2; - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) @@ -32,17 +31,18 @@ public class ResetPasswordCommand : ICommand } catch { - Console.WriteLine($"Could not find user by parameter '{args[0]}'"); + logger.LogError($"Could not find user by parameter '{args[0]}'", LogArea.Command); return; } + string password = args[1]; - if (password.Length != 64) password = HashHelper.Sha256Hash(password); + if (password.Length != 64) password = CryptoHelper.Sha256Hash(password); - user.Password = HashHelper.BCryptHash(password); + user.Password = CryptoHelper.BCryptHash(password); user.PasswordResetRequired = true; await this.database.SaveChangesAsync(); - Console.WriteLine($"The password for user {user.Username} (id: {user.UserId}) has been reset."); + logger.LogSuccess($"The password for user {user.Username} (id: {user.UserId}) has been reset.", LogArea.Command); } } \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/Commands/TestWebhookCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/TestWebhookCommand.cs similarity index 74% rename from ProjectLighthouse/Maintenance/Commands/TestWebhookCommand.cs rename to ProjectLighthouse/Administration/Maintenance/Commands/TestWebhookCommand.cs index 93ed05b5..c6c70bf4 100644 --- a/ProjectLighthouse/Maintenance/Commands/TestWebhookCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/TestWebhookCommand.cs @@ -1,13 +1,12 @@ using System.Threading.Tasks; -using JetBrains.Annotations; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Logging; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; -[UsedImplicitly] public class TestWebhookCommand : ICommand { - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { await WebhookHelper.SendWebhook("Testing 123", "Someone is testing the Discord webhook from the admin panel."); } diff --git a/ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs b/ProjectLighthouse/Administration/Maintenance/Commands/WipeTokensForUserCommand.cs similarity index 86% rename from ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs rename to ProjectLighthouse/Administration/Maintenance/Commands/WipeTokensForUserCommand.cs index 646963df..9517bd56 100644 --- a/ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs +++ b/ProjectLighthouse/Administration/Maintenance/Commands/WipeTokensForUserCommand.cs @@ -2,10 +2,11 @@ using System; using System.Linq; using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Maintenance.Commands; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.Commands; public class WipeTokensForUserCommand : ICommand { @@ -19,7 +20,7 @@ public class WipeTokensForUserCommand : ICommand }; public string Arguments() => ""; public int RequiredArgs() => 1; - public async Task Run(string[] args) + public async Task Run(string[] args, Logger logger) { User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]); if (user == null) diff --git a/ProjectLighthouse/Administration/Maintenance/ICommand.cs b/ProjectLighthouse/Administration/Maintenance/ICommand.cs new file mode 100644 index 00000000..42a3f39f --- /dev/null +++ b/ProjectLighthouse/Administration/Maintenance/ICommand.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Logging; + +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance; + +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] +public interface ICommand +{ + public string FirstAlias => this.Aliases()[0]; + public Task Run(string[] args, Logger logger); + + public string Name(); + + public string[] Aliases(); + + public string Arguments(); + + public int RequiredArgs(); +} \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/IMaintenanceJob.cs b/ProjectLighthouse/Administration/Maintenance/IMaintenanceJob.cs similarity index 50% rename from ProjectLighthouse/Maintenance/IMaintenanceJob.cs rename to ProjectLighthouse/Administration/Maintenance/IMaintenanceJob.cs index 545e7d33..15229ed1 100644 --- a/ProjectLighthouse/Maintenance/IMaintenanceJob.cs +++ b/ProjectLighthouse/Administration/Maintenance/IMaintenanceJob.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; +using JetBrains.Annotations; -namespace LBPUnion.ProjectLighthouse.Maintenance; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance; +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] public interface IMaintenanceJob { public Task Run(); diff --git a/ProjectLighthouse/Administration/Maintenance/IMigrationTask.cs b/ProjectLighthouse/Administration/Maintenance/IMigrationTask.cs new file mode 100644 index 00000000..cba570a3 --- /dev/null +++ b/ProjectLighthouse/Administration/Maintenance/IMigrationTask.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance; + +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] +public interface IMigrationTask +{ + /// + /// The user-friendly name of a migration. + /// + public string Name(); + + /// + /// Performs the migration. + /// + /// The Lighthouse database. + /// True if successful, false if not. + internal Task Run(Database database); +} \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs b/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs new file mode 100644 index 00000000..7d7490d8 --- /dev/null +++ b/ProjectLighthouse/Administration/Maintenance/MaintenanceHelper.cs @@ -0,0 +1,116 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Logging.Loggers; + +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance; + +public static class MaintenanceHelper +{ + static MaintenanceHelper() + { + Commands = getListOfInterfaceObjects(); + MaintenanceJobs = getListOfInterfaceObjects(); + MigrationTasks = getListOfInterfaceObjects(); + } + + public static List Commands { get; } + public static List MaintenanceJobs { get; } + public static List MigrationTasks { get; } + + public static async Task> RunCommand(string[] args) + { + if (args.Length < 1) + throw new Exception + ("This should never happen. " + "If it did, its because you tried to run a command before validating that the user actually wants to run one."); + + string baseCmd = args[0]; + args = args.Skip(1).ToArray(); + + // Setup memory logger for output + Logger logger = new(); + InMemoryLogger memoryLogger = new(); + logger.AddLogger(memoryLogger); + + IEnumerable suitableCommands = Commands.Where + (command => command.Aliases().Any(a => a.ToLower() == baseCmd.ToLower())) + .Where(command => args.Length >= command.RequiredArgs()); + foreach (ICommand command in suitableCommands) + { + logger.LogInfo("Running command " + command.Name(), LogArea.Command); + + await command.Run(args, logger); + logger.Flush(); + return memoryLogger.Lines; + } + + logger.LogError("Command not found.", LogArea.Command); + logger.Flush(); + return memoryLogger.Lines; + } + + public static async Task RunMaintenanceJob(string jobName) + { + IMaintenanceJob? job = MaintenanceJobs.FirstOrDefault(j => j.GetType().Name == jobName); + if (job == null) throw new ArgumentNullException(); + + await job.Run(); + } + + public static async Task RunMigration(IMigrationTask migrationTask, Database? database = null) + { + database ??= new Database(); + + // Migrations should never be run twice. + Debug.Assert(!await database.CompletedMigrations.Has(m => m.MigrationName == migrationTask.GetType().Name)); + + Logger.Info($"Running migration task {migrationTask.Name()}", LogArea.Database); + + bool success; + Exception? exception = null; + + try + { + success = await migrationTask.Run(database); + } + catch(Exception e) + { + success = false; + exception = e; + } + + if(!success) + { + Logger.Error($"Could not run migration {migrationTask.Name()}", LogArea.Database); + if (exception != null) Logger.Error(exception.ToDetailedException(), LogArea.Database); + + return; + } + + Logger.Success($"Successfully completed migration {migrationTask.Name()}", LogArea.Database); + + CompletedMigration completedMigration = new() + { + MigrationName = migrationTask.GetType().Name, + RanAt = DateTime.Now, + }; + + database.CompletedMigrations.Add(completedMigration); + await database.SaveChangesAsync(); + } + + private static List getListOfInterfaceObjects() where T : class + { + return Assembly.GetExecutingAssembly() + .GetTypes() + .Where(t => t.GetInterfaces().Contains(typeof(T)) && t.GetConstructor(Type.EmptyTypes) != null) + .Select(t => Activator.CreateInstance(t) as T) + .ToList()!; + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs similarity index 90% rename from ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs rename to ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs index 7af922b2..b8074025 100644 --- a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs +++ b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs @@ -3,11 +3,11 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Files; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Files; +using LBPUnion.ProjectLighthouse.PlayerData; -namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.MaintenanceJobs; public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob { @@ -25,7 +25,7 @@ public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob bool largeHashIsInvalidFile = false; bool tooManyPhotoSubjects = false; bool duplicatePhotoSubjects = false; - bool takenInTheFuture = true; + bool takenInTheFuture = false; // Checks should generally be ordered in least computationally expensive to most. @@ -35,7 +35,7 @@ public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob goto removePhoto; } - if (photo.Timestamp > TimestampHelper.Timestamp) + if (photo.Timestamp > TimeHelper.Timestamp) { takenInTheFuture = true; goto removePhoto; @@ -89,7 +89,8 @@ public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob $"{nameof(noHashesExist)}: {noHashesExist}, " + $"{nameof(largeHashIsInvalidFile)}: {largeHashIsInvalidFile}, " + $"{nameof(tooManyPhotoSubjects)}: {tooManyPhotoSubjects}" + - $"{nameof(duplicatePhotoSubjects)}: {duplicatePhotoSubjects}" + $"{nameof(duplicatePhotoSubjects)}: {duplicatePhotoSubjects}" + + $"{nameof(takenInTheFuture)}: {takenInTheFuture}" ); this.database.Photos.Remove(photo); diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs similarity index 87% rename from ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs rename to ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs index a9d6da95..96e9d523 100644 --- a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs +++ b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs @@ -2,9 +2,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Types.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.MaintenanceJobs; public class CleanupUnusedLocationsMaintenanceJob : IMaintenanceJob { diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs similarity index 87% rename from ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs rename to ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs index 4c6767ef..70b959de 100644 --- a/ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs +++ b/ProjectLighthouse/Administration/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; -namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs; +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.MaintenanceJobs; public class DeleteAllTokensMaintenanceJob : IMaintenanceJob { diff --git a/ProjectLighthouse/Administration/Maintenance/MigrationTasks/CleanupXMLInjectionMigration.cs b/ProjectLighthouse/Administration/Maintenance/MigrationTasks/CleanupXMLInjectionMigration.cs new file mode 100644 index 00000000..bb1b71e9 --- /dev/null +++ b/ProjectLighthouse/Administration/Maintenance/MigrationTasks/CleanupXMLInjectionMigration.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Helpers; + +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.MigrationTasks; + +public class CleanupXmlInjectionMigration : IMigrationTask +{ + public string Name() => "Cleanup XML injections"; + + // Weird, but required. Thanks, hejlsberg. + async Task IMigrationTask.Run(Database database) + { + List objsToBeSanitized = new(); + + // Store all the objects we need to sanitize in a list. + // The alternative here is to loop through every table, but thats a ton of code... + objsToBeSanitized.AddRange(database.Slots); + objsToBeSanitized.AddRange(database.Reviews); + objsToBeSanitized.AddRange(database.Comments); + objsToBeSanitized.AddRange(database.Scores); + objsToBeSanitized.AddRange(database.Users); + objsToBeSanitized.AddRange(database.Photos); + objsToBeSanitized.AddRange(database.Reports); + + foreach (object obj in objsToBeSanitized) SanitizationHelper.SanitizeStringsInClass(obj); + + await database.SaveChangesAsync(); + return true; + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Reports/GriefReport.cs b/ProjectLighthouse/Administration/Reports/GriefReport.cs similarity index 91% rename from ProjectLighthouse/Types/Reports/GriefReport.cs rename to ProjectLighthouse/Administration/Reports/GriefReport.cs index e0727b6a..a8e85c57 100644 --- a/ProjectLighthouse/Types/Reports/GriefReport.cs +++ b/ProjectLighthouse/Administration/Reports/GriefReport.cs @@ -1,8 +1,9 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -namespace LBPUnion.ProjectLighthouse.Types.Reports; +namespace LBPUnion.ProjectLighthouse.Administration.Reports; [XmlRoot("griefReport")] public class GriefReport diff --git a/ProjectLighthouse/Types/Reports/GriefType.cs b/ProjectLighthouse/Administration/Reports/GriefType.cs similarity index 85% rename from ProjectLighthouse/Types/Reports/GriefType.cs rename to ProjectLighthouse/Administration/Reports/GriefType.cs index ccce115b..e651eab0 100644 --- a/ProjectLighthouse/Types/Reports/GriefType.cs +++ b/ProjectLighthouse/Administration/Reports/GriefType.cs @@ -1,6 +1,6 @@ using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Reports; +namespace LBPUnion.ProjectLighthouse.Administration.Reports; public enum GriefType { diff --git a/ProjectLighthouse/Types/Reports/Marqee.cs b/ProjectLighthouse/Administration/Reports/Marqee.cs similarity index 70% rename from ProjectLighthouse/Types/Reports/Marqee.cs rename to ProjectLighthouse/Administration/Reports/Marqee.cs index 82e554cf..c5047516 100644 --- a/ProjectLighthouse/Types/Reports/Marqee.cs +++ b/ProjectLighthouse/Administration/Reports/Marqee.cs @@ -1,6 +1,6 @@ using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Reports; +namespace LBPUnion.ProjectLighthouse.Administration.Reports; [XmlRoot("marqee")] public class Marqee diff --git a/ProjectLighthouse/Types/Reports/Rectangle.cs b/ProjectLighthouse/Administration/Reports/Rectangle.cs similarity index 56% rename from ProjectLighthouse/Types/Reports/Rectangle.cs rename to ProjectLighthouse/Administration/Reports/Rectangle.cs index 24757bc0..c01631ad 100644 --- a/ProjectLighthouse/Types/Reports/Rectangle.cs +++ b/ProjectLighthouse/Administration/Reports/Rectangle.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Xml.Serialization; -using LBPUnion.ProjectLighthouse.Serialization; -using Newtonsoft.Json; +using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Reports; +namespace LBPUnion.ProjectLighthouse.Administration.Reports; public class Rectangle { diff --git a/ProjectLighthouse/Types/Reports/ReportPlayer.cs b/ProjectLighthouse/Administration/Reports/ReportPlayer.cs similarity index 87% rename from ProjectLighthouse/Types/Reports/ReportPlayer.cs rename to ProjectLighthouse/Administration/Reports/ReportPlayer.cs index 22c94a68..2526228e 100644 --- a/ProjectLighthouse/Types/Reports/ReportPlayer.cs +++ b/ProjectLighthouse/Administration/Reports/ReportPlayer.cs @@ -1,6 +1,6 @@ using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Reports; +namespace LBPUnion.ProjectLighthouse.Administration.Reports; [XmlRoot("player")] public class ReportPlayer diff --git a/ProjectLighthouse/Types/Reports/VisiblePlayer.cs b/ProjectLighthouse/Administration/Reports/VisiblePlayer.cs similarity index 86% rename from ProjectLighthouse/Types/Reports/VisiblePlayer.cs rename to ProjectLighthouse/Administration/Reports/VisiblePlayer.cs index 78507bb1..4fc523e1 100644 --- a/ProjectLighthouse/Types/Reports/VisiblePlayer.cs +++ b/ProjectLighthouse/Administration/Reports/VisiblePlayer.cs @@ -1,7 +1,7 @@ using System; using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Reports; +namespace LBPUnion.ProjectLighthouse.Administration.Reports; [XmlRoot("visibleBadge")] [Serializable] diff --git a/ProjectLighthouse/Types/ApiEndpointController.cs b/ProjectLighthouse/ApiEndpointController.cs similarity index 77% rename from ProjectLighthouse/Types/ApiEndpointController.cs rename to ProjectLighthouse/ApiEndpointController.cs index 7a717cab..c42889cb 100644 --- a/ProjectLighthouse/Types/ApiEndpointController.cs +++ b/ProjectLighthouse/ApiEndpointController.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse; [ApiController] [Route("/api/v1")] diff --git a/ProjectLighthouse/Configuration/ConfigurationCategories/AuthenticationConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/AuthenticationConfiguration.cs new file mode 100644 index 00000000..c1f75473 --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/AuthenticationConfiguration.cs @@ -0,0 +1,12 @@ +using System; + +namespace LBPUnion.ProjectLighthouse.Configuration.ConfigurationCategories; + +public class AuthenticationConfiguration +{ + [Obsolete("Obsolete. This feature has been removed.", true)] + public bool BlockDeniedUsers { get; set; } + + public bool RegistrationEnabled { get; set; } = true; + public bool UseExternalAuth { get; set; } +} \ No newline at end of file diff --git a/ProjectLighthouse/Configuration/ConfigurationCategories/CaptchaConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/CaptchaConfiguration.cs new file mode 100644 index 00000000..fad16ae6 --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/CaptchaConfiguration.cs @@ -0,0 +1,13 @@ +namespace LBPUnion.ProjectLighthouse.Configuration.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/Configuration/ConfigurationCategories/CustomizationConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/CustomizationConfiguration.cs new file mode 100644 index 00000000..027c1753 --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/CustomizationConfiguration.cs @@ -0,0 +1,6 @@ +namespace LBPUnion.ProjectLighthouse.Configuration.ConfigurationCategories; + +public class CustomizationConfiguration +{ + public string ServerName { get; set; } = "Project Lighthouse"; +} \ No newline at end of file diff --git a/ProjectLighthouse/Configuration/ConfigurationCategories/DigestKeyConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/DigestKeyConfiguration.cs new file mode 100644 index 00000000..006a2236 --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/DigestKeyConfiguration.cs @@ -0,0 +1,8 @@ +namespace LBPUnion.ProjectLighthouse.Configuration.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/Configuration/ConfigurationCategories/DiscordIntegrationConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/DiscordIntegrationConfiguration.cs new file mode 100644 index 00000000..92246899 --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/DiscordIntegrationConfiguration.cs @@ -0,0 +1,11 @@ +#nullable enable +namespace LBPUnion.ProjectLighthouse.Configuration.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/Configuration/ConfigurationCategories/GoogleAnalyticsConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/GoogleAnalyticsConfiguration.cs new file mode 100644 index 00000000..0cc543de --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/GoogleAnalyticsConfiguration.cs @@ -0,0 +1,8 @@ +namespace LBPUnion.ProjectLighthouse.Configuration.ConfigurationCategories; + +public class GoogleAnalyticsConfiguration +{ + public bool AnalyticsEnabled { get; set; } + + public string Id { get; set; } = ""; +} \ No newline at end of file diff --git a/ProjectLighthouse/Configuration/ConfigurationCategories/InfluxDBConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/InfluxDBConfiguration.cs new file mode 100644 index 00000000..c3aad3f2 --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/InfluxDBConfiguration.cs @@ -0,0 +1,16 @@ +namespace LBPUnion.ProjectLighthouse.Configuration.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/Configuration/ConfigurationCategories/MailConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/MailConfiguration.cs new file mode 100644 index 00000000..9bef477b --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/MailConfiguration.cs @@ -0,0 +1,18 @@ +namespace LBPUnion.ProjectLighthouse.Configuration.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/Configuration/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs new file mode 100644 index 00000000..6430b754 --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/UserGeneratedContentLimitConfiguration.cs @@ -0,0 +1,21 @@ +namespace LBPUnion.ProjectLighthouse.Configuration.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/Configuration/ConfigurationCategories/WebsiteConfiguration.cs b/ProjectLighthouse/Configuration/ConfigurationCategories/WebsiteConfiguration.cs new file mode 100644 index 00000000..7a31aa5a --- /dev/null +++ b/ProjectLighthouse/Configuration/ConfigurationCategories/WebsiteConfiguration.cs @@ -0,0 +1,8 @@ +namespace LBPUnion.ProjectLighthouse.Configuration.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/Configuration/Legacy/LegacyServerSettings.cs b/ProjectLighthouse/Configuration/Legacy/LegacyServerSettings.cs new file mode 100644 index 00000000..123fcf63 --- /dev/null +++ b/ProjectLighthouse/Configuration/Legacy/LegacyServerSettings.cs @@ -0,0 +1,214 @@ +using System.IO; +using System.Text.Json; +using LBPUnion.ProjectLighthouse.Configuration.ConfigurationCategories; +using LBPUnion.ProjectLighthouse.Types; + +namespace LBPUnion.ProjectLighthouse.Configuration.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 = this.InfluxUrl, + }; + + configuration.Authentication = new AuthenticationConfiguration + { + RegistrationEnabled = this.RegistrationEnabled, + 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/PrivacySettings.cs b/ProjectLighthouse/Configuration/PrivacySettings.cs similarity index 89% rename from ProjectLighthouse/Types/Settings/PrivacySettings.cs rename to ProjectLighthouse/Configuration/PrivacySettings.cs index b8c2e637..1bd96e1c 100644 --- a/ProjectLighthouse/Types/Settings/PrivacySettings.cs +++ b/ProjectLighthouse/Configuration/PrivacySettings.cs @@ -1,6 +1,6 @@ using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Settings; +namespace LBPUnion.ProjectLighthouse.Configuration; public class PrivacySettings { diff --git a/ProjectLighthouse/Configuration/ServerConfiguration.cs b/ProjectLighthouse/Configuration/ServerConfiguration.cs new file mode 100644 index 00000000..bd3ce250 --- /dev/null +++ b/ProjectLighthouse/Configuration/ServerConfiguration.cs @@ -0,0 +1,200 @@ +#nullable enable +using System; +using System.Diagnostics; +using System.IO; +using LBPUnion.ProjectLighthouse.Configuration.ConfigurationCategories; +using LBPUnion.ProjectLighthouse.Configuration.Legacy; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Types; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace LBPUnion.ProjectLighthouse.Configuration; + +[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 = 5; + + #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.Info("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.Info($"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.Warn("This version of Project Lighthouse now uses YML instead of JSON to store configuration.", LogArea.Config); + Logger.Warn + ("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.Info($"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.Success("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.Warn + ( + "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.Info("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.Info("Configuration file modified, reloading config...", LogArea.Config); + Logger.Warn("Some changes may not apply; they will require a restart of Lighthouse.", LogArea.Config); + + ServerConfiguration? configuration = fromFile(ConfigFileName); + if (configuration == null) + { + Logger.Warn("The new configuration was unable to be loaded for some reason. The old config has been kept.", LogArea.Config); + return; + } + + Instance = configuration; + + Logger.Success("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 + + // TODO: Find a way to properly remove config options + // YamlDotNet hates that and it's fucking annoying. + // This seriously sucks. /rant + [Obsolete("Obsolete. Use the Website/GameApi/Api listen URLS instead.")] + public string ListenUrl { get; set; } = "http://localhost:10060"; + + public string WebsiteListenUrl { get; set; } = "http://localhost:10060"; + public string GameApiListenUrl { get; set; } = "http://localhost:10061"; + public string ApiListenUrl { get; set; } = "http://localhost:10062"; + + public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; + public string RedisConnectionString { get; set; } = "redis://localhost:6379"; + 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(); + public CustomizationConfiguration Customization { get; set; } = new(); +} \ No newline at end of file diff --git a/ProjectLighthouse/Configuration/ServerStatics.cs b/ProjectLighthouse/Configuration/ServerStatics.cs new file mode 100644 index 00000000..8e87417d --- /dev/null +++ b/ProjectLighthouse/Configuration/ServerStatics.cs @@ -0,0 +1,41 @@ +#nullable enable +using System; +using System.Linq; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Types; + +namespace LBPUnion.ProjectLighthouse.Configuration; + +public static class ServerStatics +{ + public const int PageSize = 20; + + public static bool DbConnected { + get { + try + { + return new Database().Database.CanConnect(); + } + catch(Exception e) + { + Logger.Error(e.ToString(), LogArea.Database); + return false; + } + } + } + + // FIXME: This needs to go at some point. + public static bool IsUnitTesting => AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName!.StartsWith("xunit")); + + #if DEBUG + public const bool IsDebug = true; + #else + public const bool IsDebug = false; + #endif + + /// + /// The servertype, determined on startup. Shouldn't be null unless very very early in startup. + /// + // The way of doing this is kinda weird, but it works. + public static ServerType ServerType; +} \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs b/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs deleted file mode 100644 index 13f2a4d5..00000000 --- a/ProjectLighthouse/Controllers/Website/Admin/AdminUserController.cs +++ /dev/null @@ -1,36 +0,0 @@ -#nullable enable -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Types; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin; - -[ApiController] -[Route("admin/user/{id:int}")] -public class AdminUserController : ControllerBase -{ - private readonly Database database; - - public AdminUserController(Database database) - { - this.database = database; - } - - [HttpGet("unban")] - public async Task UnbanUser([FromRoute] int id) - { - User? user = this.database.UserFromWebRequest(this.Request); - if (user == null || !user.IsAdmin) return this.NotFound(); - - User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); - ; - if (targetedUser == null) return this.NotFound(); - - targetedUser.Banned = false; - targetedUser.BannedReason = null; - - await this.database.SaveChangesAsync(); - return this.Redirect($"/user/{targetedUser.UserId}"); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 23998cd7..eca4a621 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -3,16 +3,18 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Administration; +using LBPUnion.ProjectLighthouse.Administration.Reports; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.Levels.Categories; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email; +using LBPUnion.ProjectLighthouse.PlayerData.Reviews; +using LBPUnion.ProjectLighthouse.Tickets; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Categories; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Profiles; -using LBPUnion.ProjectLighthouse.Types.Profiles.Email; -using LBPUnion.ProjectLighthouse.Types.Reports; -using LBPUnion.ProjectLighthouse.Types.Reviews; -using LBPUnion.ProjectLighthouse.Types.Settings; -using LBPUnion.ProjectLighthouse.Types.Tickets; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; @@ -20,6 +22,7 @@ namespace LBPUnion.ProjectLighthouse; public class Database : DbContext { + public DbSet CompletedMigrations { get; set; } public DbSet Users { get; set; } public DbSet Locations { get; set; } public DbSet Slots { get; set; } @@ -46,7 +49,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 +85,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); } @@ -100,7 +103,7 @@ public class Database : DbContext GameToken gameToken = new() { - UserToken = HashHelper.GenerateAuthToken(), + UserToken = CryptoHelper.GenerateAuthToken(), User = user, UserId = user.UserId, UserLocation = userLocation, diff --git a/ProjectLighthouse/Extensions/AspLogLevelExtensions.cs b/ProjectLighthouse/Extensions/AspLogLevelExtensions.cs new file mode 100644 index 00000000..18b2b3d4 --- /dev/null +++ b/ProjectLighthouse/Extensions/AspLogLevelExtensions.cs @@ -0,0 +1,22 @@ +using LBPUnion.ProjectLighthouse.Logging; +using AspLogLevel = Microsoft.Extensions.Logging.LogLevel; + +namespace LBPUnion.ProjectLighthouse.Extensions; + +public static class AspLogLevelExtensions +{ + public static LogLevel ToLighthouseLevel(this AspLogLevel level) + { + return level switch + { + AspLogLevel.Trace => LogLevel.Debug, + AspLogLevel.Debug => LogLevel.Debug, + AspLogLevel.Information => LogLevel.Info, + AspLogLevel.Warning => LogLevel.Warning, + AspLogLevel.Error => LogLevel.Error, + AspLogLevel.Critical => LogLevel.Error, + AspLogLevel.None => LogLevel.Info, + _ => LogLevel.Info, + }; + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/Extensions/BinaryReaderExtensions.cs b/ProjectLighthouse/Extensions/BinaryReaderExtensions.cs similarity index 96% rename from ProjectLighthouse/Helpers/Extensions/BinaryReaderExtensions.cs rename to ProjectLighthouse/Extensions/BinaryReaderExtensions.cs index 4a83bf42..ef6f045c 100644 --- a/ProjectLighthouse/Helpers/Extensions/BinaryReaderExtensions.cs +++ b/ProjectLighthouse/Extensions/BinaryReaderExtensions.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace LBPUnion.ProjectLighthouse.Helpers.Extensions; +namespace LBPUnion.ProjectLighthouse.Extensions; public static class BinaryReaderExtensions { diff --git a/ProjectLighthouse/Helpers/Extensions/DatabaseExtensions.cs b/ProjectLighthouse/Extensions/DatabaseExtensions.cs similarity index 81% rename from ProjectLighthouse/Helpers/Extensions/DatabaseExtensions.cs rename to ProjectLighthouse/Extensions/DatabaseExtensions.cs index 5b656aac..a7e27782 100644 --- a/ProjectLighthouse/Helpers/Extensions/DatabaseExtensions.cs +++ b/ProjectLighthouse/Extensions/DatabaseExtensions.cs @@ -1,10 +1,14 @@ +using System; using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Reviews; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Reviews; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Helpers.Extensions; +namespace LBPUnion.ProjectLighthouse.Extensions; public static class DatabaseExtensions { @@ -51,4 +55,7 @@ public static class DatabaseExtensions return query; } + + public static async Task Has(this IQueryable queryable, Expression> predicate) => + await queryable.FirstOrDefaultAsync(predicate) != null; } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs b/ProjectLighthouse/Extensions/ExceptionExtensions.cs similarity index 92% rename from ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs rename to ProjectLighthouse/Extensions/ExceptionExtensions.cs index 5b6e4623..14e2783c 100644 --- a/ProjectLighthouse/Helpers/Extensions/ExceptionExtensions.cs +++ b/ProjectLighthouse/Extensions/ExceptionExtensions.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -namespace LBPUnion.ProjectLighthouse.Helpers.Extensions; +namespace LBPUnion.ProjectLighthouse.Extensions; // https://stackoverflow.com/a/8039737 public static class ExceptionExtensions diff --git a/ProjectLighthouse/Extensions/LogLevelExtensions.cs b/ProjectLighthouse/Extensions/LogLevelExtensions.cs new file mode 100644 index 00000000..83464502 --- /dev/null +++ b/ProjectLighthouse/Extensions/LogLevelExtensions.cs @@ -0,0 +1,18 @@ +using System; +using LBPUnion.ProjectLighthouse.Logging; + +namespace LBPUnion.ProjectLighthouse.Extensions; + +public static class LogLevelExtensions +{ + public static ConsoleColor ToColor(this LogLevel level) + => level switch + { + LogLevel.Debug => ConsoleColor.Magenta, + LogLevel.Error => ConsoleColor.Red, + LogLevel.Warning => ConsoleColor.Yellow, + LogLevel.Info => ConsoleColor.White, + LogLevel.Success => ConsoleColor.Green, + _ => ConsoleColor.White, + }; +} \ No newline at end of file diff --git a/ProjectLighthouse/Extensions/LogLineListExtensions.cs b/ProjectLighthouse/Extensions/LogLineListExtensions.cs new file mode 100644 index 00000000..e198f0bc --- /dev/null +++ b/ProjectLighthouse/Extensions/LogLineListExtensions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Linq; +using LBPUnion.ProjectLighthouse.Logging; + +namespace LBPUnion.ProjectLighthouse.Extensions; + +public static class LogLineListExtensions +{ + public static string ToLogString(this IEnumerable log) => log.Aggregate( + "", + (current, logLine) => current + $"[{logLine.Level}] [{logLine.Trace.Name}:{logLine.Trace.Section}] {logLine.Message}\n" + ); +} \ No newline at end of file diff --git a/ProjectLighthouse/Extensions/RedisCollectionExtensions.cs b/ProjectLighthouse/Extensions/RedisCollectionExtensions.cs new file mode 100644 index 00000000..1b98743e --- /dev/null +++ b/ProjectLighthouse/Extensions/RedisCollectionExtensions.cs @@ -0,0 +1,11 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Redis.OM.Searching; + +namespace LBPUnion.ProjectLighthouse.Extensions; + +[SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")] +public static class RedisCollectionExtensions +{ + +} \ No newline at end of file diff --git a/ProjectLighthouse/Extensions/RedisConnectionExtensions.cs b/ProjectLighthouse/Extensions/RedisConnectionExtensions.cs new file mode 100644 index 00000000..8380d00c --- /dev/null +++ b/ProjectLighthouse/Extensions/RedisConnectionExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Logging; +using Redis.OM; +using Redis.OM.Contracts; + +namespace LBPUnion.ProjectLighthouse.Extensions; + +public static class RedisConnectionExtensions +{ + public static async Task RecreateIndexAsync(this IRedisConnection connection, Type type) + { + Logger.Debug("Recreating index for " + type.Name, LogArea.Redis); + + // TODO: use `await connection.DropIndexAndAssociatedRecordsAsync(type);` here instead when that becomes a thing + bool dropped = await connection.DropIndexAsync(type); + Logger.Debug("Dropped index: " + dropped, LogArea.Redis); + + bool created = await connection.CreateIndexAsync(type); + Logger.Debug("Created index: " + created, LogArea.Redis); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Extensions/RequestExtensions.cs b/ProjectLighthouse/Extensions/RequestExtensions.cs new file mode 100644 index 00000000..d375246c --- /dev/null +++ b/ProjectLighthouse/Extensions/RequestExtensions.cs @@ -0,0 +1,77 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Configuration; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json.Linq; + +namespace LBPUnion.ProjectLighthouse.Extensions; + +public static class RequestExtensions +{ + + #region Mobile Checking + + // yoinked and adapted from https://stackoverflow.com/a/68641796 + + private static readonly Regex mobileCheck = new + ( + "Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|PlayStation Vita", + RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled + ); + + public static bool IsMobile(this HttpRequest request) => mobileCheck.IsMatch(request.Headers[HeaderNames.UserAgent].ToString()); + + #endregion + + #region Captcha + + private static readonly HttpClient client = new() + { + BaseAddress = new Uri("https://hcaptcha.com"), + }; + + [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeNotEvident")] + private static async Task verifyCaptcha(string token) + { + if (!ServerConfiguration.Instance.Captcha.CaptchaEnabled) return true; + + List> payload = new() + { + new("secret", ServerConfiguration.Instance.Captcha.Secret), + new("response", token), + }; + + HttpResponseMessage response = await client.PostAsync("/siteverify", new FormUrlEncodedContent(payload)); + + response.EnsureSuccessStatusCode(); + + string responseBody = await response.Content.ReadAsStringAsync(); + + // We only really care about the success result, nothing else that hcaptcha sends us, so lets only parse that. + bool success = bool.Parse(JObject.Parse(responseBody)["success"]?.ToString() ?? "false"); + return success; + } + + public static async Task CheckCaptchaValidity(this HttpRequest request) + { + if (ServerConfiguration.Instance.Captcha.CaptchaEnabled) + { + bool gotCaptcha = request.Form.TryGetValue("h-captcha-response", out StringValues values); + if (!gotCaptcha) return false; + + if (!await verifyCaptcha(values[0])) return false; + } + + return true; + } + + #endregion + +} \ No newline at end of file diff --git a/ProjectLighthouse/Extensions/RoomExtensions.cs b/ProjectLighthouse/Extensions/RoomExtensions.cs new file mode 100644 index 00000000..df75cea0 --- /dev/null +++ b/ProjectLighthouse/Extensions/RoomExtensions.cs @@ -0,0 +1,26 @@ +#nullable enable +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using LBPUnion.ProjectLighthouse.Match.Rooms; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Types; + +namespace LBPUnion.ProjectLighthouse.Extensions; + +public static class RoomExtensions +{ + public static List GetPlayers(this Room room, Database database) + { + List players = new(); + foreach (int playerId in room.PlayerIds) + { + User? player = database.Users.FirstOrDefault(p => p.UserId == playerId); + Debug.Assert(player != null); + + players.Add(player); + } + + return players; + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Files/FileHelper.cs similarity index 51% rename from ProjectLighthouse/Helpers/FileHelper.cs rename to ProjectLighthouse/Files/FileHelper.cs index ede1d308..18a542fc 100644 --- a/ProjectLighthouse/Helpers/FileHelper.cs +++ b/ProjectLighthouse/Files/FileHelper.cs @@ -6,12 +6,15 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -using Kettu; +using DDSReader; +using ICSharpCode.SharpZipLib.Zip.Compression; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types.Files; -using LBPUnion.ProjectLighthouse.Types.Settings; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; -namespace LBPUnion.ProjectLighthouse.Helpers; +namespace LBPUnion.ProjectLighthouse.Files; public static class FileHelper { @@ -21,7 +24,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; @@ -57,7 +60,7 @@ public static class FileHelper // Determine if file is a FARC (File Archive). // Needs to be done before anything else that determines the type by the header // because this determines the type by the footer. - string footer = Encoding.ASCII.GetString(BinaryHelper.ReadLastBytes(reader, 4)); + string footer = Encoding.ASCII.GetString(readLastBytes(reader, 4)); if (footer == "FARC") return LbpFileType.FileArchive; byte[] header = reader.ReadBytes(3); @@ -76,6 +79,19 @@ public static class FileHelper }; } + private static byte[] readLastBytes(BinaryReader reader, int count, bool restoreOldPosition = true) + { + long oldPosition = reader.BaseStream.Position; + + if (reader.BaseStream.Length < count) return Array.Empty(); + + reader.BaseStream.Position = reader.BaseStream.Length - count; + byte[] data = reader.ReadBytes(count); + + if (restoreOldPosition) reader.BaseStream.Position = oldPosition; + return data; + } + private static LbpFileType readAlternateHeader(BinaryReader reader) { reader.BaseStream.Position = 0; @@ -115,8 +131,7 @@ public static class FileHelper EnsureDirectoryCreated(Path.Combine(Environment.CurrentDirectory, "png")); if (Directory.Exists("r")) { - Logger.Log - ("Converting all textures to PNG. This may take a while if this is the first time running this operation...", LoggerLevelStartup.Instance); + Logger.Info("Converting all textures to PNG. This may take a while if this is the first time running this operation...", LogArea.Startup); ConcurrentQueue fileQueue = new(); @@ -135,7 +150,7 @@ public static class FileHelper if (file.FileType == LbpFileType.Jpeg || file.FileType == LbpFileType.Png || file.FileType == LbpFileType.Texture) { - ImageHelper.LbpFileToPNG(file); + LbpFileToPNG(file); } } } @@ -149,4 +164,111 @@ public static class FileHelper } } + #region Images + + public static bool LbpFileToPNG(LbpFile file) => LbpFileToPNG(file.Data, file.Hash, file.FileType); + + public static bool LbpFileToPNG(byte[] data, string hash, LbpFileType type) + { + if (type != LbpFileType.Jpeg && type != LbpFileType.Png && type != LbpFileType.Texture) return false; + + if (File.Exists(Path.Combine("png", $"{hash}.png"))) return true; + + using MemoryStream ms = new(data); + using BinaryReader reader = new(ms); + + try + { + return type switch + { + LbpFileType.Texture => TextureToPNG(hash, reader), + LbpFileType.Png => PNGToPNG(hash, data), + LbpFileType.Jpeg => JPGToPNG(hash, data), + // ReSharper disable once UnreachableSwitchArmDueToIntegerAnalysis + _ => false, + }; + } + catch(Exception e) + { + Console.WriteLine($"Error while converting {hash}:"); + Console.WriteLine(e); + return false; + } + } + + private static bool TextureToPNG(string hash, BinaryReader reader) + { + // Skip the magic (3 bytes), we already know its a texture + for(int i = 0; i < 3; i++) reader.ReadByte(); + + // This below is shamelessly stolen from ennuo's Toolkit: https://github.com/ennuo/toolkit/blob/d996ee4134740db0ee94e2cbf1e4edbd1b5ec798/src/main/java/ennuo/craftworld/utilities/Compressor.java#L40 + + // This byte determines the method of reading. We can only read a texture (' ') so if it's not ' ' it must be invalid. + if ((char)reader.ReadByte() != ' ') return false; + + reader.ReadInt16(); // ? + short chunks = reader.ReadInt16BE(); + + int[] compressed = new int[chunks]; + int[] decompressed = new int[chunks]; + + for(int i = 0; i < chunks; ++i) + { + compressed[i] = reader.ReadUInt16BE(); + decompressed[i] = reader.ReadUInt16BE(); + } + + using MemoryStream ms = new(); + using BinaryWriter writer = new(ms); + for(int i = 0; i < chunks; ++i) + { + byte[] deflatedData = reader.ReadBytes(compressed[i]); + if (compressed[i] == decompressed[i]) + { + writer.Write(deflatedData); + } + + Inflater inflater = new(); + inflater.SetInput(deflatedData); + byte[] inflatedData = new byte[decompressed[i]]; + inflater.Inflate(inflatedData); + + writer.Write(inflatedData); + } + + return DDSToPNG(hash, ms.ToArray()); + } + + private static bool DDSToPNG(string hash, byte[] data) + { + using MemoryStream stream = new(); + DDSImage image = new(data); + + image.SaveAsPng(stream); + + Directory.CreateDirectory("png"); + File.WriteAllBytes($"png/{hash}.png", stream.ToArray()); + return true; + } + + private static bool JPGToPNG(string hash, byte[] data) + { + using Image image = Image.Load(data); + using MemoryStream ms = new(); + image.SaveAsPng(ms); + + File.WriteAllBytes($"png/{hash}.png", ms.ToArray()); + return true; + } + + // it sounds dumb i know but hear me out: + // you're completely correct + private static bool PNGToPNG(string hash, byte[] data) + { + File.WriteAllBytes($"png/{hash}.png", data); + return true; + } + + #endregion + } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Files/LbpFile.cs b/ProjectLighthouse/Files/LbpFile.cs similarity index 76% rename from ProjectLighthouse/Types/Files/LbpFile.cs rename to ProjectLighthouse/Files/LbpFile.cs index b3cad5e3..e36e1b2c 100644 --- a/ProjectLighthouse/Types/Files/LbpFile.cs +++ b/ProjectLighthouse/Files/LbpFile.cs @@ -2,7 +2,7 @@ using System.IO; using LBPUnion.ProjectLighthouse.Helpers; -namespace LBPUnion.ProjectLighthouse.Types.Files; +namespace LBPUnion.ProjectLighthouse.Files; public class LbpFile { @@ -19,12 +19,12 @@ public class LbpFile public readonly string Hash; - public LbpFile(byte[] data, string? hash = null) + public LbpFile(byte[] data) { this.Data = data; this.FileType = FileHelper.DetermineFileType(this.Data); - this.Hash = HashHelper.Sha1Hash(this.Data).ToLower(); + this.Hash = CryptoHelper.Sha1Hash(this.Data).ToLower(); } public static LbpFile? FromHash(string hash) @@ -34,6 +34,6 @@ public class LbpFile byte[] data = File.ReadAllBytes(path); - return new LbpFile(data, hash); + return new LbpFile(data); } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Files/LbpFileType.cs b/ProjectLighthouse/Files/LbpFileType.cs similarity index 89% rename from ProjectLighthouse/Types/Files/LbpFileType.cs rename to ProjectLighthouse/Files/LbpFileType.cs index 6005bc80..31043379 100644 --- a/ProjectLighthouse/Types/Files/LbpFileType.cs +++ b/ProjectLighthouse/Files/LbpFileType.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Files; +namespace LBPUnion.ProjectLighthouse.Files; public enum LbpFileType { diff --git a/ProjectLighthouse/Helpers/BinaryHelper.cs b/ProjectLighthouse/Helpers/BinaryHelper.cs deleted file mode 100644 index 7d242e60..00000000 --- a/ProjectLighthouse/Helpers/BinaryHelper.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.IO.Pipelines; -using System.Text; -using System.Threading.Tasks; - -namespace LBPUnion.ProjectLighthouse.Helpers; - -public static class BinaryHelper -{ - public static string ReadString(BinaryReader reader) - { - List readBytes = new(); - - byte readByte; - do readBytes.Add(readByte = reader.ReadByte()); - while (readByte != 0x00); - - return Encoding.UTF8.GetString(readBytes.ToArray()); - } - - public static void ReadUntilByte(BinaryReader reader, byte byteToReadTo) - { - byte readByte; - do readByte = reader.ReadByte(); - while (readByte != byteToReadTo); - } - - public static byte[] ReadToEnd(BinaryReader reader) => reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); - - public static byte[] ReadLastBytes(BinaryReader reader, int count, bool restoreOldPosition = true) - { - long oldPosition = reader.BaseStream.Position; - - if (reader.BaseStream.Length < count) return Array.Empty(); - - reader.BaseStream.Position = reader.BaseStream.Length - count; - byte[] data = reader.ReadBytes(count); - - if (restoreOldPosition) reader.BaseStream.Position = oldPosition; - return data; - } - - // Written with reference from - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-response?view=aspnetcore-5.0 - // Surprisingly doesn't take seconds. (67ms for a 100kb file) - public static async Task ReadFromPipeReader(PipeReader reader) - { - List data = new(); - while (true) - { - ReadResult readResult = await reader.ReadAsync(); - ReadOnlySequence buffer = readResult.Buffer; - - if (readResult.IsCompleted && buffer.Length > 0) data.AddRange(buffer.ToArray()); - - reader.AdvanceTo(buffer.Start, buffer.End); - - if (readResult.IsCompleted) break; - } - - return data.ToArray(); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/CaptchaHelper.cs b/ProjectLighthouse/Helpers/CaptchaHelper.cs deleted file mode 100644 index 3008b084..00000000 --- a/ProjectLighthouse/Helpers/CaptchaHelper.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Newtonsoft.Json.Linq; - -namespace LBPUnion.ProjectLighthouse.Helpers; - -public static class CaptchaHelper -{ - private static readonly HttpClient client = new() - { - BaseAddress = new Uri("https://hcaptcha.com"), - }; - - public static async Task Verify(string token) - { - if (!ServerSettings.Instance.HCaptchaEnabled) return true; - - List> payload = new() - { - new("secret", ServerSettings.Instance.HCaptchaSecret), - new("response", token), - }; - - HttpResponseMessage response = await client.PostAsync("/siteverify", new FormUrlEncodedContent(payload)); - - response.EnsureSuccessStatusCode(); - - string responseBody = await response.Content.ReadAsStringAsync(); - - // We only really care about the success result, nothing else that hcaptcha sends us, so lets only parse that. - bool success = bool.Parse(JObject.Parse(responseBody)["success"]?.ToString() ?? "false"); - return success; - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/CensorHelper.cs b/ProjectLighthouse/Helpers/CensorHelper.cs index a78e9184..85d3db91 100644 --- a/ProjectLighthouse/Helpers/CensorHelper.cs +++ b/ProjectLighthouse/Helpers/CensorHelper.cs @@ -1,7 +1,7 @@ using System; using System.Text; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; namespace LBPUnion.ProjectLighthouse.Helpers; @@ -17,11 +17,11 @@ public static class CensorHelper "UwU", "OwO", "uwu", "owo", "o3o", ">.>", "*pounces on you*", "*boops*", "*baps*", ":P", "x3", "O_O", "xD", ":3", ";3", "^w^", }; - private static readonly string[] censorList = ResourceHelper.readManifestFile("chatCensoredList.txt").Replace("\r", "").Split("\n"); + private static readonly string[] censorList = ResourceHelper.ReadManifestFile("chatCensoredList.txt").Replace("\r", "").Split("\n"); 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,11 +44,11 @@ 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++) - lock(RandomHelper.random) + lock(CryptoHelper.Random) { if (message[i] == ' ') { @@ -56,8 +56,8 @@ public static class CensorHelper } else { - char randomChar = randomCharacters[RandomHelper.random.Next(0, randomCharacters.Length - 1)]; - if (randomChar == prevRandomChar) randomChar = randomCharacters[RandomHelper.random.Next(0, randomCharacters.Length - 1)]; + char randomChar = randomCharacters[CryptoHelper.Random.Next(0, randomCharacters.Length - 1)]; + if (randomChar == prevRandomChar) randomChar = randomCharacters[CryptoHelper.Random.Next(0, randomCharacters.Length - 1)]; prevRandomChar = randomChar; @@ -81,9 +81,9 @@ public static class CensorHelper break; case FilterMode.Furry: - lock(RandomHelper.random) + lock(CryptoHelper.Random) { - string randomWord = randomFurry[RandomHelper.random.Next(0, randomFurry.Length - 1)]; + string randomWord = randomFurry[CryptoHelper.Random.Next(0, randomFurry.Length - 1)]; sb.Append(randomWord); } diff --git a/ProjectLighthouse/Helpers/HashHelper.cs b/ProjectLighthouse/Helpers/CryptoHelper.cs similarity index 79% rename from ProjectLighthouse/Helpers/HashHelper.cs rename to ProjectLighthouse/Helpers/CryptoHelper.cs index 3cfe3a2a..aef06419 100644 --- a/ProjectLighthouse/Helpers/HashHelper.cs +++ b/ProjectLighthouse/Helpers/CryptoHelper.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Runtime.Intrinsics.Arm; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; @@ -10,30 +9,18 @@ using System.Threading.Tasks; namespace LBPUnion.ProjectLighthouse.Helpers; [SuppressMessage("ReSharper", "UnusedMember.Global")] -public static class HashHelper +public static class CryptoHelper { + /// + /// An instance of Random. Must be locked when in use. + /// + public static readonly Random Random = new(); + // private static readonly SHA1 sha1 = SHA1.Create(); private static readonly SHA256 sha256 = SHA256.Create(); /// - /// Generates a specified amount of random bytes in an array. - /// - /// The amount of bytes to generate. - /// The bytes generated - public static IEnumerable GenerateRandomBytes(int count) - { - byte[] b = new byte[count]; - - lock (RandomHelper.random) - { - RandomHelper.random.NextBytes(b); - } - - return b; - } - - /// - /// Generates a random SHA256 & BCrypted token + /// Generates a random SHA256 and BCrypted token /// /// The token as a string. public static string GenerateAuthToken() @@ -67,6 +54,35 @@ public static class HashHelper return digestString; } + /// + /// Generates a specified amount of random bytes in an array. + /// + /// The amount of bytes to generate. + /// The bytes generated + public static IEnumerable GenerateRandomBytes(int count) + { + byte[] b = new byte[count]; + + lock(Random) + { + Random.NextBytes(b); + } + + return b; + } + + public static string ToBase64(string str) + { + byte[] bytes = Encoding.UTF8.GetBytes(str); + return Convert.ToBase64String(bytes); + } + + public static string FromBase64(string base64) + { + byte[] bytes = Convert.FromBase64String(base64); + return Encoding.UTF8.GetString(bytes); + } + #region Hash Functions public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str)); @@ -75,12 +91,11 @@ public static class HashHelper public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str)); - public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(SHA1.Create().ComputeHash(bytes)).Replace("-",""); + public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(SHA1.Create().ComputeHash(bytes)).Replace("-", ""); public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str); public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes)); #endregion - } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs b/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs deleted file mode 100644 index 3b805fa9..00000000 --- a/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; - -namespace LBPUnion.ProjectLighthouse.Helpers; - -public static class DeniedAuthenticationHelper -{ - public static readonly Dictionary IPAddressAndNameDeniedAt = new(); - public static readonly Dictionary AttemptsByIPAddressAndName = new(); - - public static void SetDeniedAt(string ipAddressAndName, long timestamp = 0) - { - if (timestamp == 0) timestamp = TimestampHelper.Timestamp; - - if (IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long _)) IPAddressAndNameDeniedAt.Remove(ipAddressAndName); - IPAddressAndNameDeniedAt.Add(ipAddressAndName, timestamp); - } - - public static bool RecentlyDenied(string ipAddressAndName) - { - if (!IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long timestamp)) return false; - - return TimestampHelper.Timestamp < timestamp + 300; - } - - public static void AddAttempt(string ipAddressAndName) - { - if (AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) AttemptsByIPAddressAndName.Remove(ipAddressAndName); - AttemptsByIPAddressAndName.Add(ipAddressAndName, attempts + 1); - } - - public static int GetAttempts(string ipAddressAndName) - { - if (!AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) return 0; - - return attempts; - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/EulaHelper.cs b/ProjectLighthouse/Helpers/EulaHelper.cs deleted file mode 100644 index 3d9398c7..00000000 --- a/ProjectLighthouse/Helpers/EulaHelper.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace LBPUnion.ProjectLighthouse.Helpers; - -public static class EulaHelper -{ - public const string License = @" -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see ."; -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs b/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs deleted file mode 100644 index 5f8c81d9..00000000 --- a/ProjectLighthouse/Helpers/Extensions/RequestExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -#nullable enable -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Primitives; -using Microsoft.Net.Http.Headers; - -namespace LBPUnion.ProjectLighthouse.Helpers.Extensions; - -public static class RequestExtensions -{ - // yoinked and adapted from https://stackoverflow.com/a/68641796 - - #region Mobile Checking - - private static readonly Regex mobileCheck = new - ( - "Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|PlayStation Vita", - RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled - ); - - public static bool IsMobile(this HttpRequest request) => mobileCheck.IsMatch(request.Headers[HeaderNames.UserAgent].ToString()); - - #endregion - - public static async Task CheckCaptchaValidity(this HttpRequest request) - { - if (ServerSettings.Instance.HCaptchaEnabled) - { - bool gotCaptcha = request.Form.TryGetValue("h-captcha-response", out StringValues values); - if (!gotCaptcha) return false; - - if (!await CaptchaHelper.Verify(values[0])) return false; - } - - return true; - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs b/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs deleted file mode 100644 index fe2564ac..00000000 --- a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.IO; -using System.Linq; - -namespace LBPUnion.ProjectLighthouse.Helpers.Extensions; - -public static class StringExtensions -{ - public static string ToFileName(this string text) => Path.GetInvalidFileNameChars().Aggregate(text, (current, c) => current.Replace(c.ToString(), "")); -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/FriendHelper.cs b/ProjectLighthouse/Helpers/FriendHelper.cs deleted file mode 100644 index e5f0bf43..00000000 --- a/ProjectLighthouse/Helpers/FriendHelper.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Diagnostics.CodeAnalysis; - -namespace LBPUnion.ProjectLighthouse.Helpers; - -[NotMapped] -[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")] -public static class FriendHelper -{ - public static readonly Dictionary FriendIdsByUserId = new(); - public static readonly Dictionary BlockedIdsByUserId = new(); -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/GameVersionHelper.cs b/ProjectLighthouse/Helpers/GameVersionHelper.cs index c31adb18..a33cd43d 100644 --- a/ProjectLighthouse/Helpers/GameVersionHelper.cs +++ b/ProjectLighthouse/Helpers/GameVersionHelper.cs @@ -1,4 +1,5 @@ using System.Linq; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Types; namespace LBPUnion.ProjectLighthouse.Helpers; diff --git a/ProjectLighthouse/Helpers/ImageHelper.cs b/ProjectLighthouse/Helpers/ImageHelper.cs deleted file mode 100644 index 7801af3b..00000000 --- a/ProjectLighthouse/Helpers/ImageHelper.cs +++ /dev/null @@ -1,122 +0,0 @@ -#nullable enable -using System; -using System.IO; -using DDSReader; -using ICSharpCode.SharpZipLib.Zip.Compression; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; -using LBPUnion.ProjectLighthouse.Types.Files; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; - -namespace LBPUnion.ProjectLighthouse.Helpers; - -public static class ImageHelper -{ - public static bool LbpFileToPNG(LbpFile file) => LbpFileToPNG(file.Data, file.Hash, file.FileType); - - public static bool LbpFileToPNG(byte[] data, string hash, LbpFileType type) - { - if (type != LbpFileType.Jpeg && type != LbpFileType.Png && type != LbpFileType.Texture) return false; - - if (File.Exists(Path.Combine("png", $"{hash}.png"))) return true; - - using MemoryStream ms = new(data); - using BinaryReader reader = new(ms); - - try - { - return type switch - { - LbpFileType.Texture => TextureToPNG(hash, reader), - LbpFileType.Png => PNGToPNG(hash, data), - LbpFileType.Jpeg => JPGToPNG(hash, data), - _ => false, - }; - } - catch(Exception e) - { - Console.WriteLine($"Error while converting {hash}:"); - Console.WriteLine(e); - return false; - } - } - - private static bool TextureToPNG(string hash, BinaryReader reader) - { - // Skip the magic (3 bytes), we already know its a texture - for(int i = 0; i < 3; i++) reader.ReadByte(); - - // This below is shamelessly stolen from ennuo's Toolkit: https://github.com/ennuo/toolkit/blob/d996ee4134740db0ee94e2cbf1e4edbd1b5ec798/src/main/java/ennuo/craftworld/utilities/Compressor.java#L40 - - // This byte determines the method of reading. We can only read a texture (' ') so if it's not ' ' it must be invalid. - if ((char)reader.ReadByte() != ' ') return false; - - reader.ReadInt16(); // ? - short chunks = reader.ReadInt16BE(); - - int[] compressed = new int[chunks]; - int[] decompressed = new int[chunks]; - - int decompressedSize = 0; - int compressedSize = 0; - - for(int i = 0; i < chunks; ++i) - { - compressed[i] = reader.ReadUInt16BE(); - decompressed[i] = reader.ReadUInt16BE(); - - decompressedSize += decompressed[i]; - compressedSize += compressed[i]; - } - - using MemoryStream ms = new(); - using BinaryWriter writer = new(ms); - for(int i = 0; i < chunks; ++i) - { - byte[] deflatedData = reader.ReadBytes(compressed[i]); - if (compressed[i] == decompressed[i]) - { - writer.Write(deflatedData); - } - - Inflater inflater = new(); - inflater.SetInput(deflatedData); - byte[] inflatedData = new byte[decompressed[i]]; - inflater.Inflate(inflatedData); - - writer.Write(inflatedData); - } - - return DDSToPNG(hash, ms.ToArray()); - } - - private static bool DDSToPNG(string hash, byte[] data) - { - using MemoryStream stream = new(); - DDSImage image = new(data); - - image.SaveAsPng(stream); - - Directory.CreateDirectory("png"); - File.WriteAllBytes($"png/{hash}.png", stream.ToArray()); - return true; - } - - private static bool JPGToPNG(string hash, byte[] data) - { - using Image image = Image.Load(data); - using MemoryStream ms = new(); - image.SaveAsPng(ms); - - File.WriteAllBytes($"png/{hash}.png", ms.ToArray()); - return true; - } - - // it sounds dumb i know but hear me out: - // you're completely correct - private static bool PNGToPNG(string hash, byte[] data) - { - File.WriteAllBytes($"png/{hash}.png", data); - return true; - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/InfluxHelper.cs b/ProjectLighthouse/Helpers/InfluxHelper.cs index 98aa74b5..7895853a 100644 --- a/ProjectLighthouse/Helpers/InfluxHelper.cs +++ b/ProjectLighthouse/Helpers/InfluxHelper.cs @@ -1,19 +1,22 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using InfluxDB.Client; using InfluxDB.Client.Writes; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Settings; 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() { @@ -39,25 +42,25 @@ public static class InfluxHelper .Tag("game", gameVersion.ToString()) .Field("playerCountGame", await StatisticsHelper.RecentMatchesForGame(gameVersion)); - writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, gamePoint); + writeApi.WritePoint(gamePoint, ServerConfiguration.Instance.InfluxDB.Bucket, ServerConfiguration.Instance.InfluxDB.Organization); } - writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point); + writeApi.WritePoint(point, ServerConfiguration.Instance.InfluxDB.Bucket, ServerConfiguration.Instance.InfluxDB.Organization); writeApi.Flush(); } catch(Exception e) { - Logger.Log("Exception while logging: ", LoggerLevelInflux.Instance); - - foreach (string line in e.ToString().Split("\n")) Logger.Log(line, LoggerLevelInflux.Instance); + Logger.Error("Exception while logging: ", LogArea.InfluxDB); + Logger.Error(e.ToDetailedException(), LogArea.InfluxDB); } } + [SuppressMessage("ReSharper", "FunctionNeverReturns")] public static async Task StartLogging() { await Client.ReadyAsync(); - Logger.Log("InfluxDB is now ready.", LoggerLevelInflux.Instance); + Logger.Success("InfluxDB is now ready.", LogArea.InfluxDB); Thread t = new ( delegate() @@ -70,9 +73,8 @@ public static class InfluxHelper } catch(Exception e) { - Logger.Log("Exception while running log thread: ", LoggerLevelInflux.Instance); - - foreach (string line in e.ToString().Split("\n")) Logger.Log(line, LoggerLevelInflux.Instance); + Logger.Error("Exception while running log thread: ", LogArea.InfluxDB); + Logger.Error(e.ToDetailedException(), LogArea.InfluxDB); } Thread.Sleep(60000); diff --git a/ProjectLighthouse/Helpers/MaintenanceHelper.cs b/ProjectLighthouse/Helpers/MaintenanceHelper.cs deleted file mode 100644 index 7b50b49d..00000000 --- a/ProjectLighthouse/Helpers/MaintenanceHelper.cs +++ /dev/null @@ -1,66 +0,0 @@ -#nullable enable -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Maintenance; - -namespace LBPUnion.ProjectLighthouse.Helpers; - -public static class MaintenanceHelper -{ - - static MaintenanceHelper() - { - Commands = getListOfInterfaceObjects(); - MaintenanceJobs = getListOfInterfaceObjects(); - } - public static List Commands { get; } - - public static List MaintenanceJobs { get; } - - private static List getListOfInterfaceObjects() where T : class - { - return Assembly.GetExecutingAssembly() - .GetTypes() - .Where(t => t.GetInterfaces().Contains(typeof(T)) && t.GetConstructor(Type.EmptyTypes) != null) - .Select(t => Activator.CreateInstance(t) as T) - .ToList()!; - } - - public static async Task RunCommand(string[] args) - { - if (args.Length < 1) - throw new Exception - ("This should never happen. " + "If it did, its because you tried to run a command before validating that the user actually wants to run one."); - - string baseCmd = args[0]; - args = args.Skip(1).ToArray(); - - IEnumerable suitableCommands = Commands.Where - (command => command.Aliases().Any(a => a.ToLower() == baseCmd.ToLower())) - .Where(command => args.Length >= command.RequiredArgs()); - foreach (ICommand command in suitableCommands) - { - Console.WriteLine("Running command " + command.Name()); - await command.Run(args); - return; - } - - Console.WriteLine("Command not found."); - } - - public static async Task RunMaintenanceJob(string jobName) - { - IMaintenanceJob? job = MaintenanceJobs.FirstOrDefault(j => j.GetType().Name == jobName); - if (job == null) throw new ArgumentNullException(); - - await RunMaintenanceJob(job); - } - - public static async Task RunMaintenanceJob(IMaintenanceJob job) - { - await job.Run(); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/MatchHelper.cs b/ProjectLighthouse/Helpers/MatchHelper.cs index f04eb938..aa7d5908 100644 --- a/ProjectLighthouse/Helpers/MatchHelper.cs +++ b/ProjectLighthouse/Helpers/MatchHelper.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.Linq; using System.Text.Json; using System.Text.RegularExpressions; -using LBPUnion.ProjectLighthouse.Types.Match; +using LBPUnion.ProjectLighthouse.Match.MatchCommands; namespace LBPUnion.ProjectLighthouse.Helpers; @@ -37,7 +37,7 @@ public static class MatchHelper } // This is the function used to show people how laughably awful LBP's protocol is. Beware. - public static IMatchData? Deserialize(string data) + public static IMatchCommand? Deserialize(string data) { string matchType = ""; @@ -61,7 +61,7 @@ public static class MatchHelper return Deserialize(matchType, matchData); } - public static IMatchData? Deserialize(string matchType, string matchData) + public static IMatchCommand? Deserialize(string matchType, string matchData) { return matchType switch { diff --git a/ProjectLighthouse/Helpers/Middlewares/FakeRemoteIPAddressMiddleware.cs b/ProjectLighthouse/Helpers/Middlewares/FakeRemoteIPAddressMiddleware.cs deleted file mode 100644 index cd3ce56c..00000000 --- a/ProjectLighthouse/Helpers/Middlewares/FakeRemoteIPAddressMiddleware.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Net; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; - -namespace LBPUnion.ProjectLighthouse.Helpers.Middlewares; - -public class FakeRemoteIPAddressMiddleware -{ - private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.0.0.1"); - private readonly RequestDelegate next; - - public FakeRemoteIPAddressMiddleware(RequestDelegate next) - { - this.next = next; - } - - public async Task Invoke(HttpContext httpContext) - { - httpContext.Connection.RemoteIpAddress = this.fakeIpAddress; - - await this.next(httpContext); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/RandomHelper.cs b/ProjectLighthouse/Helpers/RandomHelper.cs deleted file mode 100644 index f978717e..00000000 --- a/ProjectLighthouse/Helpers/RandomHelper.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace LBPUnion.ProjectLighthouse.Helpers; - -public static class RandomHelper -{ - public static readonly Random random = new(); -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/ResourceHelper.cs b/ProjectLighthouse/Helpers/ResourceHelper.cs index 389155bd..ebfd55e3 100644 --- a/ProjectLighthouse/Helpers/ResourceHelper.cs +++ b/ProjectLighthouse/Helpers/ResourceHelper.cs @@ -1,18 +1,13 @@ using System; using System.IO; -using System.Linq; -using Kettu; -using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types.Settings; namespace LBPUnion.ProjectLighthouse.Helpers; public static class ResourceHelper { - public static string readManifestFile(string fileName) + public static string ReadManifestFile(string fileName) { - using Stream stream = - typeof(Program).Assembly.GetManifestResourceStream($"{typeof(Program).Namespace}.{fileName}"); + using Stream stream = typeof(Database).Assembly.GetManifestResourceStream($"{typeof(Database).Namespace}.{fileName}"); using StreamReader reader = new(stream ?? throw new Exception("The assembly or manifest resource is null.")); return reader.ReadToEnd().Trim(); diff --git a/ProjectLighthouse/Helpers/SMTPHelper.cs b/ProjectLighthouse/Helpers/SMTPHelper.cs index 01e15869..3a72db02 100644 --- a/ProjectLighthouse/Helpers/SMTPHelper.cs +++ b/ProjectLighthouse/Helpers/SMTPHelper.cs @@ -1,7 +1,7 @@ using System; using System.Net; using System.Net.Mail; -using LBPUnion.ProjectLighthouse.Types.Settings; +using LBPUnion.ProjectLighthouse.Configuration; namespace LBPUnion.ProjectLighthouse.Helpers; @@ -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/StatisticsHelper.cs b/ProjectLighthouse/Helpers/StatisticsHelper.cs index aab77a92..a03b03ab 100644 --- a/ProjectLighthouse/Helpers/StatisticsHelper.cs +++ b/ProjectLighthouse/Helpers/StatisticsHelper.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.Types; using Microsoft.EntityFrameworkCore; @@ -9,11 +10,11 @@ public static class StatisticsHelper { private static readonly Database database = new(); - public static async Task RecentMatches() => await database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync(); + public static async Task RecentMatches() => await database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300).CountAsync(); public static async Task RecentMatchesForGame (GameVersion gameVersion) - => await database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300 && l.GameVersion == gameVersion).CountAsync(); + => await database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300 && l.GameVersion == gameVersion).CountAsync(); public static async Task SlotCount() => await database.Slots.CountAsync(); diff --git a/ProjectLighthouse/Helpers/TimeHelper.cs b/ProjectLighthouse/Helpers/TimeHelper.cs index 6f034855..fade6725 100644 --- a/ProjectLighthouse/Helpers/TimeHelper.cs +++ b/ProjectLighthouse/Helpers/TimeHelper.cs @@ -6,6 +6,10 @@ public static class TimeHelper { public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds(); public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds(); + + public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + + public static long TimestampMillis => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds; } // 1397109686193 diff --git a/ProjectLighthouse/Helpers/TimestampHelper.cs b/ProjectLighthouse/Helpers/TimestampHelper.cs deleted file mode 100644 index 646ad24f..00000000 --- a/ProjectLighthouse/Helpers/TimestampHelper.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace LBPUnion.ProjectLighthouse.Helpers; - -public static class TimestampHelper -{ - public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; - - public static long TimestampMillis => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds; -} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/VersionHelper.cs b/ProjectLighthouse/Helpers/VersionHelper.cs index 0e229ccb..212eb091 100644 --- a/ProjectLighthouse/Helpers/VersionHelper.cs +++ b/ProjectLighthouse/Helpers/VersionHelper.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; using System.Linq; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types.Settings; namespace LBPUnion.ProjectLighthouse.Helpers; @@ -13,10 +10,10 @@ public static class VersionHelper { try { - CommitHash = ResourceHelper.readManifestFile("gitVersion.txt"); - Branch = ResourceHelper.readManifestFile("gitBranch.txt"); + CommitHash = ResourceHelper.ReadManifestFile("gitVersion.txt"); + Branch = ResourceHelper.ReadManifestFile("gitBranch.txt"); - string remotesFile = ResourceHelper.readManifestFile("gitRemotes.txt"); + string remotesFile = ResourceHelper.ReadManifestFile("gitRemotes.txt"); string[] lines = remotesFile.Split('\n'); @@ -26,17 +23,17 @@ public static class VersionHelper // linq is a serious and painful catastrophe but its useful so i'm gonna keep using it Remotes = lines.Select(line => line.Split("\t")[1]).ToArray(); - CommitsOutOfDate = ResourceHelper.readManifestFile("gitUnpushed.txt").Split('\n').Length; + CommitsOutOfDate = ResourceHelper.ReadManifestFile("gitUnpushed.txt").Split('\n').Length; CanCheckForUpdates = true; } catch { - Logger.Log + Logger.Error ( - "Project Lighthouse was built incorrectly. Please make sure git is available when building. " + - "Because of this, you will not be notified of updates.", - LoggerLevelStartup.Instance + "Project Lighthouse was built incorrectly. Please make sure git is available when building.", +// "Because of this, you will not be notified of updates.", + LogArea.Startup ); CommitHash = "invalid"; Branch = "invalid"; @@ -45,11 +42,11 @@ public static class VersionHelper if (IsDirty) { - Logger.Log + Logger.Warn ( "This is a modified version of Project Lighthouse. " + "Please make sure you are properly disclosing the source code to any users who may be using this instance.", - LoggerLevelStartup.Instance + LogArea.Startup ); CanCheckForUpdates = false; } @@ -57,18 +54,18 @@ public static class VersionHelper public static string CommitHash { get; set; } public static string Branch { get; set; } - public static string FullVersion => $"{ServerStatics.ServerName} {Branch}@{CommitHash} {Build}"; + public static string FullVersion => $"Project Lighthouse {Branch}@{CommitHash} {Build} ({ServerConfiguration.Instance.Customization.ServerName})"; public static bool IsDirty => CommitHash.EndsWith("-dirty") || CommitsOutOfDate != 1 || CommitHash == "invalid" || Branch == "invalid"; public static int CommitsOutOfDate { get; set; } public static bool CanCheckForUpdates { get; set; } public static string[] Remotes { get; set; } public const string Build = - #if DEBUG - "Debug"; - #elif RELEASE + #if DEBUG + "Debug"; + #elif RELEASE "Release"; #else - "Unknown"; + "Unknown"; #endif } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/WebhookHelper.cs b/ProjectLighthouse/Helpers/WebhookHelper.cs index 9d0ea1c2..031a79de 100644 --- a/ProjectLighthouse/Helpers/WebhookHelper.cs +++ b/ProjectLighthouse/Helpers/WebhookHelper.cs @@ -1,20 +1,23 @@ using System.Threading.Tasks; using Discord; using Discord.Webhook; -using LBPUnion.ProjectLighthouse.Types.Settings; +using LBPUnion.ProjectLighthouse.Configuration; 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/Types/Categories/Category.cs b/ProjectLighthouse/Levels/Categories/Category.cs similarity index 95% rename from ProjectLighthouse/Types/Categories/Category.cs rename to ProjectLighthouse/Levels/Categories/Category.cs index c2d2918a..1f673f33 100644 --- a/ProjectLighthouse/Types/Categories/Category.cs +++ b/ProjectLighthouse/Levels/Categories/Category.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Categories; +namespace LBPUnion.ProjectLighthouse.Levels.Categories; [XmlType("category")] [XmlRoot("category")] diff --git a/ProjectLighthouse/Helpers/CollectionHelper.cs b/ProjectLighthouse/Levels/Categories/CategoryHelper.cs similarity index 74% rename from ProjectLighthouse/Helpers/CollectionHelper.cs rename to ProjectLighthouse/Levels/Categories/CategoryHelper.cs index 7a19065a..2440402e 100644 --- a/ProjectLighthouse/Helpers/CollectionHelper.cs +++ b/ProjectLighthouse/Levels/Categories/CategoryHelper.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; -using LBPUnion.ProjectLighthouse.Types.Categories; -namespace LBPUnion.ProjectLighthouse.Helpers; +namespace LBPUnion.ProjectLighthouse.Levels.Categories; -public static class CollectionHelper +public static class CategoryHelper { public static readonly List Categories = new(); - static CollectionHelper() + static CategoryHelper() { Categories.Add(new TeamPicksCategory()); Categories.Add(new NewestLevelsCategory()); diff --git a/ProjectLighthouse/Types/Categories/CategoryWithUser.cs b/ProjectLighthouse/Levels/Categories/CategoryWithUser.cs similarity index 80% rename from ProjectLighthouse/Types/Categories/CategoryWithUser.cs rename to ProjectLighthouse/Levels/Categories/CategoryWithUser.cs index 1c7c6a0b..893d32a1 100644 --- a/ProjectLighthouse/Types/Categories/CategoryWithUser.cs +++ b/ProjectLighthouse/Levels/Categories/CategoryWithUser.cs @@ -1,12 +1,11 @@ #nullable enable using System.Collections.Generic; using System.Diagnostics; -using Kettu; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Categories; +namespace LBPUnion.ProjectLighthouse.Levels.Categories; public abstract class CategoryWithUser : Category { @@ -14,7 +13,7 @@ public abstract class CategoryWithUser : Category public override Slot? GetPreviewSlot(Database database) { #if DEBUG - Logger.Log("tried to get preview slot without user on CategoryWithUser", LoggerLevelCategory.Instance); + Logger.Error("tried to get preview slot without user on CategoryWithUser", LogArea.Category); if (Debugger.IsAttached) Debugger.Break(); #endif return null; @@ -24,7 +23,7 @@ public abstract class CategoryWithUser : Category public override int GetTotalSlots(Database database) { #if DEBUG - Logger.Log("tried to get total slots without user on CategoryWithUser", LoggerLevelCategory.Instance); + Logger.Error("tried to get total slots without user on CategoryWithUser", LogArea.Category); if (Debugger.IsAttached) Debugger.Break(); #endif return -1; @@ -34,7 +33,7 @@ public abstract class CategoryWithUser : Category public override IEnumerable GetSlots(Database database, int pageStart, int pageSize) { #if DEBUG - Logger.Log("tried to get slots without user on CategoryWithUser", LoggerLevelCategory.Instance); + Logger.Error("tried to get slots without user on CategoryWithUser", LogArea.Category); if (Debugger.IsAttached) Debugger.Break(); #endif return new List(); @@ -42,7 +41,7 @@ public abstract class CategoryWithUser : Category public new string Serialize(Database database) { - Logger.Log("tried to serialize without user on CategoryWithUser", LoggerLevelCategory.Instance); + Logger.Error("tried to serialize without user on CategoryWithUser", LogArea.Category); return string.Empty; } diff --git a/ProjectLighthouse/Types/Categories/CustomCategory.cs b/ProjectLighthouse/Levels/Categories/CustomCategory.cs similarity index 89% rename from ProjectLighthouse/Types/Categories/CustomCategory.cs rename to ProjectLighthouse/Levels/Categories/CustomCategory.cs index 4d48d3dd..295f0842 100644 --- a/ProjectLighthouse/Types/Categories/CustomCategory.cs +++ b/ProjectLighthouse/Levels/Categories/CustomCategory.cs @@ -1,10 +1,10 @@ #nullable enable using System.Collections.Generic; using System.Linq; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; -using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.PlayerData; -namespace LBPUnion.ProjectLighthouse.Types.Categories; +namespace LBPUnion.ProjectLighthouse.Levels.Categories; public class CustomCategory : Category { diff --git a/ProjectLighthouse/Types/Categories/DatabaseCategory.cs b/ProjectLighthouse/Levels/Categories/DatabaseCategory.cs similarity index 91% rename from ProjectLighthouse/Types/Categories/DatabaseCategory.cs rename to ProjectLighthouse/Levels/Categories/DatabaseCategory.cs index 648985c9..194dae7c 100644 --- a/ProjectLighthouse/Types/Categories/DatabaseCategory.cs +++ b/ProjectLighthouse/Levels/Categories/DatabaseCategory.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -namespace LBPUnion.ProjectLighthouse.Types.Categories; +namespace LBPUnion.ProjectLighthouse.Levels.Categories; public class DatabaseCategory { diff --git a/ProjectLighthouse/Types/Categories/HeartedCategory.cs b/ProjectLighthouse/Levels/Categories/HeartedCategory.cs similarity index 84% rename from ProjectLighthouse/Types/Categories/HeartedCategory.cs rename to ProjectLighthouse/Levels/Categories/HeartedCategory.cs index bf7a1ab3..4b8b1a73 100644 --- a/ProjectLighthouse/Types/Categories/HeartedCategory.cs +++ b/ProjectLighthouse/Levels/Categories/HeartedCategory.cs @@ -2,11 +2,12 @@ using System; using System.Collections.Generic; using System.Linq; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; -using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Types.Categories; +namespace LBPUnion.ProjectLighthouse.Levels.Categories; public class HeartedCategory : CategoryWithUser { diff --git a/ProjectLighthouse/Types/Categories/NewestLevelsCategory.cs b/ProjectLighthouse/Levels/Categories/NewestLevelsCategory.cs similarity index 85% rename from ProjectLighthouse/Types/Categories/NewestLevelsCategory.cs rename to ProjectLighthouse/Levels/Categories/NewestLevelsCategory.cs index 8ea917c5..a5d41b16 100644 --- a/ProjectLighthouse/Types/Categories/NewestLevelsCategory.cs +++ b/ProjectLighthouse/Levels/Categories/NewestLevelsCategory.cs @@ -2,10 +2,10 @@ using System; using System.Collections.Generic; using System.Linq; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; -using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.PlayerData; -namespace LBPUnion.ProjectLighthouse.Types.Categories; +namespace LBPUnion.ProjectLighthouse.Levels.Categories; public class NewestLevelsCategory : Category { diff --git a/ProjectLighthouse/Types/Categories/QueueCategory.cs b/ProjectLighthouse/Levels/Categories/QueueCategory.cs similarity index 84% rename from ProjectLighthouse/Types/Categories/QueueCategory.cs rename to ProjectLighthouse/Levels/Categories/QueueCategory.cs index 322afea6..352d3d69 100644 --- a/ProjectLighthouse/Types/Categories/QueueCategory.cs +++ b/ProjectLighthouse/Levels/Categories/QueueCategory.cs @@ -2,11 +2,12 @@ using System; using System.Collections.Generic; using System.Linq; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; -using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Types.Categories; +namespace LBPUnion.ProjectLighthouse.Levels.Categories; public class QueueCategory : CategoryWithUser { diff --git a/ProjectLighthouse/Types/Categories/TeamPicksCategory.cs b/ProjectLighthouse/Levels/Categories/TeamPicksCategory.cs similarity index 87% rename from ProjectLighthouse/Types/Categories/TeamPicksCategory.cs rename to ProjectLighthouse/Levels/Categories/TeamPicksCategory.cs index 87566a1c..40439cb1 100644 --- a/ProjectLighthouse/Types/Categories/TeamPicksCategory.cs +++ b/ProjectLighthouse/Levels/Categories/TeamPicksCategory.cs @@ -2,10 +2,10 @@ using System; using System.Collections.Generic; using System.Linq; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; -using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.PlayerData; -namespace LBPUnion.ProjectLighthouse.Types.Categories; +namespace LBPUnion.ProjectLighthouse.Levels.Categories; public class TeamPicksCategory : Category { diff --git a/ProjectLighthouse/Types/Levels/HeartedLevel.cs b/ProjectLighthouse/Levels/HeartedLevel.cs similarity index 81% rename from ProjectLighthouse/Types/Levels/HeartedLevel.cs rename to ProjectLighthouse/Levels/HeartedLevel.cs index 6500f3e9..8e9ab867 100644 --- a/ProjectLighthouse/Types/Levels/HeartedLevel.cs +++ b/ProjectLighthouse/Levels/HeartedLevel.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -namespace LBPUnion.ProjectLighthouse.Types.Levels; +namespace LBPUnion.ProjectLighthouse.Levels; public class HeartedLevel { diff --git a/ProjectLighthouse/Types/Levels/LevelTags.cs b/ProjectLighthouse/Levels/LevelTags.cs similarity index 96% rename from ProjectLighthouse/Types/Levels/LevelTags.cs rename to ProjectLighthouse/Levels/LevelTags.cs index 2df0d786..1d6f2e2f 100644 --- a/ProjectLighthouse/Types/Levels/LevelTags.cs +++ b/ProjectLighthouse/Levels/LevelTags.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -namespace LBPUnion.ProjectLighthouse.Types.Levels; +namespace LBPUnion.ProjectLighthouse.Levels; /// /// A series of tags that can be applied to a level diff --git a/ProjectLighthouse/Types/Levels/QueuedLevel.cs b/ProjectLighthouse/Levels/QueuedLevel.cs similarity index 81% rename from ProjectLighthouse/Types/Levels/QueuedLevel.cs rename to ProjectLighthouse/Levels/QueuedLevel.cs index 2957d9ce..45481ce6 100644 --- a/ProjectLighthouse/Types/Levels/QueuedLevel.cs +++ b/ProjectLighthouse/Levels/QueuedLevel.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -namespace LBPUnion.ProjectLighthouse.Types.Levels; +namespace LBPUnion.ProjectLighthouse.Levels; public class QueuedLevel { diff --git a/ProjectLighthouse/Types/Levels/RatedLevel.cs b/ProjectLighthouse/Levels/RatedLevel.cs similarity index 84% rename from ProjectLighthouse/Types/Levels/RatedLevel.cs rename to ProjectLighthouse/Levels/RatedLevel.cs index d34afd08..265783e3 100644 --- a/ProjectLighthouse/Types/Levels/RatedLevel.cs +++ b/ProjectLighthouse/Levels/RatedLevel.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -namespace LBPUnion.ProjectLighthouse.Types.Levels; +namespace LBPUnion.ProjectLighthouse.Levels; public class RatedLevel { diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Levels/Slot.cs similarity index 95% rename from ProjectLighthouse/Types/Levels/Slot.cs rename to ProjectLighthouse/Levels/Slot.cs index 481bdfab..098d7760 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Levels/Slot.cs @@ -4,13 +4,16 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; -using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Files; +using LBPUnion.ProjectLighthouse.Match.Rooms; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData.Reviews; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types.Profiles; -using LBPUnion.ProjectLighthouse.Types.Reviews; -using LBPUnion.ProjectLighthouse.Types.Settings; +using LBPUnion.ProjectLighthouse.Types; -namespace LBPUnion.ProjectLighthouse.Types.Levels; +namespace LBPUnion.ProjectLighthouse.Levels; /// /// A LittleBigPlanet level. @@ -289,8 +292,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/Levels/SlotType.cs b/ProjectLighthouse/Levels/SlotType.cs similarity index 72% rename from ProjectLighthouse/Types/Levels/SlotType.cs rename to ProjectLighthouse/Levels/SlotType.cs index 4554addd..a34876c9 100644 --- a/ProjectLighthouse/Types/Levels/SlotType.cs +++ b/ProjectLighthouse/Levels/SlotType.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Levels; +namespace LBPUnion.ProjectLighthouse.Levels; public enum SlotType { diff --git a/ProjectLighthouse/Types/Levels/VisitedLevel.cs b/ProjectLighthouse/Levels/VisitedLevel.cs similarity index 85% rename from ProjectLighthouse/Types/Levels/VisitedLevel.cs rename to ProjectLighthouse/Levels/VisitedLevel.cs index 167bd258..93a4c4f0 100644 --- a/ProjectLighthouse/Types/Levels/VisitedLevel.cs +++ b/ProjectLighthouse/Levels/VisitedLevel.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -namespace LBPUnion.ProjectLighthouse.Types.Levels; +namespace LBPUnion.ProjectLighthouse.Levels; public class VisitedLevel { diff --git a/ProjectLighthouse/Logging/AspNetToKettuLogger.cs b/ProjectLighthouse/Logging/AspNetToKettuLogger.cs deleted file mode 100644 index 113af70a..00000000 --- a/ProjectLighthouse/Logging/AspNetToKettuLogger.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Kettu; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; -using Microsoft.Extensions.Logging; - -namespace LBPUnion.ProjectLighthouse.Logging; - -public class AspNetToKettuLogger : ILogger -{ - public IDisposable BeginScope(TState state) => NullScope.Instance; - public bool IsEnabled(LogLevel logLevel) => true; - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - LoggerLevel loggerLevel = new LoggerLevelAspNet(logLevel); - - Logger.Log(state.ToString(), loggerLevel); - if (exception == null) return; - - string[] lines = exception.ToDetailedException().Replace("\r", "").Split("\n"); - foreach (string line in lines) Logger.Log(line, loggerLevel); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs b/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs deleted file mode 100644 index ea1fd976..00000000 --- a/ProjectLighthouse/Logging/AspNetToKettuLoggerProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; - -namespace LBPUnion.ProjectLighthouse.Logging; - -[ProviderAlias("Kettu")] -public class AspNetToKettuLoggerProvider : ILoggerProvider, IDisposable -{ - public void Dispose() - { - GC.SuppressFinalize(this); - } - - public ILogger CreateLogger(string categoryName) => new AspNetToKettuLogger(); -} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/ILogger.cs b/ProjectLighthouse/Logging/ILogger.cs new file mode 100644 index 00000000..a44c6939 --- /dev/null +++ b/ProjectLighthouse/Logging/ILogger.cs @@ -0,0 +1,6 @@ +namespace LBPUnion.ProjectLighthouse.Logging; + +public interface ILogger +{ + public void Log(LogLine line); +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/InfluxLogger.cs b/ProjectLighthouse/Logging/InfluxLogger.cs deleted file mode 100644 index 85c88a75..00000000 --- a/ProjectLighthouse/Logging/InfluxLogger.cs +++ /dev/null @@ -1,26 +0,0 @@ -using InfluxDB.Client; -using InfluxDB.Client.Writes; -using Kettu; -using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Types.Settings; - -namespace LBPUnion.ProjectLighthouse.Logging; - -public class InfluxLogger : LoggerBase -{ - public override bool AllowMultiple => false; - - public override void Send(LoggerLine line) - { - string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] "; - - string level = $"{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}"; - string content = line.LineData; - - using WriteApi writeApi = InfluxHelper.Client.GetWriteApi(); - - PointData point = PointData.Measurement("lighthouseLog").Field("level", level).Field("content", content); - - writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LighthouseFileLogger.cs b/ProjectLighthouse/Logging/LighthouseFileLogger.cs deleted file mode 100644 index 4e881771..00000000 --- a/ProjectLighthouse/Logging/LighthouseFileLogger.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.IO; -using Kettu; -using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; - -namespace LBPUnion.ProjectLighthouse.Logging; - -public class LighthouseFileLogger : LoggerBase -{ - private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs"); - public override bool AllowMultiple => false; - - public override void Send(LoggerLine line) - { - FileHelper.EnsureDirectoryCreated(logsDirectory); - - string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] "; - - string contentFile = $"{channel}{line.LineData}\n"; - string contentAll = $"[{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}] {line.LineData}\n"; - - try - { - File.AppendAllText(Path.Combine(logsDirectory, line.LoggerLevel.Name.ToFileName() + ".log"), contentFile); - File.AppendAllText(Path.Combine(logsDirectory, "all.log"), contentAll); - } - catch(IOException) {} // windows, ya goofed - - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LogArea.cs b/ProjectLighthouse/Logging/LogArea.cs new file mode 100644 index 00000000..f83c0d76 --- /dev/null +++ b/ProjectLighthouse/Logging/LogArea.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; + +namespace LBPUnion.ProjectLighthouse.Logging; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public enum LogArea +{ + Login, + Startup, + Category, + Comments, + Config, + Database, + Filter, + HTTP, + InfluxDB, + Match, + Photos, + Resources, + Logger, + Redis, + Command, + Admin, +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LogLevel.cs b/ProjectLighthouse/Logging/LogLevel.cs new file mode 100644 index 00000000..ea5b22b8 --- /dev/null +++ b/ProjectLighthouse/Logging/LogLevel.cs @@ -0,0 +1,10 @@ +namespace LBPUnion.ProjectLighthouse.Logging; + +public enum LogLevel +{ + Success = 0, + Info = 1, + Warning = 2, + Error = 3, + Debug = 4, +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LogLine.cs b/ProjectLighthouse/Logging/LogLine.cs new file mode 100644 index 00000000..ec7bcda2 --- /dev/null +++ b/ProjectLighthouse/Logging/LogLine.cs @@ -0,0 +1,9 @@ +namespace LBPUnion.ProjectLighthouse.Logging; + +public struct LogLine +{ + public LogTrace Trace; + public LogLevel Level; + public string Area; + public string Message; +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LogTrace.cs b/ProjectLighthouse/Logging/LogTrace.cs new file mode 100644 index 00000000..df08b52b --- /dev/null +++ b/ProjectLighthouse/Logging/LogTrace.cs @@ -0,0 +1,8 @@ +#nullable enable +namespace LBPUnion.ProjectLighthouse.Logging; + +public struct LogTrace +{ + public string? Name; + public string? Section; +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/Logger.cs b/ProjectLighthouse/Logging/Logger.cs new file mode 100644 index 00000000..183642fb --- /dev/null +++ b/ProjectLighthouse/Logging/Logger.cs @@ -0,0 +1,224 @@ +#nullable enable +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace LBPUnion.ProjectLighthouse.Logging; + +public class Logger +{ + internal static readonly Logger Instance = new(); + + #region Internals + + /// + /// A list of custom loggers to use. + /// + private readonly List loggers = new(); + + public void AddLogger(ILogger logger) + { + loggers.Add(logger); + LogDebug("Initialized " + logger.GetType().Name, LogArea.Logger); + } + + private LogTrace getTrace(int extraTraceLines = 0) + { + const int depth = 5; + const int skipDepth = depth - 2; + + StackTrace stackTrace = new(true); + StackFrame? frame = stackTrace.GetFrame(skipDepth + extraTraceLines); + System.Diagnostics.Debug.Assert(frame != null); + + string? name; + string? section; + + string? fileName = frame.GetFileName(); + if (fileName == null) + { + name = frame.GetMethod()?.DeclaringType?.Name; + section = frame.GetMethod()?.Name; + } + else + { + name = Path.GetFileNameWithoutExtension(Path.GetFileName(fileName)); + int lineNumber = frame.GetFileLineNumber(); + if (lineNumber == 0) lineNumber = -1; + + section = lineNumber.ToString(); + } + + return new LogTrace + { + Name = name, + Section = section, + }; + } + + #endregion + + #region Queue + + /// + /// A queue for the logger. + /// + /// We use a queue because if two threads try to log something at the time they'll mess each other's printing up. + /// + /// + private readonly ConcurrentQueue logQueue = new(); + + /// + /// Adds a to the queue. Only used internally. + /// + /// The logLine to send to the queue. + private void queueLog(LogLine logLine) + { + logQueue.Enqueue(logLine); + } + + [SuppressMessage("ReSharper", "FunctionNeverReturns")] + public Logger() // Start queue thread on first Logger access + { + Task.Factory.StartNew + ( + () => + { + while (true) + { + bool logged = queueLoop(); + Thread.Sleep(logged ? 10 : 100); + // We wait 100ms if we dont log since it's less likely that the program logged again. + // If we did log, wait 10ms before looping again. + + // This is all so we use as little CPU as possible. This is an endless while loop, after all. + } + } + ); + + // Flush the log queue when we're exiting. + AppDomain.CurrentDomain.UnhandledException += Flush; + AppDomain.CurrentDomain.ProcessExit += Flush; + } + + /// + /// Logs everything in the queue to all loggers immediately. + /// This is a helper function to allow for this function to be easily added to events. + /// + public void Flush(object? _, EventArgs __) + { + Flush(); + } + + /// + /// Logs everything in the queue to all loggers immediately. + /// + public void Flush() + { + while (logQueue.TryDequeue(out LogLine line)) + { + foreach (ILogger logger in loggers) + { + logger.Log(line); + } + } + } + + /// + /// A function used by the queue thread + /// + /// + private bool queueLoop() + { + bool logged = false; + if (logQueue.TryDequeue(out LogLine line)) + { + logged = true; + + foreach (ILogger logger in loggers) + { + logger.Log(line); + } + } + + return logged; + } + + #endregion + + #region Logging functions + #region Static + public static void Debug(string text, LogArea logArea) + { + #if DEBUG + Instance.Log(text, logArea.ToString(), LogLevel.Debug); + #endif + } + + public static void Success(string text, LogArea logArea) + { + Instance.Log(text, logArea.ToString(), LogLevel.Success); + } + + public static void Info(string text, LogArea logArea) + { + Instance.Log(text, logArea.ToString(), LogLevel.Info); + } + + public static void Warn(string text, LogArea logArea) + { + Instance.Log(text, logArea.ToString(), LogLevel.Warning); + } + + public static void Error(string text, LogArea logArea) + { + Instance.Log(text, logArea.ToString(), LogLevel.Error); + } + + #endregion + #region Instance-based + public void LogDebug(string text, LogArea logArea) + { + #if DEBUG + this.Log(text, logArea.ToString(), LogLevel.Debug); + #endif + } + + public void LogSuccess(string text, LogArea logArea) + { + this.Log(text, logArea.ToString(), LogLevel.Success); + } + + public void LogInfo(string text, LogArea logArea) + { + this.Log(text, logArea.ToString(), LogLevel.Info); + } + + public void LogWarn(string text, LogArea logArea) + { + this.Log(text, logArea.ToString(), LogLevel.Warning); + } + + public void LogError(string text, LogArea logArea) + { + this.Log(text, logArea.ToString(), LogLevel.Error); + } + #endregion + + public void Log(string text, string area, LogLevel level, int extraTraceLines = 0) + { + queueLog(new LogLine + { + Level = level, + Message = text, + Area = area, + Trace = getTrace(extraTraceLines), + }); + } + #endregion +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/LoggerLevels.cs b/ProjectLighthouse/Logging/LoggerLevels.cs deleted file mode 100644 index 303dda97..00000000 --- a/ProjectLighthouse/Logging/LoggerLevels.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Kettu; -using Microsoft.Extensions.Logging; - -namespace LBPUnion.ProjectLighthouse.Logging; - -public class LoggerLevelStartup : LoggerLevel -{ - public static readonly LoggerLevelStartup Instance = new(); - public override string Name => "Startup"; -} - -public class LoggerLevelDatabase : LoggerLevel -{ - public static readonly LoggerLevelDatabase Instance = new(); - public override string Name => "Database"; -} - -public class LoggerLevelHttp : LoggerLevel -{ - public static readonly LoggerLevelHttp Instance = new(); - public override string Name => "HTTP"; -} - -public class LoggerLevelFilter : LoggerLevel -{ - public static readonly LoggerLevelFilter Instance = new(); - public override string Name => "Filter"; -} - -public class LoggerLevelLogin : LoggerLevel -{ - public static readonly LoggerLevelLogin Instance = new(); - public override string Name => "Login"; -} - -public class LoggerLevelResources : LoggerLevel -{ - public static readonly LoggerLevelResources Instance = new(); - public override string Name => "Resources"; -} - -public class LoggerLevelMatch : LoggerLevel -{ - public static readonly LoggerLevelMatch Instance = new(); - public override string Name => "Match"; -} - -public class LoggerLevelPhotos : LoggerLevel -{ - public static readonly LoggerLevelPhotos Instance = new(); - public override string Name => "Photos"; -} - -public class LoggerLevelConfig : LoggerLevel -{ - public static readonly LoggerLevelConfig Instance = new(); - public override string Name => "Config"; -} - -public class LoggerLevelInflux : LoggerLevel -{ - public static readonly LoggerLevelInflux Instance = new(); - public override string Name => "Influx"; -} - -public class LoggerLevelComments : LoggerLevel -{ - public static readonly LoggerLevelComments Instance = new(); - public override string Name => "Comments"; -} - -public class LoggerLevelAspNet : LoggerLevel -{ - - public LoggerLevelAspNet(LogLevel level) - { - this.Channel = level.ToString(); - } - public override string Name => "AspNet"; -} - -public class LoggerLevelCategory : LoggerLevel -{ - public static readonly LoggerLevelCategory Instance = new(); - public override string Name => "Category"; -} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/Loggers/AspNet/AspNetToLighthouseLogger.cs b/ProjectLighthouse/Logging/Loggers/AspNet/AspNetToLighthouseLogger.cs new file mode 100644 index 00000000..70820141 --- /dev/null +++ b/ProjectLighthouse/Logging/Loggers/AspNet/AspNetToLighthouseLogger.cs @@ -0,0 +1,30 @@ +using System; +using LBPUnion.ProjectLighthouse.Extensions; +using Microsoft.Extensions.Logging; +using AspLogLevel = Microsoft.Extensions.Logging.LogLevel; + +namespace LBPUnion.ProjectLighthouse.Logging.Loggers.AspNet; + +public class AspNetToLighthouseLogger : Microsoft.Extensions.Logging.ILogger +{ + public IDisposable BeginScope(TState state) => NullScope.Instance; + public bool IsEnabled(AspLogLevel logLevel) => true; + + public string Category { get; init; } + + public AspNetToLighthouseLogger(string category) + { + this.Category = category; + } + + public void Log(AspLogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + LogLevel level = logLevel.ToLighthouseLevel(); + + Logger.Instance.Log(state.ToString(), this.Category, level, 4); + + if (exception == null) return; + + Logger.Instance.Log(exception.ToDetailedException(), this.Category, level, 4); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/Loggers/AspNet/AspNetToLighthouseLoggerProvider.cs b/ProjectLighthouse/Logging/Loggers/AspNet/AspNetToLighthouseLoggerProvider.cs new file mode 100644 index 00000000..9fc6eff6 --- /dev/null +++ b/ProjectLighthouse/Logging/Loggers/AspNet/AspNetToLighthouseLoggerProvider.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.Extensions.Logging; +using IAspLogger = Microsoft.Extensions.Logging.ILogger; + +namespace LBPUnion.ProjectLighthouse.Logging.Loggers.AspNet; + +[ProviderAlias("Kettu")] +public class AspNetToLighthouseLoggerProvider : ILoggerProvider, IDisposable +{ + public void Dispose() + { + GC.SuppressFinalize(this); + } + + public IAspLogger CreateLogger(string category) => new AspNetToLighthouseLogger(category); +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/Loggers/ConsoleLogger.cs b/ProjectLighthouse/Logging/Loggers/ConsoleLogger.cs new file mode 100644 index 00000000..3ebc3343 --- /dev/null +++ b/ProjectLighthouse/Logging/Loggers/ConsoleLogger.cs @@ -0,0 +1,52 @@ +using System; +using LBPUnion.ProjectLighthouse.Extensions; + +namespace LBPUnion.ProjectLighthouse.Logging.Loggers; + +public class ConsoleLogger : ILogger +{ + public void Log(LogLine logLine) + { + ConsoleColor oldForegroundColor = Console.ForegroundColor; + + foreach (string line in logLine.Message.Split('\n')) + { + // The following is scuffed. + // Beware~ + + // Write the level! [Success] + Console.ForegroundColor = ConsoleColor.White; + Console.Write('['); + Console.ForegroundColor = logLine.Level.ToColor(); + Console.Write(logLine.Area); + Console.ForegroundColor = ConsoleColor.White; + Console.Write(':'); + Console.ForegroundColor = logLine.Level.ToColor(); + Console.Write(logLine.Level); + Console.ForegroundColor = ConsoleColor.White; + Console.Write(']'); + Console.ForegroundColor = oldForegroundColor; + Console.Write(' '); + + if (logLine.Trace.Name != null) + { + Console.ForegroundColor = ConsoleColor.White; + Console.Write('<'); + Console.ForegroundColor = logLine.Level.ToColor(); + Console.Write(logLine.Trace.Name); + if (logLine.Trace.Section != null) + { + Console.ForegroundColor = ConsoleColor.White; + Console.Write(':'); + Console.ForegroundColor = logLine.Level.ToColor(); + Console.Write(logLine.Trace.Section); + } + Console.ForegroundColor = ConsoleColor.White; + Console.Write("> "); + Console.ForegroundColor = oldForegroundColor; + } + + Console.WriteLine(line); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/Loggers/FileLogger.cs b/ProjectLighthouse/Logging/Loggers/FileLogger.cs new file mode 100644 index 00000000..802263e5 --- /dev/null +++ b/ProjectLighthouse/Logging/Loggers/FileLogger.cs @@ -0,0 +1,27 @@ +using System; +using System.IO; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Files; + +namespace LBPUnion.ProjectLighthouse.Logging.Loggers; + +public class FileLogger : ILogger +{ + private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs"); + + public void Log(LogLine line) + { + FileHelper.EnsureDirectoryCreated(logsDirectory); + + string contentFile = $"[{ServerStatics.ServerType}] [{line.Level}] <{line.Trace.Name}:{line.Trace.Section}> {line.Message}\n"; + string contentAll = $"[{ServerStatics.ServerType}] [{line.Area}:{line.Level}] <{line.Trace.Name}:{line.Trace.Section}> {line.Message}\n"; + + try + { + File.AppendAllText(Path.Combine(logsDirectory, line.Area + ".log"), contentFile); + File.AppendAllText(Path.Combine(logsDirectory, "all.log"), contentAll); + } + catch(IOException) {} // windows, ya goofed + + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/Loggers/InMemoryLogger.cs b/ProjectLighthouse/Logging/Loggers/InMemoryLogger.cs new file mode 100644 index 00000000..24413ec3 --- /dev/null +++ b/ProjectLighthouse/Logging/Loggers/InMemoryLogger.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace LBPUnion.ProjectLighthouse.Logging.Loggers; + +public class InMemoryLogger : ILogger +{ + public readonly List Lines = new(); + + public void Log(LogLine line) + { + lock(this.Lines) + { + this.Lines.Add(line); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Logging/Loggers/InfluxLogger.cs b/ProjectLighthouse/Logging/Loggers/InfluxLogger.cs new file mode 100644 index 00000000..a5685d32 --- /dev/null +++ b/ProjectLighthouse/Logging/Loggers/InfluxLogger.cs @@ -0,0 +1,23 @@ +using InfluxDB.Client; +using InfluxDB.Client.Writes; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Helpers; + +namespace LBPUnion.ProjectLighthouse.Logging.Loggers; + +public class InfluxLogger : ILogger +{ + public void Log(LogLine line) + { + string channel = string.IsNullOrEmpty(line.Area) ? "" : $"[{line.Area}] "; + + string level = $"{$"{channel} {line}".TrimEnd()}"; + string content = line.Message; + + using WriteApi writeApi = InfluxHelper.Client.GetWriteApi(); + + PointData point = PointData.Measurement("lighthouseLog").Field("level", level).Field("content", content); + + writeApi.WritePoint(point, ServerConfiguration.Instance.InfluxDB.Bucket, ServerConfiguration.Instance.InfluxDB.Organization); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/ICommand.cs b/ProjectLighthouse/Maintenance/ICommand.cs deleted file mode 100644 index 03ce547b..00000000 --- a/ProjectLighthouse/Maintenance/ICommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Threading.Tasks; - -namespace LBPUnion.ProjectLighthouse.Maintenance; - -public interface ICommand -{ - - public string FirstAlias => this.Aliases()[0]; - public Task Run(string[] args); - - public string Name(); - - public string[] Aliases(); - - public string Arguments(); - - public int RequiredArgs(); -} \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupXMLInjection.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupXMLInjection.cs deleted file mode 100644 index c504a16e..00000000 --- a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupXMLInjection.cs +++ /dev/null @@ -1,36 +0,0 @@ -#nullable enable -using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Profiles; -using LBPUnion.ProjectLighthouse.Types.Reports; -using LBPUnion.ProjectLighthouse.Types.Reviews; - -namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs; - -public class CleanupXmlInjection : IMaintenanceJob -{ - private readonly Database database = new(); - public string Name() => "Sanitize user content"; - public string Description() => "Sanitizes all user-generated strings in levels, reviews, comments, users, and scores to prevent XML injection. Only needs to be run once."; - - public async Task Run() - { - foreach (Slot slot in this.database.Slots) SanitizationHelper.SanitizeStringsInClass(slot); - - foreach (Review review in this.database.Reviews) SanitizationHelper.SanitizeStringsInClass(review); - - foreach (Comment comment in this.database.Comments) SanitizationHelper.SanitizeStringsInClass(comment); - - foreach (Score score in this.database.Scores) SanitizationHelper.SanitizeStringsInClass(score); - - foreach (User user in this.database.Users) SanitizationHelper.SanitizeStringsInClass(user); - - foreach (Photo photo in this.database.Photos) SanitizationHelper.SanitizeStringsInClass(photo); - - foreach (GriefReport report in this.database.Reports) SanitizationHelper.SanitizeStringsInClass(report); - - await this.database.SaveChangesAsync(); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/CreateRoom.cs b/ProjectLighthouse/Match/MatchCommands/CreateRoom.cs similarity index 86% rename from ProjectLighthouse/Types/Match/CreateRoom.cs rename to ProjectLighthouse/Match/MatchCommands/CreateRoom.cs index 1b244315..345674e4 100644 --- a/ProjectLighthouse/Types/Match/CreateRoom.cs +++ b/ProjectLighthouse/Match/MatchCommands/CreateRoom.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.Match.Rooms; -namespace LBPUnion.ProjectLighthouse.Types.Match; +namespace LBPUnion.ProjectLighthouse.Match.MatchCommands; [SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] -public class CreateRoom : IMatchData +public class CreateRoom : IMatchCommand { public int BuildVersion; public int HostMood; diff --git a/ProjectLighthouse/Types/Match/FindBestRoom.cs b/ProjectLighthouse/Match/MatchCommands/FindBestRoom.cs similarity index 82% rename from ProjectLighthouse/Types/Match/FindBestRoom.cs rename to ProjectLighthouse/Match/MatchCommands/FindBestRoom.cs index eebf2e57..255c7c7f 100644 --- a/ProjectLighthouse/Types/Match/FindBestRoom.cs +++ b/ProjectLighthouse/Match/MatchCommands/FindBestRoom.cs @@ -1,13 +1,14 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.Match.Rooms; -namespace LBPUnion.ProjectLighthouse.Types.Match; +namespace LBPUnion.ProjectLighthouse.Match.MatchCommands; // Schema is the EXACT SAME as CreateRoom (but cant be a subclass here), so see comments there for details [SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] -public class FindBestRoom : IMatchData +public class FindBestRoom : IMatchCommand { public int BuildVersion; public int HostMood; diff --git a/ProjectLighthouse/Match/MatchCommands/IMatchCommand.cs b/ProjectLighthouse/Match/MatchCommands/IMatchCommand.cs new file mode 100644 index 00000000..fa0f3a40 --- /dev/null +++ b/ProjectLighthouse/Match/MatchCommands/IMatchCommand.cs @@ -0,0 +1,4 @@ +namespace LBPUnion.ProjectLighthouse.Match.MatchCommands; + +public interface IMatchCommand +{} \ No newline at end of file diff --git a/ProjectLighthouse/Match/MatchCommands/UpdateMyPlayerData.cs b/ProjectLighthouse/Match/MatchCommands/UpdateMyPlayerData.cs new file mode 100644 index 00000000..82334536 --- /dev/null +++ b/ProjectLighthouse/Match/MatchCommands/UpdateMyPlayerData.cs @@ -0,0 +1,11 @@ +#nullable enable +using LBPUnion.ProjectLighthouse.Match.Rooms; + +namespace LBPUnion.ProjectLighthouse.Match.MatchCommands; + +public class UpdateMyPlayerData : IMatchCommand +{ + public string Player { get; set; } = null!; + + public RoomState? RoomState { get; set; } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs b/ProjectLighthouse/Match/MatchCommands/UpdatePlayersInRoom.cs similarity index 69% rename from ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs rename to ProjectLighthouse/Match/MatchCommands/UpdatePlayersInRoom.cs index 58288b5c..50628471 100644 --- a/ProjectLighthouse/Types/Match/UpdatePlayersInRoom.cs +++ b/ProjectLighthouse/Match/MatchCommands/UpdatePlayersInRoom.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace LBPUnion.ProjectLighthouse.Types.Match; +namespace LBPUnion.ProjectLighthouse.Match.MatchCommands; [SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] -public class UpdatePlayersInRoom : IMatchData +public class UpdatePlayersInRoom : IMatchCommand { public List Players { get; set; } public List Reservations { get; set; } diff --git a/ProjectLighthouse/Types/Match/NatType.cs b/ProjectLighthouse/Match/NatType.cs similarity index 59% rename from ProjectLighthouse/Types/Match/NatType.cs rename to ProjectLighthouse/Match/NatType.cs index efd54b16..ab007eae 100644 --- a/ProjectLighthouse/Types/Match/NatType.cs +++ b/ProjectLighthouse/Match/NatType.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Match; +namespace LBPUnion.ProjectLighthouse.Match; public enum NatType { diff --git a/ProjectLighthouse/Types/Match/Player.cs b/ProjectLighthouse/Match/Player.cs similarity index 79% rename from ProjectLighthouse/Types/Match/Player.cs rename to ProjectLighthouse/Match/Player.cs index 263ed4fe..a36d717d 100644 --- a/ProjectLighthouse/Types/Match/Player.cs +++ b/ProjectLighthouse/Match/Player.cs @@ -1,8 +1,9 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -namespace LBPUnion.ProjectLighthouse.Types.Match; +namespace LBPUnion.ProjectLighthouse.Match; [Serializable] public class Player diff --git a/ProjectLighthouse/Types/Match/FindBestRoomResponse.cs b/ProjectLighthouse/Match/Rooms/FindBestRoomResponse.cs similarity index 90% rename from ProjectLighthouse/Types/Match/FindBestRoomResponse.cs rename to ProjectLighthouse/Match/Rooms/FindBestRoomResponse.cs index 42b23769..f22c0ebb 100644 --- a/ProjectLighthouse/Types/Match/FindBestRoomResponse.cs +++ b/ProjectLighthouse/Match/Rooms/FindBestRoomResponse.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Match; +namespace LBPUnion.ProjectLighthouse.Match.Rooms; public class FindBestRoomResponse { diff --git a/ProjectLighthouse/Types/Match/Room.cs b/ProjectLighthouse/Match/Rooms/Room.cs similarity index 65% rename from ProjectLighthouse/Types/Match/Room.cs rename to ProjectLighthouse/Match/Rooms/Room.cs index 63128150..46c4d5d7 100644 --- a/ProjectLighthouse/Types/Match/Room.cs +++ b/ProjectLighthouse/Match/Rooms/Room.cs @@ -1,36 +1,49 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; +using Redis.OM.Modeling; -namespace LBPUnion.ProjectLighthouse.Types.Match; +namespace LBPUnion.ProjectLighthouse.Match.Rooms; +[Document(StorageType = StorageType.Json)] public class Room { - [JsonIgnore] - public List Players { get; set; } + private int roomId; - public int RoomId { get; set; } + [Indexed] + public int RoomId { + get => this.roomId; + set { + this.RedisId = value.ToString(); + this.roomId = value; + } + } - [JsonIgnore] + [RedisIdField] + public string RedisId { get; set; } + + [Indexed] + public List PlayerIds { get; set; } + + [Indexed] public GameVersion RoomVersion { get; set; } - [JsonIgnore] + [Indexed] public Platform RoomPlatform { get; set; } + [Indexed] public RoomSlot Slot { get; set; } + + [Indexed] public RoomState State { get; set; } [JsonIgnore] - public bool IsInPod => this.Slot.SlotType == SlotType.Pod; - - [JsonIgnore] + [Indexed] public bool IsLookingForPlayers => this.State == RoomState.PlayingLevel || this.State == RoomState.DivingInWaiting; [JsonIgnore] - public User Host => this.Players[0]; - - public int PlayerCount => this.Players.Count; + public int HostId => this.PlayerIds[0]; #nullable enable public override bool Equals(object? obj) diff --git a/ProjectLighthouse/Helpers/RoomHelper.cs b/ProjectLighthouse/Match/Rooms/RoomHelper.cs similarity index 60% rename from ProjectLighthouse/Helpers/RoomHelper.cs rename to ProjectLighthouse/Match/Rooms/RoomHelper.cs index 03a52a82..33f1959c 100644 --- a/ProjectLighthouse/Helpers/RoomHelper.cs +++ b/ProjectLighthouse/Match/Rooms/RoomHelper.cs @@ -3,26 +3,21 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; -using Kettu; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Match; -using LBPUnion.ProjectLighthouse.Types.Profiles; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.StorableLists; +using LBPUnion.ProjectLighthouse.StorableLists.Stores; -namespace LBPUnion.ProjectLighthouse.Helpers; +namespace LBPUnion.ProjectLighthouse.Match.Rooms; public class RoomHelper { - public static readonly List Rooms = new(); - - public static readonly RoomSlot PodSlot = new() - { - SlotType = SlotType.Pod, - SlotId = 0, - }; - - private static int roomIdIncrement; + public static readonly object RoomLock = new(); + public static StorableList Rooms => RoomStore.GetRooms(); public static void StartCleanupThread() { @@ -39,25 +34,21 @@ public class RoomHelper } ); } - + + private static int roomIdIncrement; internal static int RoomIdIncrement => roomIdIncrement++; public static FindBestRoomResponse? FindBestRoom(User? user, GameVersion roomVersion, RoomSlot? slot, Platform? platform, string? location) { if (roomVersion == GameVersion.LittleBigPlanet1 || roomVersion == GameVersion.LittleBigPlanetPSP) { - Logger.Log($"Returning null for FindBestRoom, game ({roomVersion}) does not support dive in (should never happen?)", LoggerLevelMatch.Instance); + Logger.Error($"Returning null for FindBestRoom, game ({roomVersion}) does not support dive in (should never happen?)", LogArea.Match); return null; } - bool anyRoomsLookingForPlayers; - List rooms; + IEnumerable rooms = Rooms; - lock(Rooms) - { - anyRoomsLookingForPlayers = Rooms.Any(r => r.IsLookingForPlayers); - rooms = anyRoomsLookingForPlayers ? Rooms.Where(r => anyRoomsLookingForPlayers && r.IsLookingForPlayers).ToList() : Rooms; - } + rooms = rooms.OrderBy(r => r.IsLookingForPlayers); rooms = rooms.Where(r => r.RoomVersion == roomVersion).ToList(); if (platform != null) rooms = rooms.Where(r => r.RoomPlatform == platform).ToList(); @@ -73,24 +64,24 @@ public class RoomHelper // Don't attempt to dive into the current room the player is in. if (user != null) { - rooms = rooms.Where(r => !r.Players.Contains(user)).ToList(); + rooms = rooms.Where(r => !r.PlayerIds.Contains(user.UserId)).ToList(); } foreach (Room room in rooms) // Look for rooms looking for players before moving on to rooms that are idle. { - if (user != null && MatchHelper.DidUserRecentlyDiveInWith(user.UserId, room.Host.UserId)) continue; + if (user != null && MatchHelper.DidUserRecentlyDiveInWith(user.UserId, room.HostId)) continue; Dictionary relevantUserLocations = new(); // Determine if all players in a room have UserLocations stored, also store the relevant userlocations while we're at it - bool allPlayersHaveLocations = room.Players.All + bool allPlayersHaveLocations = room.PlayerIds.All ( p => { - bool gotValue = MatchHelper.UserLocations.TryGetValue(p.UserId, out string? value); + bool gotValue = MatchHelper.UserLocations.TryGetValue(p, out string? value); - if (gotValue && value != null) relevantUserLocations.Add(p.UserId, value); + if (gotValue && value != null) relevantUserLocations.Add(p, value); return gotValue; } ); @@ -105,7 +96,7 @@ public class RoomHelper response.Players = new List(); response.Locations = new List(); - foreach (User player in room.Players) + foreach (User player in room.GetPlayers(new Database())) { response.Players.Add ( @@ -140,7 +131,7 @@ public class RoomHelper }, }; - Logger.Log($"Found a room (id: {room.RoomId}) for user {user?.Username ?? "null"} (id: {user?.UserId ?? -1})", LoggerLevelMatch.Instance); + Logger.Success($"Found a room (id: {room.RoomId}) for user {user?.Username ?? "null"} (id: {user?.UserId ?? -1})", LogArea.Match); return response; } @@ -148,74 +139,80 @@ public class RoomHelper return null; } - public static Room CreateRoom(User user, GameVersion roomVersion, Platform roomPlatform, RoomSlot? slot = null) + public static Room CreateRoom(int userId, GameVersion roomVersion, Platform roomPlatform, RoomSlot? slot = null) => CreateRoom ( - new List + new List { - user, + userId, }, roomVersion, roomPlatform, slot ); - public static Room CreateRoom(List users, GameVersion roomVersion, Platform roomPlatform, RoomSlot? slot = null) + public static Room CreateRoom(List users, GameVersion roomVersion, Platform roomPlatform, RoomSlot? slot = null) { Room room = new() { RoomId = RoomIdIncrement, - Players = users, + PlayerIds = users, State = RoomState.Idle, - Slot = slot ?? PodSlot, + Slot = slot ?? RoomSlot.PodSlot, RoomVersion = roomVersion, RoomPlatform = roomPlatform, }; - CleanupRooms(room.Host, room); - lock(Rooms) Rooms.Add(room); - Logger.Log($"Created room (id: {room.RoomId}) for host {room.Host.Username} (id: {room.Host.UserId})", LoggerLevelMatch.Instance); + CleanupRooms(room.HostId, room); + lock(RoomLock) Rooms.Add(room); + Logger.Info($"Created room (id: {room.RoomId}) for host {room.HostId}", LogArea.Match); return room; } - public static Room? FindRoomByUser(User user, GameVersion roomVersion, Platform roomPlatform, bool createIfDoesNotExist = false) + public static Room? FindRoomByUser(int userId, GameVersion roomVersion, Platform roomPlatform, bool createIfDoesNotExist = false) { - lock(Rooms) - foreach (Room room in Rooms.Where(room => room.Players.Any(player => user == player))) - return room; + foreach (Room room in Rooms.Where(room => room.PlayerIds.Any(player => userId == player))) + return room; - return createIfDoesNotExist ? CreateRoom(user, roomVersion, roomPlatform) : null; + return createIfDoesNotExist ? CreateRoom(userId, roomVersion, roomPlatform) : null; } public static Room? FindRoomByUserId(int userId) { - lock(Rooms) - foreach (Room room in Rooms.Where(room => room.Players.Any(player => player.UserId == userId))) + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (Room room in Rooms) + { + if (room.PlayerIds.Any(p => p == userId)) + { return room; + } + } return null; } [SuppressMessage("ReSharper", "InvertIf")] - public static void CleanupRooms(User? host = null, Room? newRoom = null) + public static void CleanupRooms(int? hostId = null, Room? newRoom = null, Database? database = null) { - lock(Rooms) + lock(RoomLock) { - int roomCountBeforeCleanup = Rooms.Count; + int roomCountBeforeCleanup = Rooms.Count(); // Remove offline players from rooms foreach (Room room in Rooms) { - // do not shorten, this prevents collection modified errors - List playersToRemove = room.Players.Where(player => player.Status.StatusType == StatusType.Offline).ToList(); - foreach (User user in playersToRemove) room.Players.Remove(user); + List players = room.GetPlayers(database ?? new Database()); + + List playersToRemove = players.Where(player => player.Status.StatusType == StatusType.Offline).Select(player => player.UserId).ToList(); + + foreach (int player in playersToRemove) room.PlayerIds.Remove(player); } // Delete old rooms based on host - if (host != null) + if (hostId != null) try { - Rooms.RemoveAll(r => r.Host == host); + Rooms.RemoveAll(r => r.HostId == hostId); } catch { @@ -228,17 +225,18 @@ public class RoomHelper { if (room == newRoom) continue; - foreach (User newRoomPlayer in newRoom.Players) room.Players.RemoveAll(p => p == newRoomPlayer); + foreach (int newRoomPlayer in newRoom.PlayerIds) room.PlayerIds.RemoveAll(p => p == newRoomPlayer); } - Rooms.RemoveAll(r => r.Players.Count == 0); // Remove empty rooms - Rooms.RemoveAll(r => r.Players.Count > 4); // Remove obviously bogus rooms + Rooms.RemoveAll(r => r.PlayerIds.Count == 0); // Remove empty rooms + Rooms.RemoveAll(r => r.PlayerIds.Count > 4); // Remove obviously bogus rooms - int roomCountAfterCleanup = Rooms.Count; + int roomCountAfterCleanup = Rooms.Count(); if (roomCountBeforeCleanup != roomCountAfterCleanup) { - Logger.Log($"Cleaned up {roomCountBeforeCleanup - roomCountAfterCleanup} rooms.", LoggerLevelMatch.Instance); + Logger.Debug($"Cleaned up {roomCountBeforeCleanup - roomCountAfterCleanup} rooms.", + LogArea.Match); } } } diff --git a/ProjectLighthouse/Match/Rooms/RoomSlot.cs b/ProjectLighthouse/Match/Rooms/RoomSlot.cs new file mode 100644 index 00000000..452ac207 --- /dev/null +++ b/ProjectLighthouse/Match/Rooms/RoomSlot.cs @@ -0,0 +1,15 @@ +using LBPUnion.ProjectLighthouse.Levels; + +namespace LBPUnion.ProjectLighthouse.Match.Rooms; + +public class RoomSlot +{ + public int SlotId { get; set; } + public SlotType SlotType { get; set; } + + public static readonly RoomSlot PodSlot = new() + { + SlotType = SlotType.Pod, + SlotId = 0, + }; +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/RoomState.cs b/ProjectLighthouse/Match/Rooms/RoomState.cs similarity index 93% rename from ProjectLighthouse/Types/Match/RoomState.cs rename to ProjectLighthouse/Match/Rooms/RoomState.cs index 88145125..ed07937b 100644 --- a/ProjectLighthouse/Types/Match/RoomState.cs +++ b/ProjectLighthouse/Match/Rooms/RoomState.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -namespace LBPUnion.ProjectLighthouse.Types.Match; +namespace LBPUnion.ProjectLighthouse.Match.Rooms; [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum RoomState diff --git a/ProjectLighthouse/Middlewares/FakeRemoteIPAddressMiddleware.cs b/ProjectLighthouse/Middlewares/FakeRemoteIPAddressMiddleware.cs new file mode 100644 index 00000000..aedfb747 --- /dev/null +++ b/ProjectLighthouse/Middlewares/FakeRemoteIPAddressMiddleware.cs @@ -0,0 +1,20 @@ +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace LBPUnion.ProjectLighthouse.Middlewares; + +public class FakeRemoteIPAddressMiddleware : Middleware +{ + private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.0.0.1"); + + public FakeRemoteIPAddressMiddleware(RequestDelegate next) : base(next) + {} + + public override async Task InvokeAsync(HttpContext httpContext) + { + httpContext.Connection.RemoteIpAddress = this.fakeIpAddress; + + await this.next(httpContext); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Middlewares/Middleware.cs b/ProjectLighthouse/Middlewares/Middleware.cs new file mode 100644 index 00000000..bcfedda9 --- /dev/null +++ b/ProjectLighthouse/Middlewares/Middleware.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; + +namespace LBPUnion.ProjectLighthouse.Middlewares; + +public abstract class Middleware +{ + private protected RequestDelegate next { get; init; } + + protected Middleware(RequestDelegate next) + { + this.next = next; + } + + [UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] + public abstract Task InvokeAsync(HttpContext httpContext); +} \ No newline at end of file diff --git a/ProjectLighthouse/Middlewares/RequestLogMiddleware.cs b/ProjectLighthouse/Middlewares/RequestLogMiddleware.cs new file mode 100644 index 00000000..95998082 --- /dev/null +++ b/ProjectLighthouse/Middlewares/RequestLogMiddleware.cs @@ -0,0 +1,46 @@ +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Logging; +using Microsoft.AspNetCore.Http; + +namespace LBPUnion.ProjectLighthouse.Middlewares; + +public class RequestLogMiddleware : Middleware +{ + public RequestLogMiddleware(RequestDelegate next) : base(next) + {} + + // Logs every request and the response to it + // Example: "200, 13ms: GET /LITTLEBIGPLANETPS3_XML/news" + // Example: "404, 127ms: GET /asdasd?query=osucookiezi727ppbluezenithtopplayhdhr" + public override async Task InvokeAsync(HttpContext context) + { + Stopwatch requestStopwatch = new(); + requestStopwatch.Start(); + + context.Request.EnableBuffering(); // Allows us to reset the position of Request.Body for later logging + + // Log all headers. +// foreach (KeyValuePair header in context.Request.Headers) Logger.Log($"{header.Key}: {header.Value}"); + + await this.next(context); // Handle the request so we can get the status code from it + + requestStopwatch.Stop(); + + Logger.Info + ( + $"{context.Response.StatusCode}, {requestStopwatch.ElapsedMilliseconds}ms: {context.Request.Method} {context.Request.Path}{context.Request.QueryString}", + LogArea.HTTP + ); + + #if DEBUG + // Log post body + if (context.Request.Method == "POST") + { + context.Request.Body.Position = 0; + Logger.Debug(await new StreamReader(context.Request.Body).ReadToEndAsync(), LogArea.HTTP); + } + #endif + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Migrations/20220610061641_AddCompletedMigrations.cs b/ProjectLighthouse/Migrations/20220610061641_AddCompletedMigrations.cs new file mode 100644 index 00000000..5c33f89a --- /dev/null +++ b/ProjectLighthouse/Migrations/20220610061641_AddCompletedMigrations.cs @@ -0,0 +1,37 @@ +using System; +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20220610061641_AddCompletedMigrations")] + public class AddCompletedMigrations : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CompletedMigrations", + columns: table => new + { + MigrationName = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RanAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CompletedMigrations", x => x.MigrationName); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CompletedMigrations"); + } + } +} diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 6acb3ff5..147d7e03 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -1,4 +1,5 @@ // +using System; using LBPUnion.ProjectLighthouse; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -15,35 +16,69 @@ namespace ProjectLighthouse.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("ProductVersion", "6.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 64); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Administration.CompletedMigration", b => { - b.Property("AuthenticationAttemptId") + b.Property("MigrationName") + .HasColumnType("varchar(255)"); + + b.Property("RanAt") + .HasColumnType("datetime(6)"); + + b.HasKey("MigrationName"); + + b.ToTable("CompletedMigrations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Administration.Reports.GriefReport", b => + { + b.Property("ReportId") .ValueGeneratedOnAdd() .HasColumnType("int"); - b.Property("GameTokenId") - .HasColumnType("int"); - - b.Property("IPAddress") + b.Property("Bounds") .HasColumnType("longtext"); - b.Property("Platform") + b.Property("GriefStateHash") + .HasColumnType("longtext"); + + b.Property("InitialStateHash") + .HasColumnType("longtext"); + + b.Property("JpegHash") + .HasColumnType("longtext"); + + b.Property("LevelId") + .HasColumnType("int"); + + b.Property("LevelOwner") + .HasColumnType("longtext"); + + b.Property("LevelType") + .HasColumnType("longtext"); + + b.Property("Players") + .HasColumnType("longtext"); + + b.Property("ReportingPlayerId") .HasColumnType("int"); b.Property("Timestamp") .HasColumnType("bigint"); - b.HasKey("AuthenticationAttemptId"); + b.Property("Type") + .HasColumnType("int"); - b.HasIndex("GameTokenId"); + b.HasKey("ReportId"); - b.ToTable("AuthenticationAttempts"); + b.HasIndex("ReportingPlayerId"); + + b.ToTable("Reports"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Categories.DatabaseCategory", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.Categories.DatabaseCategory", b => { b.Property("CategoryId") .ValueGeneratedOnAdd() @@ -69,62 +104,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("CustomCategories"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => - { - b.Property("TokenId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Approved") - .HasColumnType("tinyint(1)"); - - b.Property("GameVersion") - .HasColumnType("int"); - - b.Property("Platform") - .HasColumnType("int"); - - b.Property("Used") - .HasColumnType("tinyint(1)"); - - b.Property("UserId") - .HasColumnType("int"); - - b.Property("UserLocation") - .HasColumnType("longtext"); - - b.Property("UserToken") - .HasColumnType("longtext"); - - b.HasKey("TokenId"); - - b.HasIndex("UserId"); - - b.ToTable("GameTokens"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => - { - b.Property("HeartedProfileId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("HeartedUserId") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.HasKey("HeartedProfileId"); - - b.HasIndex("HeartedUserId"); - - b.HasIndex("UserId"); - - b.ToTable("HeartedProfiles"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.HeartedLevel", b => { b.Property("HeartedLevelId") .ValueGeneratedOnAdd() @@ -145,7 +125,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("HeartedLevels"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.QueuedLevel", b => { b.Property("QueuedLevelId") .ValueGeneratedOnAdd() @@ -166,7 +146,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("QueuedLevels"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.RatedLevel", b => { b.Property("RatedLevelId") .ValueGeneratedOnAdd() @@ -193,7 +173,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("RatedLevels"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.Slot", b => { b.Property("SlotId") .ValueGeneratedOnAdd() @@ -318,7 +298,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Slots"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.VisitedLevel", b => { b.Property("VisitedLevelId") .ValueGeneratedOnAdd() @@ -351,7 +331,66 @@ namespace ProjectLighthouse.Migrations b.ToTable("VisitedLevels"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Used") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.HasIndex("UserId"); + + b.ToTable("GameTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Photo", b => { b.Property("PhotoId") .ValueGeneratedOnAdd() @@ -390,7 +429,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Photos"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.PhotoSubject", b => { b.Property("PhotoSubjectId") .ValueGeneratedOnAdd() @@ -409,7 +448,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("PhotoSubjects"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Comment", b => { b.Property("CommentId") .ValueGeneratedOnAdd() @@ -452,7 +491,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Comments"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Email.EmailSetToken", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email.EmailSetToken", b => { b.Property("EmailSetTokenId") .ValueGeneratedOnAdd() @@ -471,7 +510,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("EmailSetTokens"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Email.EmailVerificationToken", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email.EmailVerificationToken", b => { b.Property("EmailVerificationTokenId") .ValueGeneratedOnAdd() @@ -490,7 +529,28 @@ namespace ProjectLighthouse.Migrations b.ToTable("EmailVerificationTokens"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastContact", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.HeartedProfile", b => + { + b.Property("HeartedProfileId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("HeartedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedProfileId"); + + b.HasIndex("HeartedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedProfiles"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.LastContact", b => { b.Property("UserId") .HasColumnType("int"); @@ -509,7 +569,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("LastContacts"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Location", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -526,169 +586,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Locations"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reaction", b => - { - b.Property("RatingId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Rating") - .HasColumnType("int"); - - b.Property("TargetId") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.HasKey("RatingId"); - - b.ToTable("Reactions"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reports.GriefReport", b => - { - b.Property("ReportId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Bounds") - .HasColumnType("longtext"); - - b.Property("GriefStateHash") - .HasColumnType("longtext"); - - b.Property("InitialStateHash") - .HasColumnType("longtext"); - - b.Property("JpegHash") - .HasColumnType("longtext"); - - b.Property("LevelId") - .HasColumnType("int"); - - b.Property("LevelOwner") - .HasColumnType("longtext"); - - b.Property("LevelType") - .HasColumnType("longtext"); - - b.Property("Players") - .HasColumnType("longtext"); - - b.Property("ReportingPlayerId") - .HasColumnType("int"); - - b.Property("Timestamp") - .HasColumnType("bigint"); - - b.Property("Type") - .HasColumnType("int"); - - b.HasKey("ReportId"); - - b.HasIndex("ReportingPlayerId"); - - b.ToTable("Reports"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => - { - b.Property("RatedReviewId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("ReviewId") - .HasColumnType("int"); - - b.Property("Thumb") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.HasKey("RatedReviewId"); - - b.HasIndex("ReviewId"); - - b.HasIndex("UserId"); - - b.ToTable("RatedReviews"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => - { - b.Property("ReviewId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Deleted") - .HasColumnType("tinyint(1)"); - - b.Property("DeletedBy") - .HasColumnType("int"); - - b.Property("LabelCollection") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ReviewerId") - .HasColumnType("int"); - - b.Property("SlotId") - .HasColumnType("int"); - - b.Property("Text") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Thumb") - .HasColumnType("int"); - - b.Property("ThumbsDown") - .HasColumnType("int"); - - b.Property("ThumbsUp") - .HasColumnType("int"); - - b.Property("Timestamp") - .HasColumnType("bigint"); - - b.HasKey("ReviewId"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("SlotId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => - { - b.Property("ScoreId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("PlayerIdCollection") - .HasColumnType("longtext"); - - b.Property("Points") - .HasColumnType("int"); - - b.Property("SlotId") - .HasColumnType("int"); - - b.Property("Type") - .HasColumnType("int"); - - b.HasKey("ScoreId"); - - b.HasIndex("SlotId"); - - b.ToTable("Scores"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", b => { b.Property("UserId") .ValueGeneratedOnAdd() @@ -761,7 +659,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.UserApprovedIpAddress", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.UserApprovedIpAddress", b => { b.Property("UserApprovedIpAddressId") .ValueGeneratedOnAdd() @@ -780,7 +678,123 @@ namespace ProjectLighthouse.Migrations b.ToTable("UserApprovedIpAddresses"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reaction", b => + { + b.Property("RatingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("TargetId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("RatingId"); + + b.ToTable("Reactions"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.RatedReview", b => + { + b.Property("RatedReviewId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ReviewId") + .HasColumnType("int"); + + b.Property("Thumb") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("RatedReviewId"); + + b.HasIndex("ReviewId"); + + b.HasIndex("UserId"); + + b.ToTable("RatedReviews"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.Review", b => + { + b.Property("ReviewId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Deleted") + .HasColumnType("tinyint(1)"); + + b.Property("DeletedBy") + .HasColumnType("int"); + + b.Property("LabelCollection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ReviewerId") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Thumb") + .HasColumnType("int"); + + b.Property("ThumbsDown") + .HasColumnType("int"); + + b.Property("ThumbsUp") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("ReviewId"); + + b.HasIndex("ReviewerId"); + + b.HasIndex("SlotId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.WebToken", b => { b.Property("TokenId") .ValueGeneratedOnAdd() @@ -797,9 +811,115 @@ namespace ProjectLighthouse.Migrations b.ToTable("WebTokens"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Administration.Reports.GriefReport", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "ReportingPlayer") + .WithMany() + .HasForeignKey("ReportingPlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ReportingPlayer"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.GameToken", "GameToken") .WithMany() .HasForeignKey("GameTokenId") .OnDelete(DeleteBehavior.Cascade) @@ -808,9 +928,9 @@ namespace ProjectLighthouse.Migrations b.Navigation("GameToken"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.GameToken", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -819,15 +939,70 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Photo", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email.EmailSetToken", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email.EmailVerificationToken", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "HeartedUser") .WithMany() .HasForeignKey("HeartedUserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -838,115 +1013,31 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.LastContact", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Slot"); - b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Slot"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Slot"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") - .WithMany() - .HasForeignKey("CreatorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Location", "Location") .WithMany() .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Creator"); - b.Navigation("Location"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.UserApprovedIpAddress", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Slot"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") - .WithMany() - .HasForeignKey("CreatorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Creator"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -955,70 +1046,15 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.RatedReview", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") - .WithMany() - .HasForeignKey("PosterUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Poster"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Email.EmailSetToken", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Email.EmailVerificationToken", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastContact", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reports.GriefReport", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "ReportingPlayer") - .WithMany() - .HasForeignKey("ReportingPlayerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ReportingPlayer"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Reviews.Review", "Review") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Reviews.Review", "Review") .WithMany() .HasForeignKey("ReviewId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -1029,15 +1065,15 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.Review", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Reviewer") + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Reviewer") .WithMany() .HasForeignKey("ReviewerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") .WithMany() .HasForeignKey("SlotId") .OnDelete(DeleteBehavior.Cascade) @@ -1048,9 +1084,9 @@ namespace ProjectLighthouse.Migrations b.Navigation("Slot"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Score", b => { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot") .WithMany() .HasForeignKey("SlotId") .OnDelete(DeleteBehavior.Cascade) @@ -1058,28 +1094,6 @@ namespace ProjectLighthouse.Migrations b.Navigation("Slot"); }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") - .WithMany() - .HasForeignKey("LocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Location"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.UserApprovedIpAddress", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); #pragma warning restore 612, 618 } } diff --git a/ProjectLighthouse/Pages/LandingPage.cshtml b/ProjectLighthouse/Pages/LandingPage.cshtml deleted file mode 100644 index 99b4484f..00000000 --- a/ProjectLighthouse/Pages/LandingPage.cshtml +++ /dev/null @@ -1,44 +0,0 @@ -@page "/" -@using LBPUnion.ProjectLighthouse.Localization.StringLists -@using LBPUnion.ProjectLighthouse.Types -@using LBPUnion.ProjectLighthouse.Types.Settings -@model LBPUnion.ProjectLighthouse.Pages.LandingPage - -@{ - Layout = "Layouts/BaseLayout"; - Model.ShowTitleInPage = false; -} -

@Model.Translate(LandingPageStrings.Welcome)

- -@if (Model.User != null) -{ -

@string.Format(Model.Translate(LandingPageStrings.LoggedInAs), $"{Model.User.Username}")

// TODO: make this bold - if (ServerSettings.Instance.UseExternalAuth && Model.AuthenticationAttemptsCount > 0) - { -

- You have @Model.AuthenticationAttemptsCount authentication attempts pending. Click here to view them. -

- } -} - -@if (Model.PlayersOnlineCount == 1) -{ -

@Model.Translate(LandingPageStrings.UsersSingle)

- @foreach (User user in Model.PlayersOnline) - { - @user.Username - } -} - -else if (Model.PlayersOnlineCount == 0) -{ -

@Model.Translate(LandingPageStrings.UsersNone)

-} -else -{ -

There are currently @Model.PlayersOnlineCount users online:

- @foreach (User user in Model.PlayersOnline) - { - @user.Username - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LandingPage.cshtml.cs b/ProjectLighthouse/Pages/LandingPage.cshtml.cs deleted file mode 100644 index 0d23a58e..00000000 --- a/ProjectLighthouse/Pages/LandingPage.cshtml.cs +++ /dev/null @@ -1,42 +0,0 @@ -#nullable enable -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Pages.Layouts; -using LBPUnion.ProjectLighthouse.Types; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace LBPUnion.ProjectLighthouse.Pages; - -public class LandingPage : BaseLayout -{ - - public int AuthenticationAttemptsCount; - public List PlayersOnline; - - public int PlayersOnlineCount; - public LandingPage(Database database) : base(database) - {} - - [UsedImplicitly] - public async Task OnGet() - { - User? user = this.Database.UserFromWebRequest(this.Request); - if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); - - this.PlayersOnlineCount = await StatisticsHelper.RecentMatches(); - - if (user != null) - this.AuthenticationAttemptsCount = await this.Database.AuthenticationAttempts.Include - (a => a.GameToken) - .CountAsync(a => a.GameToken.UserId == user.UserId); - - List userIds = await this.Database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync(); - - this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync(); - return this.Page(); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml b/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml deleted file mode 100644 index e1891722..00000000 --- a/ProjectLighthouse/Pages/Partials/CaptchaPartial.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -@using LBPUnion.ProjectLighthouse.Types.Settings -@if (ServerSettings.Instance.HCaptchaEnabled) -{ -
- -} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/SlotsPage.cshtml.cs b/ProjectLighthouse/Pages/SlotsPage.cshtml.cs deleted file mode 100644 index 4687223a..00000000 --- a/ProjectLighthouse/Pages/SlotsPage.cshtml.cs +++ /dev/null @@ -1,53 +0,0 @@ -#nullable enable -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Pages.Layouts; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace LBPUnion.ProjectLighthouse.Pages; - -public class SlotsPage : BaseLayout -{ - - public int PageAmount; - - public int PageNumber; - - public int SlotCount; - - public List Slots; - - public string SearchValue; - - public SlotsPage([NotNull] Database database) : base(database) - {} - - public async Task OnGet([FromRoute] int pageNumber, [FromQuery] string? name) - { - if (string.IsNullOrWhiteSpace(name)) name = ""; - - this.SearchValue = name.Trim(); - - this.SlotCount = await this.Database.Slots.CountAsync(p => p.Name.Contains(this.SearchValue)); - - this.PageNumber = pageNumber; - this.PageAmount = Math.Max(1, (int)Math.Ceiling((double)this.SlotCount / ServerStatics.PageSize)); - - if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); - - this.Slots = await this.Database.Slots.Include(p => p.Creator) - .Where(p => p.Name.Contains(this.SearchValue)) - .OrderByDescending(p => p.FirstUploaded) - .Skip(pageNumber * ServerStatics.PageSize) - .Take(ServerStatics.PageSize) - .ToListAsync(); - - return this.Page(); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Types/AuthenticationAttempt.cs b/ProjectLighthouse/PlayerData/AuthenticationAttempt.cs similarity index 90% rename from ProjectLighthouse/Types/AuthenticationAttempt.cs rename to ProjectLighthouse/PlayerData/AuthenticationAttempt.cs index 76c45278..96eefad0 100644 --- a/ProjectLighthouse/Types/AuthenticationAttempt.cs +++ b/ProjectLighthouse/PlayerData/AuthenticationAttempt.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; public class AuthenticationAttempt { diff --git a/ProjectLighthouse/Types/GameToken.cs b/ProjectLighthouse/PlayerData/GameToken.cs similarity index 86% rename from ProjectLighthouse/Types/GameToken.cs rename to ProjectLighthouse/PlayerData/GameToken.cs index 0e57d7fd..dd83924a 100644 --- a/ProjectLighthouse/Types/GameToken.cs +++ b/ProjectLighthouse/PlayerData/GameToken.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; public class GameToken { diff --git a/ProjectLighthouse/Types/GameVersion.cs b/ProjectLighthouse/PlayerData/GameVersion.cs similarity index 88% rename from ProjectLighthouse/Types/GameVersion.cs rename to ProjectLighthouse/PlayerData/GameVersion.cs index 8e5c8b6b..29c72684 100644 --- a/ProjectLighthouse/Types/GameVersion.cs +++ b/ProjectLighthouse/PlayerData/GameVersion.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; public enum GameVersion { diff --git a/ProjectLighthouse/Helpers/LastContactHelper.cs b/ProjectLighthouse/PlayerData/LastContactHelper.cs similarity index 82% rename from ProjectLighthouse/Helpers/LastContactHelper.cs rename to ProjectLighthouse/PlayerData/LastContactHelper.cs index 8f76bf22..28c9ae6c 100644 --- a/ProjectLighthouse/Helpers/LastContactHelper.cs +++ b/ProjectLighthouse/PlayerData/LastContactHelper.cs @@ -1,11 +1,11 @@ #nullable enable using System.Linq; using System.Threading.Tasks; -using LBPUnion.ProjectLighthouse.Types; -using LBPUnion.ProjectLighthouse.Types.Profiles; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Helpers; +namespace LBPUnion.ProjectLighthouse.PlayerData; public static class LastContactHelper { @@ -26,7 +26,7 @@ public static class LastContactHelper database.LastContacts.Add(lastContact); } - lastContact.Timestamp = TimestampHelper.Timestamp; + lastContact.Timestamp = TimeHelper.Timestamp; lastContact.GameVersion = gameVersion; lastContact.Platform = platform; diff --git a/ProjectLighthouse/Types/LoginResult.cs b/ProjectLighthouse/PlayerData/LoginResult.cs similarity index 59% rename from ProjectLighthouse/Types/LoginResult.cs rename to ProjectLighthouse/PlayerData/LoginResult.cs index 274559b7..09842ac3 100644 --- a/ProjectLighthouse/Types/LoginResult.cs +++ b/ProjectLighthouse/PlayerData/LoginResult.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; /// /// Response to POST /login @@ -15,9 +15,12 @@ public class LoginResult public string AuthTicket { get; set; } [XmlElement("lbpEnvVer")] - public string LbpEnvVer { get; set; } + public string ServerBrand { get; set; } public string Serialize() => LbpSerializer.Elements - (new KeyValuePair("authTicket", this.AuthTicket), new KeyValuePair("lbpEnvVer", this.LbpEnvVer)); + ( + new KeyValuePair("authTicket", this.AuthTicket), + new KeyValuePair("lbpEnvVer", this.ServerBrand) + ); } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Photo.cs b/ProjectLighthouse/PlayerData/Photo.cs similarity index 96% rename from ProjectLighthouse/Types/Photo.cs rename to ProjectLighthouse/PlayerData/Photo.cs index 5d84ca5d..8a8ca224 100644 --- a/ProjectLighthouse/Types/Photo.cs +++ b/ProjectLighthouse/PlayerData/Photo.cs @@ -5,10 +5,11 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; [XmlRoot("photo")] [XmlType("photo")] diff --git a/ProjectLighthouse/Types/PhotoSubject.cs b/ProjectLighthouse/PlayerData/PhotoSubject.cs similarity index 91% rename from ProjectLighthouse/Types/PhotoSubject.cs rename to ProjectLighthouse/PlayerData/PhotoSubject.cs index bf8d71e6..4257743e 100644 --- a/ProjectLighthouse/Types/PhotoSubject.cs +++ b/ProjectLighthouse/PlayerData/PhotoSubject.cs @@ -3,9 +3,10 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; // [XmlRoot("subject")] [XmlType("subject")] diff --git a/ProjectLighthouse/Types/Platform.cs b/ProjectLighthouse/PlayerData/Platform.cs similarity index 70% rename from ProjectLighthouse/Types/Platform.cs rename to ProjectLighthouse/PlayerData/Platform.cs index b863f285..bbb3d372 100644 --- a/ProjectLighthouse/Types/Platform.cs +++ b/ProjectLighthouse/PlayerData/Platform.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; public enum Platform { diff --git a/ProjectLighthouse/Types/Profiles/ClientsConnected.cs b/ProjectLighthouse/PlayerData/Profiles/ClientsConnected.cs similarity index 92% rename from ProjectLighthouse/Types/Profiles/ClientsConnected.cs rename to ProjectLighthouse/PlayerData/Profiles/ClientsConnected.cs index d54590d0..b0b9c57a 100644 --- a/ProjectLighthouse/Types/Profiles/ClientsConnected.cs +++ b/ProjectLighthouse/PlayerData/Profiles/ClientsConnected.cs @@ -1,7 +1,7 @@ using LBPUnion.ProjectLighthouse.Serialization; using Microsoft.EntityFrameworkCore; -namespace LBPUnion.ProjectLighthouse.Types.Profiles; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; [Keyless] public class ClientsConnected diff --git a/ProjectLighthouse/Types/Profiles/Comment.cs b/ProjectLighthouse/PlayerData/Profiles/Comment.cs similarity index 96% rename from ProjectLighthouse/Types/Profiles/Comment.cs rename to ProjectLighthouse/PlayerData/Profiles/Comment.cs index d263c091..36782a60 100644 --- a/ProjectLighthouse/Types/Profiles/Comment.cs +++ b/ProjectLighthouse/PlayerData/Profiles/Comment.cs @@ -3,8 +3,9 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; +using LBPUnion.ProjectLighthouse.Types; -namespace LBPUnion.ProjectLighthouse.Types.Profiles; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; [XmlRoot("comment")] [XmlType("comment")] diff --git a/ProjectLighthouse/Types/Profiles/Email/EmailSetToken.cs b/ProjectLighthouse/PlayerData/Profiles/Email/EmailSetToken.cs similarity index 83% rename from ProjectLighthouse/Types/Profiles/Email/EmailSetToken.cs rename to ProjectLighthouse/PlayerData/Profiles/Email/EmailSetToken.cs index 0b3258af..152e42ed 100644 --- a/ProjectLighthouse/Types/Profiles/Email/EmailSetToken.cs +++ b/ProjectLighthouse/PlayerData/Profiles/Email/EmailSetToken.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Profiles.Email; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email; public class EmailSetToken { diff --git a/ProjectLighthouse/Types/Profiles/Email/EmailVerificationToken.cs b/ProjectLighthouse/PlayerData/Profiles/Email/EmailVerificationToken.cs similarity index 84% rename from ProjectLighthouse/Types/Profiles/Email/EmailVerificationToken.cs rename to ProjectLighthouse/PlayerData/Profiles/Email/EmailVerificationToken.cs index 0604d2e5..61559cce 100644 --- a/ProjectLighthouse/Types/Profiles/Email/EmailVerificationToken.cs +++ b/ProjectLighthouse/PlayerData/Profiles/Email/EmailVerificationToken.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Profiles.Email; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles.Email; public class EmailVerificationToken { diff --git a/ProjectLighthouse/Types/HeartedProfile.cs b/ProjectLighthouse/PlayerData/Profiles/HeartedProfile.cs similarity index 91% rename from ProjectLighthouse/Types/HeartedProfile.cs rename to ProjectLighthouse/PlayerData/Profiles/HeartedProfile.cs index f60fcee5..04a2dc92 100644 --- a/ProjectLighthouse/Types/HeartedProfile.cs +++ b/ProjectLighthouse/PlayerData/Profiles/HeartedProfile.cs @@ -2,7 +2,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; public class HeartedProfile { diff --git a/ProjectLighthouse/Types/Profiles/LastContact.cs b/ProjectLighthouse/PlayerData/Profiles/LastContact.cs similarity index 87% rename from ProjectLighthouse/Types/Profiles/LastContact.cs rename to ProjectLighthouse/PlayerData/Profiles/LastContact.cs index 5e7b1fea..4f90d32b 100644 --- a/ProjectLighthouse/Types/Profiles/LastContact.cs +++ b/ProjectLighthouse/PlayerData/Profiles/LastContact.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types.Profiles; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; public class LastContact { diff --git a/ProjectLighthouse/Types/Profiles/Location.cs b/ProjectLighthouse/PlayerData/Profiles/Location.cs similarity index 89% rename from ProjectLighthouse/Types/Profiles/Location.cs rename to ProjectLighthouse/PlayerData/Profiles/Location.cs index 6d07d62f..a0428f91 100644 --- a/ProjectLighthouse/Types/Profiles/Location.cs +++ b/ProjectLighthouse/PlayerData/Profiles/Location.cs @@ -1,7 +1,7 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Profiles; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; /// /// The location of a slot on a planet. diff --git a/ProjectLighthouse/Types/Profiles/NPData.cs b/ProjectLighthouse/PlayerData/Profiles/NPData.cs similarity index 85% rename from ProjectLighthouse/Types/Profiles/NPData.cs rename to ProjectLighthouse/PlayerData/Profiles/NPData.cs index ed52fe1e..80d82775 100644 --- a/ProjectLighthouse/Types/Profiles/NPData.cs +++ b/ProjectLighthouse/PlayerData/Profiles/NPData.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Xml.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Profiles; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; [XmlRoot("npdata")] [XmlType("npdata")] diff --git a/ProjectLighthouse/Types/Profiles/Pins.cs b/ProjectLighthouse/PlayerData/Profiles/Pins.cs similarity index 83% rename from ProjectLighthouse/Types/Profiles/Pins.cs rename to ProjectLighthouse/PlayerData/Profiles/Pins.cs index f1ed3ef6..abdee779 100644 --- a/ProjectLighthouse/Types/Profiles/Pins.cs +++ b/ProjectLighthouse/PlayerData/Profiles/Pins.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace LBPUnion.ProjectLighthouse.Types.Profiles; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; public class Pins { diff --git a/ProjectLighthouse/Types/Profiles/StatusType.cs b/ProjectLighthouse/PlayerData/Profiles/StatusType.cs similarity index 50% rename from ProjectLighthouse/Types/Profiles/StatusType.cs rename to ProjectLighthouse/PlayerData/Profiles/StatusType.cs index ea7d99a6..eae84e8c 100644 --- a/ProjectLighthouse/Types/Profiles/StatusType.cs +++ b/ProjectLighthouse/PlayerData/Profiles/StatusType.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Profiles; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; public enum StatusType { diff --git a/ProjectLighthouse/Types/User.cs b/ProjectLighthouse/PlayerData/Profiles/User.cs similarity index 80% rename from ProjectLighthouse/Types/User.cs rename to ProjectLighthouse/PlayerData/Profiles/User.cs index c64ea394..300345d2 100644 --- a/ProjectLighthouse/Types/User.cs +++ b/ProjectLighthouse/PlayerData/Profiles/User.cs @@ -3,17 +3,19 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types.Profiles; -using LBPUnion.ProjectLighthouse.Types.Settings; +using LBPUnion.ProjectLighthouse.Types; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; public class User { + #nullable enable [NotMapped] [JsonIgnore] private Database? _database; + #nullable disable [NotMapped] [JsonIgnore] @@ -62,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; } @@ -70,19 +72,19 @@ public class User [NotMapped] [JsonIgnore] - public int Reviews => database.Reviews.Count(r => r.ReviewerId == this.UserId); + public int Reviews => this.database.Reviews.Count(r => r.ReviewerId == this.UserId); [NotMapped] [JsonIgnore] - public int Comments => database.Comments.Count(c => c.Type == CommentType.Profile && c.TargetId == this.UserId); + public int Comments => this.database.Comments.Count(c => c.Type == CommentType.Profile && c.TargetId == this.UserId); [NotMapped] [JsonIgnore] - public int PhotosByMe => database.Photos.Count(p => p.CreatorId == this.UserId); + public int PhotosByMe => this.database.Photos.Count(p => p.CreatorId == this.UserId); [NotMapped] [JsonIgnore] - public int PhotosWithMe => Enumerable.Sum(database.Photos, photo => photo.Subjects.Count(subject => subject.User.UserId == this.UserId)); + public int PhotosWithMe => Enumerable.Sum(this.database.Photos, photo => photo.Subjects.Count(subject => subject.User.UserId == this.UserId)); [JsonIgnore] public int LocationId { get; set; } @@ -96,15 +98,15 @@ public class User [NotMapped] [JsonIgnore] - public int HeartedLevels => database.HeartedLevels.Count(p => p.UserId == this.UserId); + public int HeartedLevels => this.database.HeartedLevels.Count(p => p.UserId == this.UserId); [NotMapped] [JsonIgnore] - public int HeartedUsers => database.HeartedProfiles.Count(p => p.UserId == this.UserId); + public int HeartedUsers => this.database.HeartedProfiles.Count(p => p.UserId == this.UserId); [NotMapped] [JsonIgnore] - public int QueuedLevels => database.QueuedLevels.Count(p => p.UserId == this.UserId); + public int QueuedLevels => this.database.QueuedLevels.Count(p => p.UserId == this.UserId); [JsonIgnore] public string Pins { get; set; } = ""; @@ -119,7 +121,7 @@ public class User public string PlanetHashLBPVita { get; set; } = ""; [JsonIgnore] - public int Hearts => database.HeartedProfiles.Count(s => s.HeartedUserId == this.UserId); + public int Hearts => this.database.HeartedProfiles.Count(s => s.HeartedUserId == this.UserId); [JsonIgnore] public bool IsAdmin { get; set; } = false; @@ -133,7 +135,7 @@ public class User [NotMapped] [JsonIgnore] - public UserStatus Status => new(database, this.UserId); + public UserStatus Status => new(this.database, this.UserId); [JsonIgnore] public bool Banned { get; set; } @@ -147,19 +149,23 @@ 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) + LbpSerializer.StringElement("lolcatftwCount", this.QueuedLevels) + LbpSerializer.StringElement("pins", this.Pins) + - serializeEarth(gameVersion) + + this.serializeEarth(gameVersion) + LbpSerializer.BlankElement("photos") + LbpSerializer.StringElement("heartCount", this.Hearts) + LbpSerializer.StringElement("yay2", this.YayHash) + @@ -191,18 +197,18 @@ public class User /// [NotMapped] [JsonIgnore] - public int UsedSlots => database.Slots.Count(s => s.CreatorId == this.UserId); + public int UsedSlots => this.database.Slots.Count(s => s.CreatorId == this.UserId); #nullable enable public int GetUsedSlotsForGame(GameVersion version) { - return database.Slots.Count(s => s.CreatorId == this.UserId && s.GameVersion == version); + return this.database.Slots.Count(s => s.CreatorId == this.UserId && s.GameVersion == version); } #nullable disable [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 diff --git a/ProjectLighthouse/Types/UserApprovedIpAddress.cs b/ProjectLighthouse/PlayerData/Profiles/UserApprovedIpAddress.cs similarity index 85% rename from ProjectLighthouse/Types/UserApprovedIpAddress.cs rename to ProjectLighthouse/PlayerData/Profiles/UserApprovedIpAddress.cs index 577e159e..cbb4179c 100644 --- a/ProjectLighthouse/Types/UserApprovedIpAddress.cs +++ b/ProjectLighthouse/PlayerData/Profiles/UserApprovedIpAddress.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; public class UserApprovedIpAddress { diff --git a/ProjectLighthouse/PlayerData/Profiles/UserFriendData.cs b/ProjectLighthouse/PlayerData/Profiles/UserFriendData.cs new file mode 100644 index 00000000..cbc29cc5 --- /dev/null +++ b/ProjectLighthouse/PlayerData/Profiles/UserFriendData.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Redis.OM.Modeling; + +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; + +[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")] +[Document(StorageType = StorageType.Json)] +public class UserFriendData +{ + private int userId; + + [Indexed] + public int UserId { + get => this.userId; + set { + this.RedisId = value.ToString(); + this.userId = value; + } + } + + [RedisIdField] + public string RedisId { get; set; } + + [Indexed] + public List FriendIds { get; set; } + + [Indexed] + public List BlockedIds { get; set; } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/UserStatus.cs b/ProjectLighthouse/PlayerData/Profiles/UserStatus.cs similarity index 58% rename from ProjectLighthouse/Types/Profiles/UserStatus.cs rename to ProjectLighthouse/PlayerData/Profiles/UserStatus.cs index c9192a35..91c0083a 100644 --- a/ProjectLighthouse/Types/Profiles/UserStatus.cs +++ b/ProjectLighthouse/PlayerData/Profiles/UserStatus.cs @@ -1,9 +1,9 @@ #nullable enable using System.Linq; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Types.Match; +using LBPUnion.ProjectLighthouse.Match.Rooms; -namespace LBPUnion.ProjectLighthouse.Types.Profiles; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; public class UserStatus { @@ -17,27 +17,27 @@ public class UserStatus public UserStatus(Database database, int userId) { - LastContact? lastContact = database.LastContacts.Where(l => l.UserId == userId).FirstOrDefault(l => TimestampHelper.Timestamp - l.Timestamp < 300); + LastContact? lastContact = database.LastContacts.Where(l => l.UserId == userId).FirstOrDefault(l => TimeHelper.Timestamp - l.Timestamp < 300); if (lastContact == null) { - StatusType = StatusType.Offline; - CurrentVersion = null; + this.StatusType = StatusType.Offline; + this.CurrentVersion = null; } else { - StatusType = StatusType.Online; - CurrentVersion = lastContact.GameVersion; - CurrentPlatform = lastContact.Platform; + this.StatusType = StatusType.Online; + this.CurrentVersion = lastContact.GameVersion; + this.CurrentPlatform = lastContact.Platform; } - CurrentRoom = RoomHelper.FindRoomByUserId(userId); + this.CurrentRoom = RoomHelper.FindRoomByUserId(userId); } public override string ToString() { - CurrentVersion ??= GameVersion.Unknown; - CurrentPlatform ??= Platform.Unknown; + this.CurrentVersion ??= GameVersion.Unknown; + this.CurrentPlatform ??= Platform.Unknown; return this.StatusType switch { StatusType.Online => $"Currently online on {((GameVersion)this.CurrentVersion).ToPrettyString()} on {((Platform)this.CurrentPlatform)}", diff --git a/ProjectLighthouse/Types/UserUpdate.cs b/ProjectLighthouse/PlayerData/Profiles/UserUpdate.cs similarity index 85% rename from ProjectLighthouse/Types/UserUpdate.cs rename to ProjectLighthouse/PlayerData/Profiles/UserUpdate.cs index ef609967..f25e29dd 100644 --- a/ProjectLighthouse/Types/UserUpdate.cs +++ b/ProjectLighthouse/PlayerData/Profiles/UserUpdate.cs @@ -1,8 +1,7 @@ #nullable enable using System.Xml.Serialization; -using LBPUnion.ProjectLighthouse.Types.Profiles; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; public class UserUpdate { diff --git a/ProjectLighthouse/Types/Reaction.cs b/ProjectLighthouse/PlayerData/Reaction.cs similarity index 82% rename from ProjectLighthouse/Types/Reaction.cs rename to ProjectLighthouse/PlayerData/Reaction.cs index 40d8d624..48fc7f25 100644 --- a/ProjectLighthouse/Types/Reaction.cs +++ b/ProjectLighthouse/PlayerData/Reaction.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; public class Reaction { diff --git a/ProjectLighthouse/Types/Reviews/RatedReview.cs b/ProjectLighthouse/PlayerData/Reviews/RatedReview.cs similarity index 81% rename from ProjectLighthouse/Types/Reviews/RatedReview.cs rename to ProjectLighthouse/PlayerData/Reviews/RatedReview.cs index e77530fb..a1bb1e89 100644 --- a/ProjectLighthouse/Types/Reviews/RatedReview.cs +++ b/ProjectLighthouse/PlayerData/Reviews/RatedReview.cs @@ -1,7 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; -namespace LBPUnion.ProjectLighthouse.Types.Reviews; +namespace LBPUnion.ProjectLighthouse.PlayerData.Reviews; public class RatedReview { diff --git a/ProjectLighthouse/Types/Reviews/Review.cs b/ProjectLighthouse/PlayerData/Reviews/Review.cs similarity index 93% rename from ProjectLighthouse/Types/Reviews/Review.cs rename to ProjectLighthouse/PlayerData/Reviews/Review.cs index 8bc7b620..6843df3c 100644 --- a/ProjectLighthouse/Types/Reviews/Review.cs +++ b/ProjectLighthouse/PlayerData/Reviews/Review.cs @@ -2,10 +2,12 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Administration; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types.Reviews; +namespace LBPUnion.ProjectLighthouse.PlayerData.Reviews; [XmlRoot("review")] [XmlType("review")] diff --git a/ProjectLighthouse/Types/Score.cs b/ProjectLighthouse/PlayerData/Score.cs similarity index 94% rename from ProjectLighthouse/Types/Score.cs rename to ProjectLighthouse/PlayerData/Score.cs index 85e569cf..67ad2601 100644 --- a/ProjectLighthouse/Types/Score.cs +++ b/ProjectLighthouse/PlayerData/Score.cs @@ -1,10 +1,10 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Levels; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types.Levels; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; [XmlRoot("playRecord")] [XmlType("playRecord")] diff --git a/ProjectLighthouse/Types/WebToken.cs b/ProjectLighthouse/PlayerData/WebToken.cs similarity index 83% rename from ProjectLighthouse/Types/WebToken.cs rename to ProjectLighthouse/PlayerData/WebToken.cs index fdcd6d5b..7860a418 100644 --- a/ProjectLighthouse/Types/WebToken.cs +++ b/ProjectLighthouse/PlayerData/WebToken.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace LBPUnion.ProjectLighthouse.Types; +namespace LBPUnion.ProjectLighthouse.PlayerData; public class WebToken { diff --git a/ProjectLighthouse/Program.cs b/ProjectLighthouse/Program.cs deleted file mode 100644 index 8f1a64d0..00000000 --- a/ProjectLighthouse/Program.cs +++ /dev/null @@ -1,114 +0,0 @@ -#nullable enable -using System; -using System.Diagnostics; -using Kettu; -using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types.Settings; -using Microsoft.AspNetCore.Hosting; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace LBPUnion.ProjectLighthouse; - -public static class Program -{ - public static void Main(string[] args) - { - // Log startup time - Stopwatch stopwatch = new(); - stopwatch.Start(); - - // Setup logging - - Logger.StartLogging(); - Logger.UpdateRate /= 2; - LoggerLine.LogFormat = "[{0}] {1}"; - Logger.AddLogger(new ConsoleLogger()); - Logger.AddLogger(new LighthouseFileLogger()); - - Logger.Log("Welcome to Project Lighthouse!", LoggerLevelStartup.Instance); - Logger.Log($"You are running version {VersionHelper.FullVersion}", LoggerLevelStartup.Instance); - - // Referencing ServerSettings.Instance here loads the config, see ServerSettings.cs for more information - Logger.Log("Loaded config file version " + ServerSettings.Instance.ConfigVersion, LoggerLevelStartup.Instance); - - Logger.Log("Determining if the database is available...", LoggerLevelStartup.Instance); - bool dbConnected = ServerStatics.DbConnected; - Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance); - - if (!dbConnected) Environment.Exit(1); - using Database database = new(); - - Logger.Log("Migrating database...", LoggerLevelDatabase.Instance); - MigrateDatabase(database); - - if (ServerSettings.Instance.InfluxEnabled) - { - Logger.Log("Influx logging is enabled. Starting influx logging...", LoggerLevelStartup.Instance); - InfluxHelper.StartLogging().Wait(); - if (ServerSettings.Instance.InfluxLoggingEnabled) Logger.AddLogger(new InfluxLogger()); - } - - #if DEBUG - Logger.Log - ( - "This is a debug build, so performance may suffer! " + - "If you are running Lighthouse in a production environment, " + - "it is highly recommended to run a release build. ", - LoggerLevelStartup.Instance - ); - Logger.Log("You can do so by running any dotnet command with the flag: \"-c Release\". ", LoggerLevelStartup.Instance); - #endif - - if (args.Length != 0) - { - MaintenanceHelper.RunCommand(args).Wait(); - return; - } - - if (ServerSettings.Instance.ConvertAssetsOnStartup) FileHelper.ConvertAllTexturesToPng(); - - Logger.Log("Starting room cleanup thread...", LoggerLevelStartup.Instance); - RoomHelper.StartCleanupThread(); - - stopwatch.Stop(); - Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance); - - CreateHostBuilder(args).Build().Run(); - } - - public static void MigrateDatabase(Database database) - { - Stopwatch stopwatch = new(); - stopwatch.Start(); - - database.Database.MigrateAsync().Wait(); - - stopwatch.Stop(); - Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); - } - - public static IHostBuilder CreateHostBuilder(string[] args) - => Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults - ( - webBuilder => - { - webBuilder.UseStartup(); - webBuilder.UseWebRoot("StaticFiles"); - webBuilder.UseUrls(ServerSettings.Instance.ServerListenUrl); - } - ) - .ConfigureLogging - ( - logging => - { - logging.ClearProviders(); - logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); - } - ); -} \ No newline at end of file diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index 72987f69..f823faf0 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -4,30 +4,27 @@ net6.0 LBPUnion.ProjectLighthouse LBPUnion.ProjectLighthouse - - - - true - $(NoWarn);1591 + Library + false - - - - - - - - - - + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + + + @@ -43,30 +40,28 @@ Always - + Always - + Always - - - + - + - - - - + + + + diff --git a/ProjectLighthouse/Startup/DebugWarmupLifetime.cs b/ProjectLighthouse/Startup/DebugWarmupLifetime.cs index bfe4e7c3..2161dbf4 100644 --- a/ProjectLighthouse/Startup/DebugWarmupLifetime.cs +++ b/ProjectLighthouse/Startup/DebugWarmupLifetime.cs @@ -2,9 +2,10 @@ using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types.Settings; +using LBPUnion.ProjectLighthouse.Types; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting.Internal; using Microsoft.Extensions.Logging; @@ -37,21 +38,28 @@ public class DebugWarmupLifetime : IHostLifetime { using HttpClient client = new(); - string url = ServerSettings.Instance.ServerListenUrl; + string url = ServerStatics.ServerType switch + { + ServerType.GameServer => ServerConfiguration.Instance.GameApiListenUrl, + ServerType.Website => ServerConfiguration.Instance.WebsiteListenUrl, + ServerType.Api => ServerConfiguration.Instance.ApiListenUrl, + _ => throw new ArgumentOutOfRangeException(), + }; + url = url.Replace("0.0.0.0", "127.0.0.1"); - Logger.Log("Warming up Hot Reload...", LoggerLevelStartup.Instance); + Logger.Debug("Warming up Hot Reload...", LogArea.Startup); try { client.GetAsync(url).Wait(); } catch(Exception e) { - Logger.Log("An error occurred while attempting to warm up hot reload. Initial page load will be delayed.", LoggerLevelStartup.Instance); - Logger.Log(e.Message, LoggerLevelStartup.Instance); + Logger.Debug("An error occurred while attempting to warm up hot reload. Initial page load will be delayed.", LogArea.Startup); + Logger.Debug(e.ToDetailedException(), LogArea.Startup); return; } - Logger.Log("Hot Reload is ready to go!", LoggerLevelStartup.Instance); + Logger.Success("Hot Reload is ready to go!", LogArea.Startup); } public Task StopAsync(CancellationToken cancellationToken) => this.consoleLifetime.StopAsync(cancellationToken); diff --git a/ProjectLighthouse/Startup/TestStartup.cs b/ProjectLighthouse/Startup/TestStartup.cs deleted file mode 100644 index 6370f1c0..00000000 --- a/ProjectLighthouse/Startup/TestStartup.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LBPUnion.ProjectLighthouse.Helpers.Middlewares; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; - -namespace LBPUnion.ProjectLighthouse.Startup; - -public class TestStartup : Startup -{ - public TestStartup(IConfiguration configuration) : base(configuration) - {} - - public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - app.UseMiddleware(); - base.Configure(app, env); - } -} \ No newline at end of file diff --git a/ProjectLighthouse/StartupTasks.cs b/ProjectLighthouse/StartupTasks.cs new file mode 100644 index 00000000..95497e90 --- /dev/null +++ b/ProjectLighthouse/StartupTasks.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using LBPUnion.ProjectLighthouse.Administration; +using LBPUnion.ProjectLighthouse.Administration.Maintenance; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.Files; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Logging.Loggers; +using LBPUnion.ProjectLighthouse.Match.Rooms; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using LBPUnion.ProjectLighthouse.Startup; +using LBPUnion.ProjectLighthouse.StorableLists; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse; + +public static class StartupTasks +{ + public static void Run(string[] args, ServerType serverType) + { + // Log startup time + Stopwatch stopwatch = new(); + stopwatch.Start(); + + ServerStatics.ServerType = serverType; + + // Setup logging + Logger.Instance.AddLogger(new ConsoleLogger()); + Logger.Instance.AddLogger(new FileLogger()); + + Logger.Info($"Welcome to the Project Lighthouse {serverType.ToString()}!", LogArea.Startup); + Logger.Info($"You are running version {VersionHelper.FullVersion}", LogArea.Startup); + + // Referencing ServerConfiguration.Instance here loads the config, see ServerConfiguration.cs for more information + Logger.Success("Loaded config file version " + ServerConfiguration.Instance.ConfigVersion, LogArea.Startup); + + Logger.Info("Connecting to the database...", LogArea.Startup); + bool dbConnected = ServerStatics.DbConnected; + if (!dbConnected) + { + Logger.Error("Database unavailable! Exiting.", LogArea.Startup); + } + else + { + Logger.Success("Connected to the database!", LogArea.Startup); + } + + if (!dbConnected) Environment.Exit(1); + using Database database = new(); + + #if !DEBUG + if(serverType == ServerType.GameServer) + #endif + migrateDatabase(database); + + if (ServerConfiguration.Instance.InfluxDB.InfluxEnabled) + { + Logger.Info("Influx logging is enabled. Starting influx logging...", LogArea.Startup); + InfluxHelper.StartLogging().Wait(); + if (ServerConfiguration.Instance.InfluxDB.LoggingEnabled) Logger.Instance.AddLogger(new InfluxLogger()); + } + + Logger.Debug + ( + "This is a debug build, so performance may suffer! " + + "If you are running Lighthouse in a production environment, " + + "it is highly recommended to run a release build. ", + LogArea.Startup + ); + Logger.Debug("You can do so by running any dotnet command with the flag: \"-c Release\". ", LogArea.Startup); + + if (args.Length != 0) + { + List logLines = MaintenanceHelper.RunCommand(args).Result; + Console.WriteLine(logLines.ToLogString()); + return; + } + + if (ServerConfiguration.Instance.WebsiteConfiguration.ConvertAssetsOnStartup + && serverType == ServerType.Website) + { + FileHelper.ConvertAllTexturesToPng(); + } + + Logger.Info("Initializing Redis...", LogArea.Startup); + RedisDatabase.Initialize().Wait(); + + if (serverType == ServerType.GameServer) + { + Logger.Info("Starting room cleanup thread...", LogArea.Startup); + RoomHelper.StartCleanupThread(); + } + + // Create admin user if no users exist + if (serverType == ServerType.Website && database.Users.CountAsync().Result == 0) + { + const string passwordClear = "lighthouse"; + string password = CryptoHelper.BCryptHash(CryptoHelper.Sha256Hash(passwordClear)); + + User admin = database.CreateUser("admin", password).Result; + admin.IsAdmin = true; + admin.PasswordResetRequired = true; + + database.SaveChanges(); + + Logger.Success("No users were found, so an admin user was created. " + + $"The username is 'admin' and the password is '{passwordClear}'.", LogArea.Startup); + } + + stopwatch.Stop(); + Logger.Success($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LogArea.Startup); + } + + private static void migrateDatabase(Database database) + { + Logger.Info("Migrating database...", LogArea.Database); + Stopwatch totalStopwatch = new(); + Stopwatch stopwatch = new(); + totalStopwatch.Start(); + stopwatch.Start(); + + database.Database.MigrateAsync().Wait(); + stopwatch.Stop(); + Logger.Success($"Structure migration took {stopwatch.ElapsedMilliseconds}ms.", LogArea.Database); + + stopwatch.Reset(); + stopwatch.Start(); + + List completedMigrations = database.CompletedMigrations.ToList(); + List migrationsToRun = MaintenanceHelper.MigrationTasks + .Where(migrationTask => !completedMigrations + .Select(m => m.MigrationName) + .Contains(migrationTask.GetType().Name) + ).ToList(); + + foreach (IMigrationTask migrationTask in migrationsToRun) + { + MaintenanceHelper.RunMigration(migrationTask, database).Wait(); + } + + stopwatch.Stop(); + totalStopwatch.Stop(); + Logger.Success($"Extra migration tasks took {stopwatch.ElapsedMilliseconds}ms.", LogArea.Database); + Logger.Success($"Total migration took {totalStopwatch.ElapsedMilliseconds}ms.", LogArea.Database); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/NormalStorableList.cs b/ProjectLighthouse/StorableLists/NormalStorableList.cs new file mode 100644 index 00000000..bba3e62d --- /dev/null +++ b/ProjectLighthouse/StorableLists/NormalStorableList.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LBPUnion.ProjectLighthouse.StorableLists; + +public class NormalStorableList : StorableList +{ + private readonly List list; + public NormalStorableList(List normalCollection) : base(normalCollection) + { + this.list = normalCollection; + } + + public override void Add(T item) + { + this.list.Add(item); + } + + public override Task AddAsync(T item) + { + this.list.Add(item); + return Task.CompletedTask; + } + + public override void RemoveAll(Predicate predicate) + { + this.list.RemoveAll(predicate); + } + + public override Task RemoveAllAsync(Predicate predicate) + { + this.list.RemoveAll(predicate); + return Task.CompletedTask; + } + + public override Task RemoveAllAsync() + { + this.list.RemoveAll(_ => true); + return Task.CompletedTask; + } + + public override void RemoveAll() + { + this.list.RemoveAll(_ => true); + } + + public override void Remove(T item) + { + this.list.Remove(item); + } + + public override Task RemoveAsync(T item) + { + this.list.Remove(item); + return Task.CompletedTask; + } + public override void Update(T item) {} + public override Task UpdateAsync(T item) => Task.CompletedTask; +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/RedisDatabase.cs b/ProjectLighthouse/StorableLists/RedisDatabase.cs new file mode 100644 index 00000000..6910aec0 --- /dev/null +++ b/ProjectLighthouse/StorableLists/RedisDatabase.cs @@ -0,0 +1,77 @@ +#nullable enable +using System; +using System.Globalization; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Match.Rooms; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; +using Redis.OM; +using Redis.OM.Contracts; +using Redis.OM.Searching; + +namespace LBPUnion.ProjectLighthouse.StorableLists; + +public static class RedisDatabase +{ + private static readonly RedisConnectionProvider provider; + + static RedisDatabase() + { + provider = new RedisConnectionProvider(ServerConfiguration.Instance.RedisConnectionString); + } + + public static bool Initialized { get; private set; } + public static async Task Initialize() + { + if (Initialized) throw new InvalidOperationException("Redis has already been initialized."); + + try + { + IRedisConnection connection = getConnection(); + + string pong = (await connection.ExecuteAsync("PING")).ToString(CultureInfo.InvariantCulture); + if (pong != "PONG") + { + Logger.Error("Could not ping, ping returned " + pong, + LogArea.Redis); + return; + } + + await createIndexes(connection); + } + catch(Exception e) + { + Logger.Error("Could not initialize Redis:\n" + e, LogArea.Redis); + return; + } + + Initialized = true; + Logger.Success("Initialized Redis.", LogArea.Redis); + } + + public static async Task FlushAll() + { + IRedisConnection connection = getConnection(); + await connection.ExecuteAsync("FLUSHALL"); + + await createIndexes(connection); + } + + private static async Task createIndexes(IRedisConnection connection) + { + await connection.RecreateIndexAsync(typeof(Room)); + await connection.RecreateIndexAsync(typeof(UserFriendData)); + } + + private static IRedisConnection getConnection() + { + Logger.Debug("Getting a Redis connection", LogArea.Redis); + return provider.Connection; + } + + public static IRedisCollection UserFriendStoreCollection => provider.RedisCollection(); + + internal static IRedisCollection GetRooms() => provider.RedisCollection(); +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/RedisStorableList.cs b/ProjectLighthouse/StorableLists/RedisStorableList.cs new file mode 100644 index 00000000..c6938f28 --- /dev/null +++ b/ProjectLighthouse/StorableLists/RedisStorableList.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Redis.OM.Searching; + +namespace LBPUnion.ProjectLighthouse.StorableLists; + +public class RedisStorableList : StorableList +{ + private readonly IRedisCollection redisNormalCollection; + public RedisStorableList(IRedisCollection normalCollection) : base(normalCollection) + { + this.redisNormalCollection = normalCollection; + } + + public override Task AddAsync(T item) => this.redisNormalCollection.InsertAsync(item); + public override void Add(T item) + { + this.redisNormalCollection.Insert(item); + } + + public override Task RemoveAsync(T item) => this.redisNormalCollection.Delete(item); + public override void Remove(T item) + { + this.redisNormalCollection.DeleteSync(item); + } + + public override Task UpdateAsync(T item) => this.redisNormalCollection.Update(item); + public override void Update(T item) + { + this.redisNormalCollection.UpdateSync(item); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/StorableList.cs b/ProjectLighthouse/StorableLists/StorableList.cs new file mode 100644 index 00000000..ebd34d28 --- /dev/null +++ b/ProjectLighthouse/StorableLists/StorableList.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LBPUnion.ProjectLighthouse.StorableLists; + +public abstract class StorableList : IEnumerable +{ + private protected readonly IEnumerable NormalCollection; + protected StorableList(IEnumerable normalCollection) + { + this.NormalCollection = normalCollection; + } + + public abstract void Add(T item); + public abstract Task AddAsync(T item); + public abstract void Remove(T item); + public abstract Task RemoveAsync(T item); + public abstract void Update(T item); + public abstract Task UpdateAsync(T item); + + public virtual void RemoveAll(Predicate predicate) + { + foreach (T item in this.NormalCollection) + { + if (!predicate.Invoke(item)) continue; + + this.Remove(item); + } + } + + public virtual async Task RemoveAllAsync(Predicate predicate) + { + foreach (T item in this.NormalCollection) + { + if (!predicate.Invoke(item)) continue; + + await this.RemoveAsync(item); + } + } + + public virtual void RemoveAll() + { + foreach (T item in this.NormalCollection) + { + this.Remove(item); + } + } + + public virtual async Task RemoveAllAsync() + { + foreach (T item in this.NormalCollection) + { + await this.RemoveAsync(item); + } + } + + public IEnumerator GetEnumerator() => this.NormalCollection.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/Stores/RoomStore.cs b/ProjectLighthouse/StorableLists/Stores/RoomStore.cs new file mode 100644 index 00000000..6452b5ce --- /dev/null +++ b/ProjectLighthouse/StorableLists/Stores/RoomStore.cs @@ -0,0 +1,21 @@ +#nullable enable +using System.Collections.Generic; +using LBPUnion.ProjectLighthouse.Match.Rooms; + +namespace LBPUnion.ProjectLighthouse.StorableLists.Stores; + +public static class RoomStore +{ + private static List? rooms; + + public static StorableList GetRooms() + { + if (RedisDatabase.Initialized) + { + return new RedisStorableList(RedisDatabase.GetRooms()); + } + + rooms ??= new List(); + return new NormalStorableList(rooms); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/StorableLists/Stores/UserFriendStore.cs b/ProjectLighthouse/StorableLists/Stores/UserFriendStore.cs new file mode 100644 index 00000000..4e17e725 --- /dev/null +++ b/ProjectLighthouse/StorableLists/Stores/UserFriendStore.cs @@ -0,0 +1,40 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using LBPUnion.ProjectLighthouse.PlayerData.Profiles; + +namespace LBPUnion.ProjectLighthouse.StorableLists.Stores; + +public static class UserFriendStore +{ + private static List? friendDataStore; + + private static StorableList getStorableFriendData() + { + if (RedisDatabase.Initialized) + { + return new RedisStorableList(RedisDatabase.UserFriendStoreCollection); + } + + friendDataStore ??= new List(); + return new NormalStorableList(friendDataStore); + } + + public static UserFriendData? GetUserFriendData(int userId) => getStorableFriendData().FirstOrDefault(s => s.UserId == userId); + + public static UserFriendData CreateUserFriendData(int userId) + { + UserFriendData friendData = new() + { + UserId = userId, + }; + + getStorableFriendData().Add(friendData); + return friendData; + } + + public static void UpdateFriendData(UserFriendData friendData) + { + getStorableFriendData().Update(friendData); + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Tickets/DataHeader.cs b/ProjectLighthouse/Tickets/Data/DataHeader.cs similarity index 60% rename from ProjectLighthouse/Types/Tickets/DataHeader.cs rename to ProjectLighthouse/Tickets/Data/DataHeader.cs index 940f980c..a41bcab6 100644 --- a/ProjectLighthouse/Types/Tickets/DataHeader.cs +++ b/ProjectLighthouse/Tickets/Data/DataHeader.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Tickets; +namespace LBPUnion.ProjectLighthouse.Tickets.Data; public struct DataHeader { diff --git a/ProjectLighthouse/Types/Tickets/DataType.cs b/ProjectLighthouse/Tickets/Data/DataType.cs similarity index 74% rename from ProjectLighthouse/Types/Tickets/DataType.cs rename to ProjectLighthouse/Tickets/Data/DataType.cs index 92da0543..a35bf811 100644 --- a/ProjectLighthouse/Types/Tickets/DataType.cs +++ b/ProjectLighthouse/Tickets/Data/DataType.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Tickets; +namespace LBPUnion.ProjectLighthouse.Tickets.Data; public enum DataType : byte { diff --git a/ProjectLighthouse/Types/Tickets/SectionHeader.cs b/ProjectLighthouse/Tickets/Data/SectionHeader.cs similarity index 62% rename from ProjectLighthouse/Types/Tickets/SectionHeader.cs rename to ProjectLighthouse/Tickets/Data/SectionHeader.cs index 5242fe59..a22bb2d3 100644 --- a/ProjectLighthouse/Types/Tickets/SectionHeader.cs +++ b/ProjectLighthouse/Tickets/Data/SectionHeader.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Tickets; +namespace LBPUnion.ProjectLighthouse.Tickets.Data; public struct SectionHeader { diff --git a/ProjectLighthouse/Types/Tickets/SectionType.cs b/ProjectLighthouse/Tickets/Data/SectionType.cs similarity index 57% rename from ProjectLighthouse/Types/Tickets/SectionType.cs rename to ProjectLighthouse/Tickets/Data/SectionType.cs index c7bb43a5..673e1523 100644 --- a/ProjectLighthouse/Types/Tickets/SectionType.cs +++ b/ProjectLighthouse/Tickets/Data/SectionType.cs @@ -1,4 +1,4 @@ -namespace LBPUnion.ProjectLighthouse.Types.Tickets; +namespace LBPUnion.ProjectLighthouse.Tickets.Data; public enum SectionType : byte { diff --git a/ProjectLighthouse/Types/Tickets/NPTicket.cs b/ProjectLighthouse/Tickets/NPTicket.cs similarity index 71% rename from ProjectLighthouse/Types/Tickets/NPTicket.cs rename to ProjectLighthouse/Tickets/NPTicket.cs index 5f948a1c..84fa0723 100644 --- a/ProjectLighthouse/Types/Tickets/NPTicket.cs +++ b/ProjectLighthouse/Tickets/NPTicket.cs @@ -3,22 +3,24 @@ using System; using System.IO; using System.Text; using System.Text.Json; -using Kettu; +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; using LBPUnion.ProjectLighthouse.Logging; -using LBPUnion.ProjectLighthouse.Types.Settings; +using LBPUnion.ProjectLighthouse.PlayerData; +using LBPUnion.ProjectLighthouse.Tickets.Data; +using Version = LBPUnion.ProjectLighthouse.Types.Version; -namespace LBPUnion.ProjectLighthouse.Types.Tickets; +namespace LBPUnion.ProjectLighthouse.Tickets; /// /// A PSN ticket, typically sent from PS3/RPCN /// public class NPTicket { - public string Username { get; set; } + public string? Username { get; set; } - private Version ticketVersion { get; set; } + private Version? ticketVersion { get; set; } public Platform Platform { get; set; } @@ -26,7 +28,7 @@ public class NPTicket public ulong IssuedDate { get; set; } public ulong ExpireDate { get; set; } - private string titleId { get; set; } + private string? titleId { get; set; } public GameVersion GameVersion { get; set; } @@ -93,12 +95,8 @@ public class NPTicket reader.ReadUInt16BE(); // Ticket length, we don't care about this - #if DEBUG SectionHeader bodyHeader = reader.ReadSectionHeader(); - Logger.Log($"bodyHeader.Type is {bodyHeader.Type}", LoggerLevelLogin.Instance); - #else - reader.ReadSectionHeader(); - #endif + Logger.Debug($"bodyHeader.Type is {bodyHeader.Type}", LogArea.Login); switch (npTicket.ticketVersion) { @@ -111,22 +109,22 @@ public class NPTicket default: throw new NotImplementedException(); } + if (npTicket.titleId == null) throw new ArgumentNullException($"{nameof(npTicket)}.{nameof(npTicket.titleId)}"); + // We already read the title id, however we need to do some post-processing to get what we want. // Current data: UP9000-BCUS98245_00 - // We need to chop this to get the titleId we're looking for + // We need to chop this to get the titleId we're looking for npTicket.titleId = npTicket.titleId.Substring(7); // Trim UP9000- npTicket.titleId = npTicket.titleId.Substring(0, npTicket.titleId.Length - 3); // Trim _00 at the end // Data now (hopefully): BCUS98245 - #if DEBUG - Logger.Log($"titleId is {npTicket.titleId}", LoggerLevelLogin.Instance); - #endif + Logger.Debug($"titleId is {npTicket.titleId}", LogArea.Login); npTicket.GameVersion = GameVersionHelper.FromTitleId(npTicket.titleId); // Finally, convert it to GameVersion if (npTicket.GameVersion == GameVersion.Unknown) { - Logger.Log($"Could not determine game version from title id {npTicket.titleId}", LoggerLevelLogin.Instance); + Logger.Warn($"Could not determine game version from title id {npTicket.titleId}", LogArea.Login); return null; } @@ -143,45 +141,35 @@ public class NPTicket if (npTicket.Platform == Platform.Unknown) { - Logger.Log($"Could not determine platform from IssuerId {npTicket.IssuerId} decimal", LoggerLevelLogin.Instance); + Logger.Warn($"Could not determine platform from IssuerId {npTicket.IssuerId} decimal", LogArea.Login); return null; } #if DEBUG - Logger.Log("npTicket data:", LoggerLevelLogin.Instance); - foreach (string line in JsonSerializer.Serialize(npTicket).Split('\n')) - { - Logger.Log(line, LoggerLevelLogin.Instance); - } + Logger.Debug("npTicket data:", LogArea.Login); + Logger.Debug(JsonSerializer.Serialize(npTicket), LogArea.Login); #endif return npTicket; } catch(NotImplementedException) { - Logger.Log($"The ticket version {npTicket.ticketVersion} is not implemented yet.", LoggerLevelLogin.Instance); - Logger.Log + Logger.Error($"The ticket version {npTicket.ticketVersion} is not implemented yet.", LogArea.Login); + Logger.Error ( "Please let us know that this is a ticket version that is actually used on our issue tracker at https://github.com/LBPUnion/project-lighthouse/issues !", - LoggerLevelLogin.Instance + LogArea.Login ); return null; } catch(Exception e) { - Logger.Log("Failed to read npTicket!", LoggerLevelLogin.Instance); - Logger.Log("Either this is spam data, or the more likely that this is a bug.", LoggerLevelLogin.Instance); - Logger.Log - ( - "Please report the following exception to our issue tracker at https://github.com/LBPUnion/project-lighthouse/issues!", - LoggerLevelLogin.Instance - ); - - foreach (string line in e.ToDetailedException().Split('\n')) - { - Logger.Log(line, LoggerLevelLogin.Instance); - } + Logger.Error("Failed to read npTicket!", LogArea.Login); + Logger.Error("Either this is spam data, or the more likely that this is a bug.", LogArea.Login); + Logger.Error + ("Please report the following exception to our issue tracker at https://github.com/LBPUnion/project-lighthouse/issues!", LogArea.Login); + Logger.Error(e.ToDetailedException(), LogArea.Login); return null; } } diff --git a/ProjectLighthouse/Types/Tickets/TicketReader.cs b/ProjectLighthouse/Tickets/TicketReader.cs similarity index 84% rename from ProjectLighthouse/Types/Tickets/TicketReader.cs rename to ProjectLighthouse/Tickets/TicketReader.cs index 47256adb..cfb7c067 100644 --- a/ProjectLighthouse/Types/Tickets/TicketReader.cs +++ b/ProjectLighthouse/Tickets/TicketReader.cs @@ -2,16 +2,18 @@ using System.Diagnostics; using System.IO; using System.Text; using JetBrains.Annotations; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.Tickets.Data; +using LBPUnion.ProjectLighthouse.Types; -namespace LBPUnion.ProjectLighthouse.Types.Tickets; +namespace LBPUnion.ProjectLighthouse.Tickets; public class TicketReader : BinaryReader { public TicketReader([NotNull] Stream input) : base(input) {} - public Version ReadTicketVersion() => new(this.ReadByte() >> 4, this.ReadByte()); + public Version ReadTicketVersion() => new((byte)(this.ReadByte() >> 4), this.ReadByte()); public SectionHeader ReadSectionHeader() { diff --git a/ProjectLighthouse/Types/Match/IMatchData.cs b/ProjectLighthouse/Types/Match/IMatchData.cs deleted file mode 100644 index 28ecab3d..00000000 --- a/ProjectLighthouse/Types/Match/IMatchData.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace LBPUnion.ProjectLighthouse.Types.Match; - -public interface IMatchData -{} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/RoomSlot.cs b/ProjectLighthouse/Types/Match/RoomSlot.cs deleted file mode 100644 index b3d2c0e4..00000000 --- a/ProjectLighthouse/Types/Match/RoomSlot.cs +++ /dev/null @@ -1,9 +0,0 @@ -using LBPUnion.ProjectLighthouse.Types.Levels; - -namespace LBPUnion.ProjectLighthouse.Types.Match; - -public class RoomSlot -{ - public int SlotId { get; set; } - public SlotType SlotType { get; set; } -} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs b/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs deleted file mode 100644 index 2e11508e..00000000 --- a/ProjectLighthouse/Types/Match/UpdateMyPlayerData.cs +++ /dev/null @@ -1,9 +0,0 @@ -#nullable enable -namespace LBPUnion.ProjectLighthouse.Types.Match; - -public class UpdateMyPlayerData : IMatchData -{ - public string Player { get; set; } = null!; - - public RoomState? RoomState { get; set; } -} \ No newline at end of file diff --git a/ProjectLighthouse/Types/ServerType.cs b/ProjectLighthouse/Types/ServerType.cs new file mode 100644 index 00000000..4d677fe3 --- /dev/null +++ b/ProjectLighthouse/Types/ServerType.cs @@ -0,0 +1,8 @@ +namespace LBPUnion.ProjectLighthouse.Types; + +public enum ServerType +{ + GameServer = 0, + Website = 1, + Api = 2, +} \ 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 fc356ac8..00000000 --- a/ProjectLighthouse/Types/Settings/ServerSettings.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Text.Json; -using System.Text.Json.Serialization; -using JetBrains.Annotations; -using Kettu; -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; - static ServerSettings() - { - if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own - - Logger.Log("Loading config...", LoggerLevelConfig.Instance); - - if (File.Exists(ConfigFileName)) - { - string configFile = File.ReadAllText(ConfigFileName); - - Instance = JsonSerializer.Deserialize(configFile) ?? throw new ArgumentNullException(nameof(ConfigFileName)); - - if (Instance.ConfigVersion < CurrentConfigVersion) - { - Logger.Log($"Upgrading config file from version {Instance.ConfigVersion} to version {CurrentConfigVersion}", LoggerLevelConfig.Instance); - 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.Log - ( - "The configuration file was not found. " + - "A blank configuration file has been created for you at " + - $"{Path.Combine(Environment.CurrentDirectory, ConfigFileName)}", - LoggerLevelConfig.Instance - ); - - Environment.Exit(1); - } - - // Set up reloading - if (Instance.ConfigReloading) - { - Logger.Log("Setting up config reloading...", LoggerLevelConfig.Instance); - 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.Log("Configuration file modified, reloading config.", LoggerLevelConfig.Instance); - Logger.Log("Some changes may not apply, in which case may require a restart of Project Lighthouse.", LoggerLevelConfig.Instance); - - 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/Settings/ServerStatics.cs b/ProjectLighthouse/Types/Settings/ServerStatics.cs deleted file mode 100644 index 62c2a7b5..00000000 --- a/ProjectLighthouse/Types/Settings/ServerStatics.cs +++ /dev/null @@ -1,36 +0,0 @@ -#nullable enable -using System; -using System.Linq; -using Kettu; -using LBPUnion.ProjectLighthouse.Logging; - -namespace LBPUnion.ProjectLighthouse.Types.Settings; - -public static class ServerStatics -{ - public const string ServerName = "ProjectLighthouse"; - - public const int PageSize = 20; - - public static bool DbConnected { - get { - try - { - return new Database().Database.CanConnect(); - } - catch(Exception e) - { - Logger.Log(e.ToString(), LoggerLevelDatabase.Instance); - return false; - } - } - } - - public static bool IsUnitTesting => AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.StartsWith("xunit")); - - #if DEBUG - public static readonly bool IsDebug = true; - #else - public static readonly bool IsDebug = false; - #endif -} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Version.cs b/ProjectLighthouse/Types/Version.cs index eb9f3649..e66bf026 100644 --- a/ProjectLighthouse/Types/Version.cs +++ b/ProjectLighthouse/Types/Version.cs @@ -1,11 +1,11 @@ namespace LBPUnion.ProjectLighthouse.Types; -public class Version +public struct Version { - public int Major { get; set; } - public int Minor { get; set; } + public byte Major { get; set; } + public byte Minor { get; set; } - public Version(int major, int minor) + public Version(byte major, byte minor) { this.Major = major; this.Minor = minor; diff --git a/docker-compose.yml b/docker-compose.yml index 0f507cfe..e35dd091 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,15 @@ services: - '3306' # Expose port to localhost:3306 volumes: - lighthouse-db:/var/lib/mysql + lighthouse-redis: + image: redis/redis-stack + ports: + - '6379:6379' + - '8001:8001' + expose: + - '6379' + - '8001' + volumes: lighthouse-db: \ No newline at end of file diff --git a/scripts-and-tools/build.sh b/scripts-and-tools/build.sh new file mode 100644 index 00000000..8be9cefb --- /dev/null +++ b/scripts-and-tools/build.sh @@ -0,0 +1,14 @@ +# Build script for production +# +# No arguments +# Called manually + +cd project-lighthouse || (echo "Source directory not found, pls clone properly~" && exit 1) + +echo "Pulling latest changes..." +git pull + +echo "Building..." +dotnet build -c Release + +exit $? # Expose error code from build command \ No newline at end of file diff --git a/create-migration.sh b/scripts-and-tools/create-migration.sh similarity index 54% rename from create-migration.sh rename to scripts-and-tools/create-migration.sh index 5ccbe0fb..43c6a3d2 100755 --- a/create-migration.sh +++ b/scripts-and-tools/create-migration.sh @@ -1,4 +1,9 @@ #!/bin/bash +# Developer script to create EntityFramework database migrations +# +# $1: Name of the migration, e.g. SwitchToPermissionLevels +# Invoked manually + export LIGHTHOUSE_DB_CONNECTION_STRING='server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse' dotnet ef migrations add "$1" --project ProjectLighthouse \ No newline at end of file diff --git a/scripts-and-tools/example-nginx-lighthouse.conf b/scripts-and-tools/example-nginx-lighthouse.conf new file mode 100644 index 00000000..368556bb --- /dev/null +++ b/scripts-and-tools/example-nginx-lighthouse.conf @@ -0,0 +1,55 @@ +# Normal server +server { + server_name example.com resa.example.com resb.example.com resc.example.com resd.example.com rese.example.com resf.example.com res1.example.com res2.example.com res3.example.com res4.example.com res5.example.com res6.example.com res7.example.com res8.example.com res9.example.com res0.example.com; + + # SSL Configuration + listen 443 ssl; + + ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers ALL; + ssl_prefer_server_ciphers off; + + # Server locations + # Technically, the ports dont follow standards, + # but they're bad standards so who cares. + # + # /: Website server (10060) + # /LITTLEBIGPLANETPS3_XML/: Gameserver (10061) + # /api/: API server (10062) + location / { + proxy_http_version 1.1; + proxy_pass http://127.0.0.1:10060; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /LITTLEBIGPLANETPS3_XML/ { + proxy_http_version 1.1; + proxy_pass http://127.0.0.1:10061; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /api/ { + proxy_http_version 1.1; + proxy_pass http://127.0.0.1:10062; + } + + keepalive_timeout 0; +} + +# HTTPS redirection +server { + # Same server_name as above + server_name example.com resa.example.com resb.example.com resc.example.com resd.example.com rese.example.com resf.example.com res1.example.com res2.example.com res3.example.com res4.example.com res5.example.com res6.example.com res7.example.com res8.example.com res9.example.com res0.example.com; + listen 80; + + location / { + return 301 https://example.com$request_uri; + } + + # Pass through traffic to Gameserver as normal. Important for LBP1/2/PSP!!!! + location /LITTLEBIGPLANETPS3_XML/ { + proxy_http_version 1.1; + proxy_pass http://127.0.0.1:10061; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} \ No newline at end of file diff --git a/scripts-and-tools/project-lighthouse-api.service b/scripts-and-tools/project-lighthouse-api.service new file mode 100644 index 00000000..6f256554 --- /dev/null +++ b/scripts-and-tools/project-lighthouse-api.service @@ -0,0 +1,14 @@ +[Unit] +Description=Project Lighthouse API - a clean-room, open-source custom server for LBP +Documentation=https://github.com/LBPUnion/ProjectLighthouse + +[Service] +Type=simple +ExecStart=bash -c "/srv/lighthouse/start.sh API" +TimeoutStopSec=15 +User=lighthouse +Restart=on-failure + +[Install] +Alias=lighthouse-api +WantedBy=multi-user.target \ No newline at end of file diff --git a/scripts-and-tools/project-lighthouse-gameserver.service b/scripts-and-tools/project-lighthouse-gameserver.service new file mode 100644 index 00000000..d6bbb073 --- /dev/null +++ b/scripts-and-tools/project-lighthouse-gameserver.service @@ -0,0 +1,14 @@ +[Unit] +Description=Project Lighthouse GameServer - a clean-room, open-source custom server for LBP +Documentation=https://github.com/LBPUnion/ProjectLighthouse + +[Service] +Type=simple +ExecStart=bash -c "/srv/lighthouse/start.sh GameServer" +TimeoutStopSec=15 +User=lighthouse +Restart=on-failure + +[Install] +Alias=lighthouse-website +WantedBy=multi-user.target \ No newline at end of file diff --git a/scripts-and-tools/project-lighthouse-website.service b/scripts-and-tools/project-lighthouse-website.service new file mode 100644 index 00000000..9d453796 --- /dev/null +++ b/scripts-and-tools/project-lighthouse-website.service @@ -0,0 +1,14 @@ +[Unit] +Description=Project Lighthouse Website - a clean-room, open-source custom server for LBP +Documentation=https://github.com/LBPUnion/ProjectLighthouse + +[Service] +Type=simple +ExecStart=bash -c "/srv/lighthouse/start.sh Website" +TimeoutStopSec=15 +User=lighthouse +Restart=on-failure + +[Install] +Alias=lighthouse-website +WantedBy=multi-user.target \ No newline at end of file diff --git a/scripts-and-tools/start.sh b/scripts-and-tools/start.sh new file mode 100644 index 00000000..10d2803a --- /dev/null +++ b/scripts-and-tools/start.sh @@ -0,0 +1,13 @@ +# Startup script for production +# +# $1: Server to start; case sensitive!!!!! +# Called from systemd units + +cd "$HOME"/data || (echo "Data directory not found, pls create one~" && exit 1) + +echo "Running..." + +# Normally this requires ASPNETCORE_URLS but we override that in the configuration +dotnet ../project-lighthouse/ProjectLighthouse.Servers."$1"/bin/Release/net6.0/LBPUnion.ProjectLighthouse.Servers."$1".dll + +exit $? # Expose error code from dotnet command \ No newline at end of file