|
@@ -2,6 +2,8 @@
|
|
|
#include "errors.hpp"
|
|
#include "errors.hpp"
|
|
|
#include "json_http.hpp"
|
|
#include "json_http.hpp"
|
|
|
#include <chrono>
|
|
#include <chrono>
|
|
|
|
|
+#include <fstream>
|
|
|
|
|
+#include <sstream>
|
|
|
|
|
|
|
|
namespace svapi {
|
|
namespace svapi {
|
|
|
|
|
|
|
@@ -36,6 +38,29 @@ void ApiServer::registerRoutes() {
|
|
|
catch (const std::exception& e) { sendJson(res, 500, errorBody("internal", e.what())); }
|
|
catch (const std::exception& e) { sendJson(res, 500, errorBody("internal", e.what())); }
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // SPA history fallback: a hard navigation / refresh / direct URL to a client-side
|
|
|
|
|
+ // route (e.g. /login, /settings) reaches the server as a 404 (no such static file).
|
|
|
|
|
+ // Serve index.html so React Router can handle it. Excludes API/meta routes and asset
|
|
|
|
|
+ // requests (paths with a file extension), which should keep their real 404.
|
|
|
|
|
+ svr_.set_error_handler([this](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
|
+ if (res.status == 404 && req.method == "GET" && !d_.webuiDir.empty()
|
|
|
|
|
+ && req.path.rfind("/api/", 0) != 0
|
|
|
|
|
+ && req.path.rfind("/ui/", 0) != 0
|
|
|
|
|
+ && req.path.rfind("/docs", 0) != 0
|
|
|
|
|
+ && req.path != "/openapi.json" && req.path != "/llms.txt"
|
|
|
|
|
+ && req.path != "/healthz" && req.path != "/readyz"
|
|
|
|
|
+ && req.path.find('.') == std::string::npos) {
|
|
|
|
|
+ std::ifstream f(d_.webuiDir + "/index.html", std::ios::binary);
|
|
|
|
|
+ if (f) {
|
|
|
|
|
+ std::stringstream ss; ss << f.rdbuf();
|
|
|
|
|
+ res.set_content(ss.str(), "text/html");
|
|
|
|
|
+ res.status = 200;
|
|
|
|
|
+ return httplib::Server::HandlerResponse::Handled;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return httplib::Server::HandlerResponse::Unhandled;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
// Authenticate /api/* (authorization is per-handler).
|
|
// Authenticate /api/* (authorization is per-handler).
|
|
|
svr_.set_pre_routing_handler([this](const httplib::Request& req, httplib::Response& res) {
|
|
svr_.set_pre_routing_handler([this](const httplib::Request& req, httplib::Response& res) {
|
|
|
if (req.method != "OPTIONS" && req.path.rfind("/api/", 0) == 0) {
|
|
if (req.method != "OPTIONS" && req.path.rfind("/api/", 0) == 0) {
|