mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-08-01 01:38:39 +00:00
Merge branch 'main' of https://github.com/LBPUnion/project-lighthouse
This commit is contained in:
commit
e82c2afbe4
30 changed files with 400 additions and 110 deletions
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
@ -60,13 +60,13 @@ jobs:
|
|||
|
||||
- name: Process Test Results (Control)
|
||||
if: ${{ matrix.os.prettyName == 'Linux' }}
|
||||
uses: im-open/process-dotnet-test-results@v2.0.0
|
||||
uses: im-open/process-dotnet-test-results@v2.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Process Test Results
|
||||
if: ${{ matrix.os.prettyName != 'Linux' }}
|
||||
uses: im-open/process-dotnet-test-results@v2.0.0
|
||||
uses: im-open/process-dotnet-test-results@v2.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
create-status-check: false
|
||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -13,4 +13,7 @@ riderModule.iml
|
|||
/ProjectLighthouse/ProjectLighthouse.csproj.user
|
||||
.vs/
|
||||
.vscode/
|
||||
.editorconfig
|
||||
.editorconfig
|
||||
lighthouse.config.json
|
||||
gitBranch.txt
|
||||
gitVersion.txt
|
||||
|
|
|
@ -8,8 +8,9 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
{
|
||||
public DatabaseFact()
|
||||
{
|
||||
ServerSettings.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
|
||||
if (!ServerSettings.DbConnected)
|
||||
ServerSettings.Instance = new ServerSettings();
|
||||
ServerSettings.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
|
||||
if (!ServerStatics.DbConnected)
|
||||
{
|
||||
this.Skip = "Database not available";
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
@ -55,6 +56,14 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
return this.Client.SendAsync(requestMessage);
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> UploadFileEndpointRequest(string filePath)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(await File.ReadAllTextAsync(filePath));
|
||||
string hash = HashHelper.Sha1Hash(bytes);
|
||||
|
||||
return await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/upload/{hash}", new ByteArrayContent(bytes));
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> UploadFileRequest(string endpoint, string filePath)
|
||||
=> await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath)));
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
Assert.True(response.IsSuccessStatusCode);
|
||||
string responseContent = await response.Content.ReadAsStringAsync();
|
||||
Assert.Contains("MM_AUTH=", responseContent);
|
||||
Assert.Contains(ServerSettings.ServerName, responseContent);
|
||||
Assert.Contains(ServerStatics.ServerName, responseContent);
|
||||
}
|
||||
|
||||
[DatabaseFact]
|
||||
|
@ -41,7 +41,7 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
Assert.NotNull(loginResult.LbpEnvVer);
|
||||
|
||||
Assert.Contains("MM_AUTH=", loginResult.AuthTicket);
|
||||
Assert.Equal(ServerSettings.ServerName, loginResult.LbpEnvVer);
|
||||
Assert.Equal(ServerStatics.ServerName, loginResult.LbpEnvVer);
|
||||
}
|
||||
|
||||
[DatabaseFact]
|
||||
|
@ -59,7 +59,7 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
[DatabaseFact]
|
||||
public async Task ShouldReturnForbiddenWhenNotAuthenticated()
|
||||
{
|
||||
HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/eula");
|
||||
HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/announce");
|
||||
Assert.False(response.IsSuccessStatusCode);
|
||||
Assert.True(response.StatusCode == HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
|
|
@ -17,13 +17,18 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
User userA = await database.CreateUser("unitTestUser0");
|
||||
User userB = await database.CreateUser("unitTestUser1");
|
||||
|
||||
Location l = new();
|
||||
Location l = new()
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
};
|
||||
database.Locations.Add(l);
|
||||
await database.SaveChangesAsync();
|
||||
|
||||
Slot slotA = new()
|
||||
{
|
||||
Creator = userA,
|
||||
CreatorId = userA.UserId,
|
||||
Name = "slotA",
|
||||
Location = l,
|
||||
LocationId = l.Id,
|
||||
|
@ -33,6 +38,7 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
Slot slotB = new()
|
||||
{
|
||||
Creator = userB,
|
||||
CreatorId = userB.UserId,
|
||||
Name = "slotB",
|
||||
Location = l,
|
||||
LocationId = l.Id,
|
||||
|
@ -49,8 +55,10 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
|
||||
LoginResult loginResult = await this.Authenticate();
|
||||
|
||||
HttpResponseMessage respMessageA = await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser0", loginResult.AuthTicket);
|
||||
HttpResponseMessage respMessageB = await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser1", loginResult.AuthTicket);
|
||||
HttpResponseMessage respMessageA = await this.AuthenticatedRequest
|
||||
("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser0&pageStart=1&pageSize=1", loginResult.AuthTicket);
|
||||
HttpResponseMessage respMessageB = await this.AuthenticatedRequest
|
||||
("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser1&pageStart=1&pageSize=1", loginResult.AuthTicket);
|
||||
|
||||
Assert.True(respMessageA.IsSuccessStatusCode);
|
||||
Assert.True(respMessageB.IsSuccessStatusCode);
|
||||
|
|
|
@ -17,35 +17,35 @@ namespace LBPUnion.ProjectLighthouse.Tests
|
|||
[Fact]
|
||||
public async Task ShouldNotAcceptScript()
|
||||
{
|
||||
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/scriptTest", "ExampleFiles/TestScript.ff");
|
||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestScript.ff");
|
||||
Assert.False(response.IsSuccessStatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ShouldNotAcceptFarc()
|
||||
{
|
||||
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/farcTest", "ExampleFiles/TestFarc.farc");
|
||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestFarc.farc");
|
||||
Assert.False(response.IsSuccessStatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ShouldNotAcceptGarbage()
|
||||
{
|
||||
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/garbageTest", "ExampleFiles/TestGarbage.bin");
|
||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestGarbage.bin");
|
||||
Assert.False(response.IsSuccessStatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ShouldAcceptTexture()
|
||||
{
|
||||
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/textureTest", "ExampleFiles/TestTexture.tex");
|
||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestTexture.tex");
|
||||
Assert.True(response.IsSuccessStatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ShouldAcceptLevel()
|
||||
{
|
||||
HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/levelTest", "ExampleFiles/TestLevel.lvl");
|
||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestLevel.lvl");
|
||||
Assert.True(response.IsSuccessStatusCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
return this.Ok();
|
||||
}
|
||||
|
||||
[HttpPost("lolcatftw/clear")]
|
||||
public async Task<IActionResult> ClearQueuedLevels()
|
||||
{
|
||||
User? user = await this.database.UserFromRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == user.UserId));
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
return this.Ok();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hearted Levels
|
||||
|
|
|
@ -65,7 +65,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
new LoginResult
|
||||
{
|
||||
AuthTicket = "MM_AUTH=" + token.UserToken,
|
||||
LbpEnvVer = ServerSettings.ServerName,
|
||||
LbpEnvVer = ServerStatics.ServerName,
|
||||
}.Serialize()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using Kettu;
|
|||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
||||
|
@ -21,11 +22,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("eula")]
|
||||
public async Task<IActionResult> Eula()
|
||||
{
|
||||
User user = await this.database.UserFromRequest(this.Request);
|
||||
return user == null ? this.StatusCode(403, "") : this.Ok(EulaHelper.PrivateInstanceNoticeOrBlank + "\n" + $"{EulaHelper.License}\n");
|
||||
}
|
||||
public IActionResult Eula() => this.Ok(ServerSettings.Instance.EulaText + "\n" + $"{EulaHelper.License}\n");
|
||||
|
||||
[HttpGet("announce")]
|
||||
public async Task<IActionResult> Announce()
|
||||
|
@ -33,7 +30,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
User user = await this.database.UserFromRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
return this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n\n" + EulaHelper.PrivateInstanceNoticeOrBlank);
|
||||
return this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n\n" + ServerSettings.Instance.EulaText);
|
||||
}
|
||||
|
||||
[HttpGet("notification")]
|
||||
|
|
|
@ -51,10 +51,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
|
||||
// TODO: check if this is a valid hash
|
||||
[HttpPost("upload/{hash}")]
|
||||
[AllowSynchronousIo]
|
||||
public async Task<IActionResult> UploadResource(string hash)
|
||||
{
|
||||
|
||||
string assetsDirectory = FileHelper.ResourcePath;
|
||||
string path = FileHelper.GetResourcePath(hash);
|
||||
|
||||
|
@ -70,6 +68,12 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
return this.UnprocessableEntity();
|
||||
}
|
||||
|
||||
if (HashHelper.Sha1Hash(file.Data) != hash)
|
||||
{
|
||||
Logger.Log($"File hash does not match the uploaded file! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
|
||||
return this.Conflict();
|
||||
}
|
||||
|
||||
Logger.Log($"File is OK! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
|
||||
await IOFile.WriteAllBytesAsync(path, file.Data);
|
||||
return this.Ok();
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
.Include(s => s.Location)
|
||||
.Where(s => s.Creator!.Username == user.Username)
|
||||
.Skip(pageStart - 1)
|
||||
.Take(Math.Min(pageSize, ServerSettings.EntitledSlots)),
|
||||
.Take(Math.Min(pageSize, ServerStatics.EntitledSlots)),
|
||||
string.Empty,
|
||||
(current, slot) => current + slot.Serialize()
|
||||
);
|
||||
|
@ -56,7 +56,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.EntitledSlots)
|
||||
"hint_start", pageStart + Math.Min(pageSize, ServerStatics.EntitledSlots)
|
||||
},
|
||||
{
|
||||
"total", user.UsedSlots
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
||||
{
|
||||
|
@ -20,18 +18,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
|
||||
[HttpGet("playersInPodCount")]
|
||||
[HttpGet("totalPlayerCount")]
|
||||
public async Task<IActionResult> TotalPlayerCount()
|
||||
{
|
||||
int recentMatches = await this.database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 60).CountAsync();
|
||||
|
||||
return this.Ok(recentMatches.ToString());
|
||||
}
|
||||
public async Task<IActionResult> TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!);
|
||||
|
||||
[HttpGet("planetStats")]
|
||||
public async Task<IActionResult> PlanetStats()
|
||||
{
|
||||
int totalSlotCount = await this.database.Slots.CountAsync();
|
||||
int mmPicksCount = await this.database.Slots.CountAsync(s => s.TeamPick);
|
||||
int totalSlotCount = await StatisticsHelper.SlotCount();
|
||||
int mmPicksCount = await StatisticsHelper.MMPicksCount();
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
|
@ -41,6 +34,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("planetStats/totalLevelCount")]
|
||||
public async Task<IActionResult> TotalLevelCount() => this.Ok((await this.database.Slots.CountAsync()).ToString());
|
||||
public async Task<IActionResult> TotalLevelCount() => this.Ok((await StatisticsHelper.SlotCount()).ToString());
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ namespace LBPUnion.ProjectLighthouse
|
|||
public DbSet<RatedReview> RatedReviews { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
=> options.UseMySql(ServerSettings.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
|
||||
=> options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
|
||||
|
||||
public async Task<User> CreateUser(string username)
|
||||
{
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
||||
{
|
||||
// Yoinked from https://stackoverflow.com/a/68530667
|
||||
// Thanks to T-moty!
|
||||
/// <summary>
|
||||
/// Allows synchronous stream operations for this request.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class AllowSynchronousIoAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
IHttpBodyControlFeature syncIoFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>();
|
||||
if (syncIoFeature != null) syncIoFeature.AllowSynchronousIO = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
||||
{
|
||||
public static class EulaHelper
|
||||
|
@ -17,13 +15,5 @@ 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 <https://www.gnu.org/licenses/>.";
|
||||
|
||||
public const string PrivateInstanceNotice = @"This server is a private testing instance.
|
||||
Please do not make anything public for now, and keep in mind security isn't as tight as a full release would.";
|
||||
|
||||
[SuppressMessage("ReSharper", "HeuristicUnreachableCode")]
|
||||
public const string PrivateInstanceNoticeOrBlank = ShowPrivateInstanceNotice ? PrivateInstanceNotice : "";
|
||||
|
||||
public const bool ShowPrivateInstanceNotice = false;
|
||||
}
|
||||
}
|
|
@ -78,6 +78,18 @@ namespace LBPUnion.ProjectLighthouse.Helpers
|
|||
|
||||
public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash));
|
||||
|
||||
public static int ResourceSize(string hash)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (int)new FileInfo(GetResourcePath(hash)).Length;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnsureDirectoryCreated(string path)
|
||||
{
|
||||
if (!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path)));
|
||||
|
|
56
ProjectLighthouse/Helpers/GitVersionHelper.cs
Normal file
56
ProjectLighthouse/Helpers/GitVersionHelper.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Kettu;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
||||
{
|
||||
public static class GitVersionHelper
|
||||
{
|
||||
static GitVersionHelper()
|
||||
{
|
||||
try
|
||||
{
|
||||
CommitHash = readManifestFile("gitVersion.txt");
|
||||
Branch = readManifestFile("gitBranch.txt");
|
||||
CanCheckForUpdates = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Log
|
||||
(
|
||||
"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
|
||||
);
|
||||
CommitHash = "invalid";
|
||||
Branch = "invalid";
|
||||
CanCheckForUpdates = false;
|
||||
}
|
||||
|
||||
if (IsDirty)
|
||||
{
|
||||
Logger.Log
|
||||
(
|
||||
"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
|
||||
);
|
||||
CanCheckForUpdates = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string readManifestFile(string fileName)
|
||||
{
|
||||
using Stream stream = typeof(Program).Assembly.GetManifestResourceStream($"{typeof(Program).Namespace}.{fileName}");
|
||||
using StreamReader reader = new(stream ?? throw new Exception("The assembly or manifest resource is null."));
|
||||
|
||||
return reader.ReadToEnd().Trim();
|
||||
}
|
||||
|
||||
public static string CommitHash { get; set; }
|
||||
public static string Branch { get; set; }
|
||||
public static bool IsDirty => CommitHash.EndsWith("-dirty");
|
||||
public static bool CanCheckForUpdates { get; set; }
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
|
|||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static class HashHelper
|
||||
{
|
||||
// private static readonly SHA1 sha1 = SHA1.Create();
|
||||
private static readonly SHA1 sha1 = SHA1.Create();
|
||||
private static readonly SHA256 sha256 = SHA256.Create();
|
||||
private static readonly Random random = new();
|
||||
|
||||
|
@ -67,11 +67,11 @@ namespace LBPUnion.ProjectLighthouse.Helpers
|
|||
|
||||
public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str));
|
||||
|
||||
public static string Sha256Hash(byte[] bytes)
|
||||
{
|
||||
byte[] hash = sha256.ComputeHash(bytes);
|
||||
return Encoding.UTF8.GetString(hash, 0, hash.Length);
|
||||
}
|
||||
public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", "");
|
||||
|
||||
public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str));
|
||||
|
||||
public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(sha1.ComputeHash(bytes)).Replace("-", "");
|
||||
|
||||
public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str);
|
||||
|
||||
|
|
50
ProjectLighthouse/Helpers/InfluxHelper.cs
Normal file
50
ProjectLighthouse/Helpers/InfluxHelper.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using InfluxDB.Client;
|
||||
using InfluxDB.Client.Writes;
|
||||
using Kettu;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
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 async void Log()
|
||||
{
|
||||
using WriteApi writeApi = Client.GetWriteApi();
|
||||
PointData point = PointData.Measurement("lighthouse")
|
||||
.Field("playerCount", await StatisticsHelper.RecentMatches())
|
||||
.Field("slotCount", await StatisticsHelper.SlotCount());
|
||||
|
||||
writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point);
|
||||
|
||||
writeApi.Flush();
|
||||
}
|
||||
|
||||
public static async Task StartLogging()
|
||||
{
|
||||
await Client.ReadyAsync();
|
||||
Logger.Log("InfluxDB is now ready.", LoggerLevelInflux.Instance);
|
||||
Thread t = new
|
||||
(
|
||||
delegate()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
#pragma warning disable CS4014
|
||||
Log();
|
||||
#pragma warning restore CS4014
|
||||
// Logger.Log("Logged.", LoggerLevelInflux.Instance);
|
||||
Thread.Sleep(60000);
|
||||
}
|
||||
}
|
||||
);
|
||||
t.IsBackground = true;
|
||||
t.Name = "InfluxDB Logger";
|
||||
t.Start();
|
||||
}
|
||||
}
|
||||
}
|
17
ProjectLighthouse/Helpers/StatisticsHelper.cs
Normal file
17
ProjectLighthouse/Helpers/StatisticsHelper.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
||||
{
|
||||
public static class StatisticsHelper
|
||||
{
|
||||
private static readonly Database database = new();
|
||||
|
||||
public static async Task<int> RecentMatches() => await database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync();
|
||||
|
||||
public static async Task<int> SlotCount() => await database.Slots.CountAsync();
|
||||
|
||||
public static async Task<int> MMPicksCount() => await database.Slots.CountAsync(s => s.TeamPick);
|
||||
}
|
||||
}
|
27
ProjectLighthouse/Logging/InfluxLogger.cs
Normal file
27
ProjectLighthouse/Logging/InfluxLogger.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
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 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);
|
||||
}
|
||||
public override bool AllowMultiple => false;
|
||||
}
|
||||
}
|
|
@ -51,6 +51,18 @@ namespace LBPUnion.ProjectLighthouse.Logging
|
|||
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 LoggerLevelAspNet : LoggerLevel
|
||||
{
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using Kettu;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
@ -28,8 +29,13 @@ namespace LBPUnion.ProjectLighthouse
|
|||
Logger.AddLogger(new LighthouseFileLogger());
|
||||
|
||||
Logger.Log("Welcome to Project Lighthouse!", LoggerLevelStartup.Instance);
|
||||
Logger.Log($"Running {ServerStatics.ServerName} {GitVersionHelper.CommitHash}@{GitVersionHelper.Branch}", LoggerLevelStartup.Instance);
|
||||
|
||||
// This 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 = ServerSettings.DbConnected;
|
||||
bool dbConnected = ServerStatics.DbConnected;
|
||||
Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance);
|
||||
|
||||
if (!dbConnected) Environment.Exit(1);
|
||||
|
@ -38,6 +44,15 @@ namespace LBPUnion.ProjectLighthouse
|
|||
Logger.Log("Migrating database...", LoggerLevelDatabase.Instance);
|
||||
MigrateDatabase(database);
|
||||
|
||||
if (ServerSettings.Instance.InfluxEnabled)
|
||||
{
|
||||
Logger.Log("Influx logging is enabled. Starting influx logging...", LoggerLevelStartup.Instance);
|
||||
#pragma warning disable CS4014
|
||||
InfluxHelper.StartLogging();
|
||||
#pragma warning restore CS4014
|
||||
if (ServerSettings.Instance.InfluxLoggingEnabled) Logger.AddLogger(new InfluxLogger());
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance);
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.2"/>
|
||||
<PackageReference Include="InfluxDB.Client" Version="3.1.0"/>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0"/>
|
||||
<PackageReference Include="Kettu" Version="1.2.1"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="6.0.0"/>
|
||||
|
@ -21,7 +22,19 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Types\SlotXsd.cs"/>
|
||||
<None Remove="gitVersion.txt"/>
|
||||
<EmbeddedResource Include="gitVersion.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<None Remove="gitBranch.txt"/>
|
||||
<EmbeddedResource Include="gitBranch.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</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""/>
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Types.Reviews;
|
||||
|
@ -206,7 +207,8 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels
|
|||
|
||||
public string SerializeResources()
|
||||
{
|
||||
return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource));
|
||||
return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)) +
|
||||
LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize));
|
||||
}
|
||||
|
||||
public string Serialize(RatedLevel? yourRatingStats = null, VisitedLevel? yourVisitedStats = null, Review? yourReview = null)
|
||||
|
|
|
@ -1,44 +1,91 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
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
|
||||
{
|
||||
public static class ServerSettings
|
||||
[Serializable]
|
||||
public class ServerSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum amount of slots allowed on users' earth
|
||||
/// </summary>
|
||||
public const int EntitledSlots = 50;
|
||||
static ServerSettings()
|
||||
{
|
||||
if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own
|
||||
|
||||
public const int ListsQuota = 50;
|
||||
if (File.Exists(ConfigFileName))
|
||||
{
|
||||
string configFile = File.ReadAllText(ConfigFileName);
|
||||
|
||||
public const string ServerName = "ProjectLighthouse";
|
||||
Instance = JsonSerializer.Deserialize<ServerSettings>(configFile) ?? throw new ArgumentNullException(nameof(ConfigFileName));
|
||||
|
||||
private static string? dbConnectionString;
|
||||
if (Instance.ConfigVersion >= CurrentConfigVersion) return;
|
||||
|
||||
public static string DbConnectionString {
|
||||
get {
|
||||
if (dbConnectionString == null) return dbConnectionString = Environment.GetEnvironmentVariable("LIGHTHOUSE_DB_CONNECTION_STRING") ?? "";
|
||||
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,
|
||||
}
|
||||
);
|
||||
|
||||
return dbConnectionString;
|
||||
File.WriteAllText(ConfigFileName, configFile);
|
||||
}
|
||||
set => dbConnectionString = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
string configFile = JsonSerializer.Serialize
|
||||
(
|
||||
new ServerSettings(),
|
||||
typeof(ServerSettings),
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
}
|
||||
);
|
||||
|
||||
public static bool DbConnected {
|
||||
get {
|
||||
try
|
||||
{
|
||||
return new Database().Database.CanConnect();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Logger.Log(e.ToString(), LoggerLevelDatabase.Instance);
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#region Meta
|
||||
|
||||
[NotNull]
|
||||
public static ServerSettings Instance;
|
||||
|
||||
public const int CurrentConfigVersion = 4;
|
||||
|
||||
[JsonPropertyName("ConfigVersionDoNotModifyOrYouWillBeSlapped")]
|
||||
public int ConfigVersion { get; set; } = CurrentConfigVersion;
|
||||
|
||||
public const string ConfigFileName = "lighthouse.config.json";
|
||||
|
||||
#endregion Meta
|
||||
|
||||
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; } = "";
|
||||
|
||||
public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
|
||||
}
|
||||
}
|
36
ProjectLighthouse/Types/Settings/ServerStatics.cs
Normal file
36
ProjectLighthouse/Types/Settings/ServerStatics.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Kettu;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Settings
|
||||
{
|
||||
public static class ServerStatics
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum amount of slots allowed on users' earth
|
||||
/// </summary>
|
||||
public const int EntitledSlots = 50;
|
||||
|
||||
public const int ListsQuota = 50;
|
||||
|
||||
public const string ServerName = "ProjectLighthouse";
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
|
@ -105,7 +105,7 @@ namespace LBPUnion.ProjectLighthouse.Types
|
|||
LbpSerializer.StringElement("game", this.Game) +
|
||||
this.SerializeSlots(gameVersion == GameVersion.LittleBigPlanetVita) +
|
||||
LbpSerializer.StringElement("lists", this.Lists) +
|
||||
LbpSerializer.StringElement("lists_quota", ServerSettings.ListsQuota) + // technically not a part of the user but LBP expects it
|
||||
LbpSerializer.StringElement("lists_quota", ServerStatics.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) +
|
||||
|
@ -147,7 +147,7 @@ namespace LBPUnion.ProjectLighthouse.Types
|
|||
/// <summary>
|
||||
/// The number of slots remaining on the earth
|
||||
/// </summary>
|
||||
public int FreeSlots => ServerSettings.EntitledSlots - this.UsedSlots;
|
||||
public int FreeSlots => ServerStatics.EntitledSlots - this.UsedSlots;
|
||||
|
||||
private static readonly string[] slotTypes =
|
||||
{
|
||||
|
@ -177,12 +177,12 @@ namespace LBPUnion.ProjectLighthouse.Types
|
|||
slotTypesLocal = slotTypes;
|
||||
}
|
||||
|
||||
slots += LbpSerializer.StringElement("entitledSlots", ServerSettings.EntitledSlots);
|
||||
slots += LbpSerializer.StringElement("entitledSlots", ServerStatics.EntitledSlots);
|
||||
slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots);
|
||||
|
||||
foreach (string slotType in slotTypesLocal)
|
||||
{
|
||||
slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerSettings.EntitledSlots);
|
||||
slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerStatics.EntitledSlots);
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0);
|
||||
slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots);
|
||||
|
|
|
@ -62,7 +62,7 @@ Finally, take a break. Chances are that took a while.
|
|||
|
||||
## Contributing Tips
|
||||
|
||||
### Database
|
||||
### Database migrations
|
||||
|
||||
Some modifications may require updates to the database schema. You can automatically create a migration file by:
|
||||
|
||||
|
@ -72,6 +72,12 @@ Some modifications may require updates to the database schema. You can automatic
|
|||
you're doing.
|
||||
4. Running `dotnet ef migrations add <NameOfMigrationInPascalCase> --project ProjectLighthouse`.
|
||||
|
||||
### Running tests
|
||||
|
||||
You can run tests either through your IDE or by running `dotnet tests`.
|
||||
|
||||
Keep in mind while running database tests you need to have `LIGHTHOUSE_DB_CONNECTION_STRING` set.
|
||||
|
||||
## Compatibility across games and platforms
|
||||
|
||||
| Game | Console (PS3/Vita) | Emulator (RPCS3) | Next-Gen (PS4/PS5) |
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue