aboutsummaryrefslogtreecommitdiff
path: root/zenserver
diff options
context:
space:
mode:
Diffstat (limited to 'zenserver')
-rw-r--r--zenserver/config.cpp1
-rw-r--r--zenserver/config.h1
-rw-r--r--zenserver/experimental/frontend.cpp119
-rw-r--r--zenserver/experimental/frontend.h24
-rw-r--r--zenserver/zenserver.cpp11
-rw-r--r--zenserver/zenserver.vcxproj2
-rw-r--r--zenserver/zenserver.vcxproj.filters7
7 files changed, 165 insertions, 0 deletions
diff --git a/zenserver/config.cpp b/zenserver/config.cpp
index 42f59b26c..759534d58 100644
--- a/zenserver/config.cpp
+++ b/zenserver/config.cpp
@@ -90,6 +90,7 @@ ParseGlobalCliOptions(int argc, char* argv[], ZenServerOptions& GlobalOptions, Z
options.add_options()("t, test", "Enable test mode", cxxopts::value<bool>(GlobalOptions.IsTest)->default_value("false"));
options.add_options()("log-id", "Specify id for adding context to log output", cxxopts::value<std::string>(GlobalOptions.LogId));
options.add_options()("data-dir", "Specify persistence root", cxxopts::value<std::filesystem::path>(GlobalOptions.DataDir));
+ options.add_options()("content-dir", "Frontend content directory", cxxopts::value<std::filesystem::path>(GlobalOptions.ContentDir));
options
.add_option("lifetime", "", "owner-pid", "Specify owning process id", cxxopts::value<int>(GlobalOptions.OwnerPid), "<identifier>");
diff --git a/zenserver/config.h b/zenserver/config.h
index 75c19d690..af1a24455 100644
--- a/zenserver/config.h
+++ b/zenserver/config.h
@@ -17,6 +17,7 @@ struct ZenServerOptions
bool UninstallService = false; // Flag used to initiate service uninstall (temporary)
std::string LogId; // Id for tagging log output
std::filesystem::path DataDir; // Root directory for state (used for testing)
+ std::filesystem::path ContentDir; // Root directory for serving frontend content (experimental)
};
struct ZenUpstreamJupiterConfig
diff --git a/zenserver/experimental/frontend.cpp b/zenserver/experimental/frontend.cpp
new file mode 100644
index 000000000..79fcf0a17
--- /dev/null
+++ b/zenserver/experimental/frontend.cpp
@@ -0,0 +1,119 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "frontend.h"
+
+#include <zencore/filesystem.h>
+#include <zencore/string.h>
+
+namespace zen {
+
+namespace html {
+
+ constexpr std::string_view Index = R"(
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
+<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-skAcpIdS7UcVUC05LJ9Dxay8AXcDYfBJqt1CJ85S/CFujBsIzCIv+l9liuYLaMQ/" crossorigin="anonymous"></script>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
+<style type="text/css">
+body {
+ background-color: #fafafa;
+}
+</style>
+<script type="text/javascript">
+ const getCacheStats = () => {
+ const opts = { headers: { "Accept": "application/json" } };
+ fetch("/z$", opts)
+ .then(response => {
+ if (!response.ok) {
+ throw Error(response.statusText);
+ }
+ return response.json();
+ })
+ .then(json => {
+ document.getElementById("status").innerHTML = "connected"
+ document.getElementById("stats").innerHTML = JSON.stringify(json, null, 4);
+ })
+ .catch(error => {
+ document.getElementById("status").innerHTML = "disconnected"
+ document.getElementById("stats").innerHTML = ""
+ console.log(error);
+ })
+ .finally(() => {
+ window.setTimeout(getCacheStats, 1000);
+ });
+ };
+ getCacheStats();
+</script>
+</head>
+<body>
+ <div class="container">
+ <div class="row">
+ <div class="text-center mt-5">
+ <pre>
+__________ _________ __
+\____ / ____ ____ / _____/_/ |_ ____ _______ ____
+ / / _/ __ \ / \ \_____ \ \ __\ / _ \ \_ __ \_/ __ \
+ / /_ \ ___/ | | \ / \ | | ( <_> ) | | \/\ ___/
+/_______ \ \___ >|___| //_______ / |__| \____/ |__| \___ >
+ \/ \/ \/ \/ \/
+ </pre>
+ <pre id="status"/>
+ </div>
+ </div>
+ <div class="row">
+ <pre class="mb-0">Z$:</pre>
+ <pre id="stats"></pre>
+ <div>
+ </div>
+</body>
+</html>
+)";
+
+} // namespace html
+
+HttpFrontendService::HttpFrontendService(std::filesystem::path Directory) : m_Directory(Directory)
+{
+}
+
+HttpFrontendService::~HttpFrontendService()
+{
+}
+
+const char*
+HttpFrontendService::BaseUri() const
+{
+ return "/dashboard"; // in order to use the root path we need to remove HttpAddUrlToUrlGroup in HttpSys.cpp
+}
+
+void
+HttpFrontendService::HandleRequest(zen::HttpServerRequest& Request)
+{
+ using namespace std::literals;
+
+ if (m_Directory.empty())
+ {
+ Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, html::Index);
+ }
+ else
+ {
+ std::string_view Uri = Request.RelativeUri();
+ std::filesystem::path RelPath{Uri.empty() ? "index.html" : Uri};
+ std::filesystem::path AbsPath = m_Directory / RelPath;
+
+ FileContents File = ReadFile(AbsPath);
+
+ if (!File.ErrorCode)
+ {
+ // TODO: Map file extension to MIME type
+ Request.WriteResponse(HttpResponseCode::OK, HttpContentType::kHTML, File.Data[0]);
+ }
+ else
+ {
+ return Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Ooops!"sv);
+ }
+ }
+}
+
+} // namespace zen
diff --git a/zenserver/experimental/frontend.h b/zenserver/experimental/frontend.h
new file mode 100644
index 000000000..2ae20e940
--- /dev/null
+++ b/zenserver/experimental/frontend.h
@@ -0,0 +1,24 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <zenhttp/httpserver.h>
+
+#include <filesystem>
+
+namespace zen {
+
+class HttpFrontendService final : public zen::HttpService
+{
+public:
+ HttpFrontendService(std::filesystem::path Directory);
+ virtual ~HttpFrontendService();
+
+ virtual const char* BaseUri() const override;
+ virtual void HandleRequest(zen::HttpServerRequest& Request) override;
+
+private:
+ std::filesystem::path m_Directory;
+};
+
+} // namespace zen
diff --git a/zenserver/zenserver.cpp b/zenserver/zenserver.cpp
index b45df9fef..db1be9dea 100644
--- a/zenserver/zenserver.cpp
+++ b/zenserver/zenserver.cpp
@@ -81,6 +81,7 @@
#include "cache/structuredcachestore.h"
#include "compute/apply.h"
#include "diag/diagsvcs.h"
+#include "experimental/frontend.h"
#include "experimental/usnjournal.h"
#include "projectstore.h"
#include "testing/httptest.h"
@@ -302,6 +303,12 @@ public:
{
m_Http->RegisterService(*m_HttpFunctionService);
}
+
+ m_FrontendService = std::make_unique<HttpFrontendService>(m_ContentRoot);
+ if (m_FrontendService)
+ {
+ m_Http->RegisterService(*m_FrontendService);
+ }
}
#if ZEN_ENABLE_MESH
@@ -364,6 +371,7 @@ public:
void SetDedicatedMode(bool State) { m_IsDedicatedMode = State; }
void SetTestMode(bool State) { m_TestMode = State; }
void SetDataRoot(std::filesystem::path Root) { m_DataRoot = Root; }
+ void SetContentRoot(std::filesystem::path Root) { m_ContentRoot = Root; }
void EnsureIoRunner()
{
@@ -447,6 +455,7 @@ private:
bool m_IsDedicatedMode = false;
bool m_TestMode = false;
std::filesystem::path m_DataRoot;
+ std::filesystem::path m_ContentRoot;
std::jthread m_IoRunner;
asio::io_context m_IoContext;
asio::steady_timer m_PidCheckTimer{m_IoContext};
@@ -471,6 +480,7 @@ private:
zen::HttpHealthService m_HealthService;
zen::Mesh m_ZenMesh{m_IoContext};
std::unique_ptr<zen::HttpFunctionService> m_HttpFunctionService;
+ std::unique_ptr<zen::HttpFrontendService> m_FrontendService;
bool m_DebugOptionForcedCrash = false;
};
@@ -560,6 +570,7 @@ ZenWindowsService::Run()
ZenServer Server;
Server.SetDataRoot(GlobalOptions.DataDir);
+ Server.SetContentRoot(GlobalOptions.ContentDir);
Server.SetTestMode(GlobalOptions.IsTest);
Server.SetDedicatedMode(GlobalOptions.IsDedicated);
Server.Initialize(ServiceConfig, GlobalOptions.BasePort, GlobalOptions.OwnerPid, Entry);
diff --git a/zenserver/zenserver.vcxproj b/zenserver/zenserver.vcxproj
index bcb7ea028..335786fbf 100644
--- a/zenserver/zenserver.vcxproj
+++ b/zenserver/zenserver.vcxproj
@@ -109,6 +109,7 @@
<ClInclude Include="compute\apply.h" />
<ClInclude Include="config.h" />
<ClInclude Include="diag\logging.h" />
+ <ClInclude Include="experimental\frontend.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="sos\sos.h" />
<ClInclude Include="testing\httptest.h" />
@@ -132,6 +133,7 @@
<ClCompile Include="compute\apply.cpp" />
<ClCompile Include="config.cpp" />
<ClCompile Include="diag\logging.cpp" />
+ <ClCompile Include="experimental\frontend.cpp" />
<ClCompile Include="projectstore.cpp" />
<ClCompile Include="cache\cacheagent.cpp" />
<ClCompile Include="sos\sos.cpp" />
diff --git a/zenserver/zenserver.vcxproj.filters b/zenserver/zenserver.vcxproj.filters
index 6b99ca8d7..1c5b17fee 100644
--- a/zenserver/zenserver.vcxproj.filters
+++ b/zenserver/zenserver.vcxproj.filters
@@ -38,6 +38,9 @@
<ClInclude Include="testing\httptest.h" />
<ClInclude Include="windows\service.h" />
<ClInclude Include="resource.h" />
+ <ClInclude Include="experimental\frontend.h">
+ <Filter>experimental</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="zenserver.cpp" />
@@ -70,6 +73,10 @@
</ClCompile>
<ClCompile Include="testing\httptest.cpp" />
<ClCompile Include="windows\service.cpp" />
+ <ClCompile Include="admin\admin.cpp" />
+ <ClCompile Include="experimental\frontend.cpp">
+ <Filter>experimental</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="cache">