Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom URL #1307

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 49 additions & 38 deletions src/HttpServer/HttpServer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ uint32_t HttpServer::_scripting_request_id = 0;

HttpServer::HttpServer(std::shared_ptr<SessionManager> session_manager, fs::path root_folder, fs::path user_directory,
std::string auth_token, bool read_only_mode, bool enable_frontend, bool enable_database, bool enable_scripting,
bool enable_runtime_config)
bool enable_runtime_config, std::string url_prefix)
: _session_manager(session_manager),
_http_root_folder(root_folder),
_auth_token(auth_token),
Expand All @@ -39,7 +39,8 @@ HttpServer::HttpServer(std::shared_ptr<SessionManager> session_manager, fs::path
_enable_frontend(enable_frontend),
_enable_database(enable_database),
_enable_scripting(enable_scripting),
_enable_runtime_config(enable_runtime_config) {
_enable_runtime_config(enable_runtime_config),
_url_prefix(url_prefix) {
if (_enable_frontend && !root_folder.empty()) {
_frontend_found = IsValidFrontendFolder(root_folder);

Expand All @@ -55,54 +56,54 @@ void HttpServer::RegisterRoutes() {
uWS::App& app = _session_manager->App();

if (_enable_scripting) {
app.post("/api/scripting/action", [&](auto res, auto req) { HandleScriptingAction(res, req); });
app.post(fmt::format("{}/api/scripting/action", _url_prefix), [&](auto res, auto req) { HandleScriptingAction(res, req); });
} else {
app.post("/api/scripting/action", [&](auto res, auto req) { NotImplemented(res, req); });
app.post(fmt::format("{}/api/scripting/action", _url_prefix), [&](auto res, auto req) { NotImplemented(res, req); });
}

if (_enable_database) {
// Dynamic routes for preferences, layouts, snippets and workspaces
app.get("/api/database/preferences", [&](auto res, auto req) { HandleGetPreferences(res, req); });
app.put("/api/database/preferences", [&](auto res, auto req) { HandleSetPreferences(res, req); });
app.del("/api/database/preferences", [&](auto res, auto req) { HandleClearPreferences(res, req); });

app.get("/api/database/list/layouts", [&](auto res, auto req) { HandleGetObjectList("layout", res, req); });
app.get("/api/database/layouts", [&](auto res, auto req) { HandleGetObjects("layout", res, req); });
app.get("/api/database/layout/:name", [&](auto res, auto req) { HandleGetObject("layout", res, req); });
app.put("/api/database/layout", [&](auto res, auto req) { HandleSetObject("layout", res, req); });
app.del("/api/database/layout", [&](auto res, auto req) { HandleClearObject("layout", res, req); });

app.get("/api/database/list/snippets", [&](auto res, auto req) { HandleGetObjectList("snippet", res, req); });
app.get("/api/database/snippets", [&](auto res, auto req) { HandleGetObjects("snippet", res, req); });
app.get("/api/database/snippet/:name", [&](auto res, auto req) { HandleGetObject("snippet", res, req); });
app.put("/api/database/snippet", [&](auto res, auto req) { HandleSetObject("snippet", res, req); });
app.del("/api/database/snippet", [&](auto res, auto req) { HandleClearObject("snippet", res, req); });

app.get("/api/database/list/workspaces", [&](auto res, auto req) { HandleGetObjectList("workspace", res, req); });
app.get("/api/database/workspaces", [&](auto res, auto req) { HandleGetObjects("workspace", res, req); });
app.get("/api/database/workspace/:name", [&](auto res, auto req) { HandleGetObject("workspace", res, req); });
app.put("/api/database/workspace", [&](auto res, auto req) { HandleSetObject("workspace", res, req); });
app.del("/api/database/workspace", [&](auto res, auto req) { HandleClearObject("workspace", res, req); });
app.get(fmt::format("{}/api/database/preferences", _url_prefix), [&](auto res, auto req) { HandleGetPreferences(res, req); });
app.put(fmt::format("{}/api/database/preferences", _url_prefix), [&](auto res, auto req) { HandleSetPreferences(res, req); });
app.del(fmt::format("{}/api/database/preferences", _url_prefix), [&](auto res, auto req) { HandleClearPreferences(res, req); });

app.get(fmt::format("{}/api/database/list/layouts", _url_prefix), [&](auto res, auto req) { HandleGetObjectList("layout", res, req); });
app.get(fmt::format("{}/api/database/layouts", _url_prefix), [&](auto res, auto req) { HandleGetObjects("layout", res, req); });
app.get(fmt::format("{}/api/database/layout/:name", _url_prefix), [&](auto res, auto req) { HandleGetObject("layout", res, req); });
app.put(fmt::format("{}/api/database/layout", _url_prefix), [&](auto res, auto req) { HandleSetObject("layout", res, req); });
app.del(fmt::format("{}/api/database/layout", _url_prefix), [&](auto res, auto req) { HandleClearObject("layout", res, req); });

app.get(fmt::format("{}/api/database/list/snippets", _url_prefix), [&](auto res, auto req) { HandleGetObjectList("snippet", res, req); });
app.get(fmt::format("{}/api/database/snippets", _url_prefix), [&](auto res, auto req) { HandleGetObjects("snippet", res, req); });
app.get(fmt::format("{}/api/database/snippet/:name", _url_prefix), [&](auto res, auto req) { HandleGetObject("snippet", res, req); });
app.put(fmt::format("{}/api/database/snippet", _url_prefix), [&](auto res, auto req) { HandleSetObject("snippet", res, req); });
app.del(fmt::format("{}/api/database/snippet", _url_prefix), [&](auto res, auto req) { HandleClearObject("snippet", res, req); });

app.get(fmt::format("{}/api/database/list/workspaces", _url_prefix), [&](auto res, auto req) { HandleGetObjectList("workspace", res, req); });
app.get(fmt::format("{}/api/database/workspaces", _url_prefix), [&](auto res, auto req) { HandleGetObjects("workspace", res, req); });
app.get(fmt::format("{}/api/database/workspace/:name", _url_prefix), [&](auto res, auto req) { HandleGetObject("workspace", res, req); });
app.put(fmt::format("{}/api/database/workspace", _url_prefix), [&](auto res, auto req) { HandleSetObject("workspace", res, req); });
app.del(fmt::format("{}/api/database/workspace", _url_prefix), [&](auto res, auto req) { HandleClearObject("workspace", res, req); });
} else {
app.get("/api/database/*", [&](auto res, auto req) { NotImplemented(res, req); });
app.put("/api/database/*", [&](auto res, auto req) { NotImplemented(res, req); });
app.del("/api/database/*", [&](auto res, auto req) { NotImplemented(res, req); });
app.get(fmt::format("{}/api/database/*", _url_prefix), [&](auto res, auto req) { NotImplemented(res, req); });
app.put(fmt::format("{}/api/database/*", _url_prefix), [&](auto res, auto req) { NotImplemented(res, req); });
app.del(fmt::format("{}/api/database/*", _url_prefix), [&](auto res, auto req) { NotImplemented(res, req); });
}

if (_enable_frontend) {
if (_enable_runtime_config) {
app.get("/config", [&](auto res, auto req) { HandleGetConfig(res, req); });
app.get(fmt::format("{}/config", _url_prefix), [&](auto res, auto req) { HandleGetConfig(res, req); });
} else {
app.get("/config", [&](auto res, auto req) { DefaultSuccess(res, req); });
app.get(fmt::format("{}/config", _url_prefix), [&](auto res, auto req) { DefaultSuccess(res, req); });
}
// Static routes for all other files
app.get("/*", [&](Res* res, Req* req) { HandleStaticRequest(res, req); });
app.get(fmt::format("{}/*", _url_prefix), [&](Res* res, Req* req) { HandleStaticRequest(res, req); });
} else {
app.get("/*", [&](auto res, auto req) { NotImplemented(res, req); });
app.get(fmt::format("{}/*", _url_prefix), [&](auto res, auto req) { NotImplemented(res, req); });
}

// CORS support for the API
app.options("/api/*", [&](auto res, auto req) {
app.options(fmt::format("{}/api/*", _url_prefix), [&](auto res, auto req) {
AddCorsHeaders(res);
res->end();
});
Expand All @@ -118,13 +119,23 @@ void HttpServer::HandleGetConfig(Res* res, Req* req) {
void HttpServer::HandleStaticRequest(Res* res, Req* req) {
std::string_view url = req->getUrl();
fs::path path = _http_root_folder;
if (url.empty() || url == "/") {

// Trim all leading '/' and prefix
while (url.size() && url[0] == '/') {
url = url.substr(1);
}
// internal _url_prefix saved an additional '/'
if (_url_prefix.size()) {
url.remove_prefix(_url_prefix.size() - 1);
}
// Trim all '/' behind url_prefix
while (url.size() && url[0] == '/') {
url = url.substr(1);
}

if (url.empty()) {
path /= "index.html";
} else {
// Trim all leading '/'
while (url.size() && url[0] == '/') {
url = url.substr(1);
}
path /= std::string(url);
}

Expand Down
3 changes: 2 additions & 1 deletion src/HttpServer/HttpServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class HttpServer {
public:
HttpServer(std::shared_ptr<SessionManager> session_manager, fs::path root_folder, fs::path user_directory, std::string auth_token,
bool read_only_mode = false, bool enable_frontend = true, bool enable_database = true, bool enable_scripting = false,
bool enable_runtime_config = true);
bool enable_runtime_config = true, std::string url_prefix = "");
bool CanServeFrontend() {
return _frontend_found;
}
Expand Down Expand Up @@ -96,6 +96,7 @@ class HttpServer {
bool _enable_database;
bool _enable_scripting;
bool _enable_runtime_config;
std::string _url_prefix;
std::shared_ptr<SessionManager> _session_manager;
static uint32_t _scripting_request_id;
};
Expand Down
24 changes: 22 additions & 2 deletions src/Main/Main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,26 @@ int main(int argc, char* argv[]) {
carta::OnMessageTask::SetSessionManager(session_manager);

// HTTP server
auto carta_url_prefix = getenv("CARTA_URL_PREFIX");
std::string url_prefix = "";
if (carta_url_prefix && carta_url_prefix[0]) {
if (int(carta_url_prefix[0]) == 47) {
spdlog::critical("Custom url prefix is not allowed to start with '/'");
carta::logger::FlushLogFile();
return 1;
}
url_prefix = fmt::format("/{}", carta_url_prefix);
for (auto it = url_prefix.begin(); it < url_prefix.end(); it++) {
int iit = int(*it);
if ((iit < 48 || (iit < 65 && iit > 57) || (iit > 90 && iit < 97) || iit > 122) && // [0-9], [A-Z], [a-z]
(iit != 43 && iit != 45 && iit != 47 && iit != 64 && iit != 95) // +, -, /, @, _
) {
spdlog::critical("Custom prefix must be the following characters: [0-9], [A-Z], [a-z], +, -, /, @, _");
carta::logger::FlushLogFile();
return 1;
}
}
}
if (!settings.no_frontend || !settings.no_database || settings.enable_scripting) {
fs::path frontend_path;

Expand All @@ -119,7 +139,7 @@ int main(int argc, char* argv[]) {

http_server =
std::make_unique<HttpServer>(session_manager, frontend_path, settings.user_directory, auth_token, settings.read_only_mode,
!settings.no_frontend, !settings.no_database, settings.enable_scripting, !settings.no_runtime_config);
!settings.no_frontend, !settings.no_database, settings.enable_scripting, !settings.no_runtime_config, url_prefix);
http_server->RegisterRoutes();

if (!settings.no_frontend && !http_server->CanServeFrontend()) {
Expand Down Expand Up @@ -151,7 +171,7 @@ int main(int argc, char* argv[]) {
}
}

string base_url = fmt::format("http://{}:{}", default_host_string, port);
string base_url = fmt::format("http://{}:{}{}", default_host_string, port, url_prefix);

if (!settings.no_frontend && http_server->CanServeFrontend()) {
string frontend_url = base_url;
Expand Down
Loading