mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-04-19 19:14:51 +00:00
Merge main into mod-panel
This commit is contained in:
commit
b2e6f25265
38 changed files with 468 additions and 139 deletions
|
@ -3,11 +3,13 @@
|
|||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes>
|
||||
<Path>.config/dotnet-tools.json</Path>
|
||||
<Path>.github</Path>
|
||||
<Path>.gitignore</Path>
|
||||
<Path>.idea</Path>
|
||||
<Path>CONTRIBUTING.md</Path>
|
||||
<Path>DatabaseMigrations</Path>
|
||||
<Path>LICENSE</Path>
|
||||
<Path>ProjectLighthouse.sln.DotSettings</Path>
|
||||
<Path>ProjectLighthouse.sln.DotSettings.user</Path>
|
||||
<Path>README.md</Path>
|
||||
|
|
|
@ -130,7 +130,7 @@ public class LoginController : ControllerBase
|
|||
new LoginResult
|
||||
{
|
||||
AuthTicket = "MM_AUTH=" + token.UserToken,
|
||||
LbpEnvVer = ServerStatics.ServerName,
|
||||
ServerBrand = VersionHelper.FullVersion,
|
||||
}.Serialize()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -86,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)))
|
||||
{
|
||||
|
|
|
@ -96,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);
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ public class RoomVisualizerController : ControllerBase
|
|||
#if !DEBUG
|
||||
return this.NotFound();
|
||||
#else
|
||||
RoomHelper.Rooms.RemoveAll();
|
||||
lock(RoomHelper.RoomLock) RoomHelper.Rooms.RemoveAll();
|
||||
return this.Redirect("/debug/roomVisualizer");
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
@page "/"
|
||||
@using LBPUnion.ProjectLighthouse.Configuration
|
||||
@using LBPUnion.ProjectLighthouse.Extensions
|
||||
@using LBPUnion.ProjectLighthouse.PlayerData.Profiles
|
||||
@using LBPUnion.ProjectLighthouse.Types
|
||||
@using LBPUnion.ProjectLighthouse.Levels
|
||||
@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.LandingPage
|
||||
|
||||
@{
|
||||
Layout = "Layouts/BaseLayout";
|
||||
Model.ShowTitleInPage = false;
|
||||
bool isMobile = this.Request.IsMobile();
|
||||
}
|
||||
<h1>Welcome to <b>Project Lighthouse</b>!</h1>
|
||||
<h1>Welcome to <b>@ServerConfiguration.Instance.Customization.ServerName</b>!</h1>
|
||||
|
||||
@if (Model.User != null)
|
||||
{
|
||||
|
@ -41,4 +43,39 @@ else
|
|||
{
|
||||
<a href="/user/@user.UserId" title="@user.Status.ToString()">@user.Username</a>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<br>
|
||||
|
||||
<div class="@(isMobile ? "" : "ui center aligned grid")">
|
||||
<div class="eight wide column">
|
||||
<div class="ui pink segment">
|
||||
<h1><i class="ribbon icon"></i>Latest Team Picks</h1>
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui left aligned segment">
|
||||
@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))
|
||||
<br>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (isMobile)
|
||||
{
|
||||
<br>
|
||||
}
|
||||
<div class="eight wide column">
|
||||
<div class="ui blue segment">
|
||||
<h1><i class="certificate icon"></i>Newest Levels</h1>
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui left aligned segment">
|
||||
@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))
|
||||
<br>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,23 +1,27 @@
|
|||
#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 LBPUnion.ProjectLighthouse.Types;
|
||||
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<User> PlayersOnline = new();
|
||||
|
||||
public int PlayersOnlineCount;
|
||||
public LandingPage(Database database) : base(database)
|
||||
{}
|
||||
|
||||
public List<Slot>? LatestTeamPicks;
|
||||
public List<Slot>? NewestLevels;
|
||||
|
||||
[UsedImplicitly]
|
||||
public async Task<IActionResult> OnGet()
|
||||
|
@ -35,6 +39,38 @@ public class LandingPage : BaseLayout
|
|||
List<int> 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
|
||||
},
|
||||
};
|
||||
}
|
|
@ -33,11 +33,11 @@
|
|||
<head>
|
||||
@if (Model.Title == string.Empty)
|
||||
{
|
||||
<title>Project Lighthouse</title>
|
||||
<title>@ServerConfiguration.Instance.Customization.ServerName</title>
|
||||
}
|
||||
else
|
||||
{
|
||||
<title>Project Lighthouse - @Model.Title</title>
|
||||
<title>@ServerConfiguration.Instance.Customization.ServerName - @Model.Title</title>
|
||||
}
|
||||
<link rel="stylesheet" type="text/css" href="~/css/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.8/dist/semantic.min.css">
|
||||
|
@ -52,7 +52,7 @@
|
|||
|
||||
@* Embed Stuff *@
|
||||
<meta name="theme-color" data-react-helmet="true" content="#008cff">
|
||||
<meta content="Project Lighthouse - @Model.Title" property="og:title">
|
||||
<meta content="@ServerConfiguration.Instance.Customization.ServerName - @Model.Title" property="og:title">
|
||||
@if (!string.IsNullOrEmpty(Model.Description))
|
||||
{
|
||||
<meta content="@Model.Description" property="og:description">
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
@using LBPUnion.ProjectLighthouse.Configuration
|
||||
@using LBPUnion.ProjectLighthouse.PlayerData
|
||||
@using LBPUnion.ProjectLighthouse.PlayerData.Profiles
|
||||
@using LBPUnion.ProjectLighthouse.Types
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@model LBPUnion.ProjectLighthouse.Levels.Slot
|
||||
|
||||
|
@ -12,8 +11,10 @@
|
|||
await using Database database = new();
|
||||
|
||||
string slotName = string.IsNullOrEmpty(Model.Name) ? "Unnamed Level" : Model.Name;
|
||||
bool isMobile = (bool?)ViewData["IsMobile"] ?? false;
|
||||
|
||||
bool isMobile = (bool?)ViewData["IsMobile"] ?? false;
|
||||
bool mini = (bool?)ViewData["IsMini"] ?? false;
|
||||
|
||||
bool isQueued = false;
|
||||
bool isHearted = false;
|
||||
|
||||
|
@ -31,25 +32,44 @@
|
|||
}
|
||||
<div class="card">
|
||||
@{
|
||||
int size = isMobile ? 50 : 100;
|
||||
int size = isMobile || mini ? 50 : 100;
|
||||
}
|
||||
<div>
|
||||
<img src="~/assets/slotCardOverlay.png" style="min-width: @(size)px; width: @(size)px; height: @(size)px; pointer-events: none; position: absolute">
|
||||
<img class="cardIcon slotCardIcon" src="/gameAssets/@iconHash" style="min-width: @(size)px; width: @(size)px; height: @(size)px">
|
||||
</div>
|
||||
<div class="cardStats">
|
||||
@if (showLink)
|
||||
@if (!mini)
|
||||
{
|
||||
<h2>
|
||||
<a href="~/slot/@Model.SlotId">@slotName</a>
|
||||
</h2>
|
||||
@if (showLink)
|
||||
{
|
||||
<h2>
|
||||
<a href="~/slot/@Model.SlotId">@slotName</a>
|
||||
</h2>
|
||||
}
|
||||
else
|
||||
{
|
||||
<h1>
|
||||
@slotName
|
||||
</h1>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<h1>
|
||||
@slotName
|
||||
</h1>
|
||||
@if (showLink)
|
||||
{
|
||||
<h3>
|
||||
<a href="~/slot/@Model.SlotId">@slotName</a>
|
||||
</h3>
|
||||
}
|
||||
else
|
||||
{
|
||||
<h3>
|
||||
@slotName
|
||||
</h3>
|
||||
}
|
||||
}
|
||||
|
||||
<div class="cardStatsUnderTitle">
|
||||
<i class="pink heart icon" title="Hearts"></i> <span>@Model.Hearts</span>
|
||||
<i class="blue play icon" title="Plays"></i> <span>@Model.PlaysUnique</span>
|
||||
|
@ -68,7 +88,7 @@
|
|||
</div>
|
||||
<div class="cardButtons">
|
||||
<br>
|
||||
@if (user != null)
|
||||
@if (user != null && !mini)
|
||||
{
|
||||
if (isHearted)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#nullable enable
|
||||
using JetBrains.Annotations;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
|
||||
|
@ -38,7 +39,8 @@ public class PasswordResetPage : BaseLayout
|
|||
|
||||
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("~/");
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ 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 Xunit;
|
||||
|
@ -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]
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.2.0" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="101.0.4951.4100" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
|
|
|
@ -115,6 +115,7 @@
|
|||
<s:Boolean x:Key="/Default/UserDictionary/Words/=dpadrate/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ezoiar/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=farc/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=FLUSHALL/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=friendscores/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ingame/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kettu/@EntryIndexedValue">True</s:Boolean>
|
||||
|
|
25
ProjectLighthouse/Administration/CompletedMigration.cs
Normal file
25
ProjectLighthouse/Administration/CompletedMigration.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using LBPUnion.ProjectLighthouse.Administration.Maintenance;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Administration;
|
||||
|
||||
/// <summary>
|
||||
/// A record of the completion of a <see cref="IMigrationTask"/>.
|
||||
/// </summary>
|
||||
public class CompletedMigration
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the migration.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Do not use the user-friendly name when setting this.
|
||||
/// </remarks>
|
||||
[Key]
|
||||
public string MigrationName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The moment the migration was ran.
|
||||
/// </summary>
|
||||
public DateTime RanAt { get; set; }
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Administration.Maintenance;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IMaintenanceJob
|
||||
{
|
||||
public Task Run();
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Administration.Maintenance;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IMigrationTask
|
||||
{
|
||||
/// <summary>
|
||||
/// The user-friendly name of a migration.
|
||||
/// </summary>
|
||||
public string Name();
|
||||
|
||||
/// <summary>
|
||||
/// Performs the migration.
|
||||
/// </summary>
|
||||
/// <param name="database">The Lighthouse database.</param>
|
||||
/// <returns>True if successful, false if not.</returns>
|
||||
internal Task<bool> Run(Database database);
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
#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;
|
||||
|
||||
|
@ -11,24 +13,16 @@ namespace LBPUnion.ProjectLighthouse.Administration.Maintenance;
|
|||
|
||||
public static class MaintenanceHelper
|
||||
{
|
||||
|
||||
static MaintenanceHelper()
|
||||
{
|
||||
Commands = getListOfInterfaceObjects<ICommand>();
|
||||
MaintenanceJobs = getListOfInterfaceObjects<IMaintenanceJob>();
|
||||
MigrationTasks = getListOfInterfaceObjects<IMigrationTask>();
|
||||
}
|
||||
|
||||
public static List<ICommand> Commands { get; }
|
||||
|
||||
public static List<IMaintenanceJob> MaintenanceJobs { get; }
|
||||
|
||||
private static List<T> getListOfInterfaceObjects<T>() 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 List<IMigrationTask> MigrationTasks { get; }
|
||||
|
||||
public static async Task<List<LogLine>> RunCommand(string[] args)
|
||||
{
|
||||
|
@ -66,11 +60,57 @@ public static class MaintenanceHelper
|
|||
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();
|
||||
}
|
||||
|
||||
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<T> getListOfInterfaceObjects<T>() 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()!;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Administration.Reports;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Levels;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Reviews;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Administration.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();
|
||||
}
|
||||
}
|
|
@ -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<bool> IMigrationTask.Run(Database database)
|
||||
{
|
||||
List<object> 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace LBPUnion.ProjectLighthouse.Configuration.ConfigurationCategories;
|
||||
|
||||
public class CustomizationConfiguration
|
||||
{
|
||||
public string ServerName { get; set; } = "Project Lighthouse";
|
||||
}
|
|
@ -23,7 +23,7 @@ public class ServerConfiguration
|
|||
// You can use an ObsoleteAttribute instead. Make sure you set it to error, though.
|
||||
//
|
||||
// Thanks for listening~
|
||||
public const int CurrentConfigVersion = 4;
|
||||
public const int CurrentConfigVersion = 5;
|
||||
|
||||
#region Meta
|
||||
|
||||
|
@ -163,6 +163,9 @@ public class ServerConfiguration
|
|||
|
||||
#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";
|
||||
|
||||
|
@ -193,5 +196,5 @@ public class ServerConfiguration
|
|||
public MailConfiguration Mail { get; set; } = new();
|
||||
public UserGeneratedContentLimitConfiguration UserGeneratedContentLimits { get; set; } = new();
|
||||
public WebsiteConfiguration WebsiteConfiguration { get; set; } = new();
|
||||
|
||||
public CustomizationConfiguration Customization { get; set; } = new();
|
||||
}
|
|
@ -2,13 +2,12 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Configuration;
|
||||
|
||||
public static class ServerStatics
|
||||
{
|
||||
public const string ServerName = "ProjectLighthouse";
|
||||
|
||||
public const int PageSize = 20;
|
||||
|
||||
public static bool DbConnected {
|
||||
|
@ -25,11 +24,18 @@ public static class ServerStatics
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: This needs to go at some point.
|
||||
public static bool IsUnitTesting => AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName!.StartsWith("xunit"));
|
||||
|
||||
#if DEBUG
|
||||
public static readonly bool IsDebug = true;
|
||||
public const bool IsDebug = true;
|
||||
#else
|
||||
public static readonly bool IsDebug = false;
|
||||
public const bool IsDebug = false;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The servertype, determined on startup. Shouldn't be null unless very very early in startup.
|
||||
/// </summary>
|
||||
// The way of doing this is kinda weird, but it works.
|
||||
public static ServerType ServerType;
|
||||
}
|
|
@ -3,6 +3,7 @@ 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;
|
||||
|
@ -21,6 +22,7 @@ namespace LBPUnion.ProjectLighthouse;
|
|||
|
||||
public class Database : DbContext
|
||||
{
|
||||
public DbSet<CompletedMigration> CompletedMigrations { get; set; }
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<Location> Locations { get; set; }
|
||||
public DbSet<Slot> Slots { get; set; }
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
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;
|
||||
|
@ -52,4 +55,7 @@ public static class DatabaseExtensions
|
|||
|
||||
return query;
|
||||
}
|
||||
|
||||
public static async Task<bool> Has<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> predicate) =>
|
||||
await queryable.FirstOrDefaultAsync(predicate) != null;
|
||||
}
|
|
@ -31,8 +31,8 @@ public static class VersionHelper
|
|||
{
|
||||
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.",
|
||||
"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";
|
||||
|
@ -54,14 +54,14 @@ 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
|
||||
#if DEBUG
|
||||
"Debug";
|
||||
#elif RELEASE
|
||||
"Release";
|
||||
|
|
|
@ -11,7 +11,8 @@ public class ConsoleLogger : ILogger
|
|||
|
||||
foreach (string line in logLine.Message.Split('\n'))
|
||||
{
|
||||
// The following is scuffed. Beware~
|
||||
// The following is scuffed.
|
||||
// Beware~
|
||||
|
||||
// Write the level! [Success]
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Logging.Loggers;
|
||||
|
||||
public class LighthouseFileLogger : ILogger
|
||||
public class FileLogger : ILogger
|
||||
{
|
||||
private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs");
|
||||
|
||||
|
@ -13,8 +13,8 @@ public class LighthouseFileLogger : ILogger
|
|||
{
|
||||
FileHelper.EnsureDirectoryCreated(logsDirectory);
|
||||
|
||||
string contentFile = $"[{line.Level}] <{line.Trace.Name}:{line.Trace.Section}> {line.Message}\n";
|
||||
string contentAll = $"[{line.Area}:{line.Level}] <{line.Trace.Name}:{line.Trace.Section}> {line.Message}\n";
|
||||
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
|
||||
{
|
|
@ -16,7 +16,8 @@ namespace LBPUnion.ProjectLighthouse.Match.Rooms;
|
|||
|
||||
public class RoomHelper
|
||||
{
|
||||
public static readonly StorableList<Room> Rooms = RoomStore.GetRooms();
|
||||
public static readonly object RoomLock = new();
|
||||
public static StorableList<Room> Rooms => RoomStore.GetRooms();
|
||||
|
||||
public static void StartCleanupThread()
|
||||
{
|
||||
|
@ -162,7 +163,7 @@ public class RoomHelper
|
|||
};
|
||||
|
||||
CleanupRooms(room.HostId, room);
|
||||
lock(Rooms) Rooms.Add(room);
|
||||
lock(RoomLock) Rooms.Add(room);
|
||||
Logger.Info($"Created room (id: {room.RoomId}) for host {room.HostId}", LogArea.Match);
|
||||
|
||||
return room;
|
||||
|
@ -193,7 +194,7 @@ public class RoomHelper
|
|||
[SuppressMessage("ReSharper", "InvertIf")]
|
||||
public static void CleanupRooms(int? hostId = null, Room? newRoom = null, Database? database = null)
|
||||
{
|
||||
lock(Rooms)
|
||||
lock(RoomLock)
|
||||
{
|
||||
int roomCountBeforeCleanup = Rooms.Count();
|
||||
|
||||
|
|
|
@ -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<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
RanAt = table.Column<DateTime>(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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using LBPUnion.ProjectLighthouse;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
@ -18,6 +19,19 @@ namespace ProjectLighthouse.Migrations
|
|||
.HasAnnotation("ProductVersion", "6.0.5")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Administration.CompletedMigration", b =>
|
||||
{
|
||||
b.Property<string>("MigrationName")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<DateTime>("RanAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("MigrationName");
|
||||
|
||||
b.ToTable("CompletedMigrations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Administration.Reports.GriefReport", b =>
|
||||
{
|
||||
b.Property<int>("ReportId")
|
||||
|
@ -581,15 +595,16 @@ namespace ProjectLighthouse.Migrations
|
|||
b.Property<int>("AdminGrantedSlots")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("Banned")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("BannedReason")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Biography")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("BooHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("EmailAddress")
|
||||
|
@ -602,48 +617,39 @@ namespace ProjectLighthouse.Migrations
|
|||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("IconHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("IsAdmin")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("LocationId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("MehHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("PasswordResetRequired")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("PermissionLevel")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Pins")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PlanetHashLBP2")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PlanetHashLBP3")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PlanetHashLBPVita")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("YayHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("UserId");
|
||||
|
|
|
@ -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<string, object>("authTicket", this.AuthTicket), new KeyValuePair<string, object>("lbpEnvVer", this.LbpEnvVer));
|
||||
(
|
||||
new KeyValuePair<string, object>("authTicket", this.AuthTicket),
|
||||
new KeyValuePair<string, object>("lbpEnvVer", this.ServerBrand)
|
||||
);
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
<PackageReference Include="DDSReader" Version="1.0.8-pre" />
|
||||
<PackageReference Include="Discord.Net.Webhook" Version="3.6.1" />
|
||||
<PackageReference Include="Discord.Net.Webhook" Version="3.7.2" />
|
||||
<PackageReference Include="InfluxDB.Client" Version="4.2.0" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="6.0.5" />
|
||||
|
@ -50,10 +50,6 @@
|
|||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Migrations\20220522192158_SwitchToPermissionLevels.Designer.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Exec Command="git describe --long --always --dirty --exclude=\* --abbrev=8 > "$(ProjectDir)/gitVersion.txt"" />
|
||||
<Exec Command="git branch --show-current > "$(ProjectDir)/gitBranch.txt"" />
|
||||
|
|
|
@ -20,7 +20,6 @@ public class DebugWarmupLifetime : IHostLifetime
|
|||
private CancellationTokenRegistration applicationStartedRegistration;
|
||||
|
||||
private readonly ConsoleLifetime consoleLifetime;
|
||||
public static ServerType ServerType;
|
||||
|
||||
public DebugWarmupLifetime
|
||||
(
|
||||
|
@ -39,7 +38,7 @@ public class DebugWarmupLifetime : IHostLifetime
|
|||
{
|
||||
using HttpClient client = new();
|
||||
|
||||
string url = ServerType switch
|
||||
string url = ServerStatics.ServerType switch
|
||||
{
|
||||
ServerType.GameServer => ServerConfiguration.Instance.GameApiListenUrl,
|
||||
ServerType.Website => ServerConfiguration.Instance.WebsiteListenUrl,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
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;
|
||||
|
@ -9,6 +11,7 @@ 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;
|
||||
|
@ -24,18 +27,16 @@ public static class StartupTasks
|
|||
Stopwatch stopwatch = new();
|
||||
stopwatch.Start();
|
||||
|
||||
#if DEBUG
|
||||
DebugWarmupLifetime.ServerType = serverType;
|
||||
#endif
|
||||
ServerStatics.ServerType = serverType;
|
||||
|
||||
// Setup logging
|
||||
Logger.Instance.AddLogger(new ConsoleLogger());
|
||||
Logger.Instance.AddLogger(new LighthouseFileLogger());
|
||||
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 ServerSettings.Instance here loads the config, see ServerSettings.cs for more information
|
||||
// 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);
|
||||
|
@ -46,13 +47,15 @@ public static class StartupTasks
|
|||
}
|
||||
else
|
||||
{
|
||||
Logger.Success("Connected!", LogArea.Startup);
|
||||
Logger.Success("Connected to the database!", LogArea.Startup);
|
||||
}
|
||||
|
||||
if (!dbConnected) Environment.Exit(1);
|
||||
using Database database = new();
|
||||
|
||||
Logger.Info("Migrating database...", LogArea.Database);
|
||||
|
||||
#if !DEBUG
|
||||
if(serverType == ServerType.GameServer)
|
||||
#endif
|
||||
migrateDatabase(database);
|
||||
|
||||
if (ServerConfiguration.Instance.InfluxDB.InfluxEnabled)
|
||||
|
@ -83,25 +86,66 @@ public static class StartupTasks
|
|||
{
|
||||
FileHelper.ConvertAllTexturesToPng();
|
||||
}
|
||||
|
||||
Logger.Info("Starting room cleanup thread...", LogArea.Startup);
|
||||
RoomHelper.StartCleanupThread();
|
||||
|
||||
|
||||
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<CompletedMigration> completedMigrations = database.CompletedMigrations.ToList();
|
||||
List<IMigrationTask> 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();
|
||||
Logger.Success($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LogArea.Database);
|
||||
totalStopwatch.Stop();
|
||||
Logger.Success($"Extra migration tasks took {stopwatch.ElapsedMilliseconds}ms.", LogArea.Database);
|
||||
Logger.Success($"Total migration took {totalStopwatch.ElapsedMilliseconds}ms.", LogArea.Database);
|
||||
}
|
||||
}
|
|
@ -39,8 +39,7 @@ public static class RedisDatabase
|
|||
return;
|
||||
}
|
||||
|
||||
await connection.RecreateIndexAsync(typeof(Room));
|
||||
await connection.RecreateIndexAsync(typeof(UserFriendData));
|
||||
await createIndexes(connection);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
@ -52,6 +51,20 @@ public static class RedisDatabase
|
|||
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);
|
||||
|
|
|
@ -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
|
Loading…
Add table
Reference in a new issue