diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/CoreConfig.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/CoreConfig.cs
index 0118cbcc..51274b0f 100644
--- a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/CoreConfig.cs
+++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/CoreConfig.cs
@@ -56,6 +56,12 @@ public record CoreConfig : BaseConfig
[JsonPropertyName("buildTime")]
public string? BuildTime { get; set; }
+ ///
+ /// Timestamp of server start up
+ ///
+ [JsonPropertyName("serverStartTime")]
+ public long? ServerStartTime { get; set; }
+
///
/// Server locale keys that will be added to the bottom of the startup watermark
///
diff --git a/Libraries/SPTarkov.Server.Core/Services/PostDbLoadService.cs b/Libraries/SPTarkov.Server.Core/Services/PostDbLoadService.cs
index 822663a3..68a44588 100644
--- a/Libraries/SPTarkov.Server.Core/Services/PostDbLoadService.cs
+++ b/Libraries/SPTarkov.Server.Core/Services/PostDbLoadService.cs
@@ -34,6 +34,8 @@ public class PostDbLoadService(
public void PerformPostDbLoadActions()
{
+ _coreConfig.ServerStartTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
+
// Regenerate base cache now mods are loaded and game is starting
// Mods that add items and use the baseClass service generate the cache including their items, the next mod that
// add items gets left out,causing warnings
diff --git a/Libraries/SPTarkov.Server.Core/Status/StatusPage.cs b/Libraries/SPTarkov.Server.Core/Status/StatusPage.cs
new file mode 100644
index 00000000..fe177891
--- /dev/null
+++ b/Libraries/SPTarkov.Server.Core/Status/StatusPage.cs
@@ -0,0 +1,59 @@
+using System.Text;
+using SPTarkov.DI.Annotations;
+using SPTarkov.Server.Core.Models.Spt.Config;
+using SPTarkov.Server.Core.Servers;
+using SPTarkov.Server.Core.Servers.Http;
+using SPTarkov.Server.Core.Services;
+using SPTarkov.Server.Core.Utils;
+
+namespace SPTarkov.Server.Core.Status
+{
+ [Injectable]
+ public class StatusPage(
+ TimeUtil timeUtil,
+ ProfileActivityService profileActivityService,
+ ConfigServer configServer
+ ) : IHttpListener
+ {
+ protected readonly CoreConfig _coreConfig = configServer.GetConfig();
+
+ public bool CanHandle(string sessionId, HttpRequest req)
+ {
+ return req.Method == "GET" && req.Path.Value.Contains("/status");
+ }
+
+ public async Task Handle(string sessionId, HttpRequest req, HttpResponse resp)
+ {
+ var sptVersion = $"SPT version: {ProgramStatics.SPT_VERSION()}";
+ var debugEnabled = $"Debug enabled: {ProgramStatics.DEBUG()}";
+ var modsEnabled = $"Mods enabled: {ProgramStatics.MODS()}";
+ var timeStarted =
+ $"Started : {timeUtil.GetDateTimeFromTimeStamp(_coreConfig.ServerStartTime.Value)}";
+ var uptime =
+ $"Uptime: {DateTimeOffset.UtcNow.ToUnixTimeSeconds() - _coreConfig.ServerStartTime} seconds".ToArray();
+ var activeProfiles = profileActivityService.GetActiveProfileIdsWithinMinutes(30);
+ var activePlayerCount =
+ $"Profiles active in last 30 minutes: {activeProfiles.Count}. {string.Join(",", activeProfiles)}";
+
+ resp.StatusCode = 200;
+ resp.ContentType = "text/html";
+
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes(sptVersion));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes("
"));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes(debugEnabled));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes("
"));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes(modsEnabled));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes("
"));
+
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes(timeStarted));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes("
"));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes(uptime));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes("
"));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes(activePlayerCount));
+ await resp.Body.WriteAsync(Encoding.ASCII.GetBytes("
"));
+
+ await resp.StartAsync();
+ await resp.CompleteAsync();
+ }
+ }
+}