Docs
This commit is contained in:
15
README.md
15
README.md
@@ -143,7 +143,20 @@ The local REST control API is documented as an OpenAPI/Swagger spec:
|
||||
docs/openapi.yaml
|
||||
```
|
||||
|
||||
Load that file in Swagger UI, Redoc, or another OpenAPI viewer to inspect the `/api/state`, layer control, stack preset, and reload endpoints. Live state updates are also sent over the `/ws` WebSocket.
|
||||
When the control server is running, the same spec is also served at:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:<serverPort>/docs/openapi.yaml
|
||||
http://127.0.0.1:<serverPort>/openapi.yaml
|
||||
```
|
||||
|
||||
A Swagger UI page is available at:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:<serverPort>/docs
|
||||
```
|
||||
|
||||
Use those docs to inspect the `/api/state`, layer control, stack preset, and reload endpoints. Live state updates are also sent over the `/ws` WebSocket.
|
||||
|
||||
## Shader Packages
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ std::string GuessContentType(const std::filesystem::path& assetPath)
|
||||
return "text/css";
|
||||
if (extension == ".json")
|
||||
return "application/json";
|
||||
if (extension == ".yaml" || extension == ".yml")
|
||||
return "application/yaml";
|
||||
if (extension == ".svg")
|
||||
return "image/svg+xml";
|
||||
if (extension == ".png")
|
||||
@@ -78,9 +80,10 @@ ControlServer::~ControlServer()
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool ControlServer::Start(const std::filesystem::path& uiRoot, unsigned short preferredPort, const Callbacks& callbacks, std::string& error)
|
||||
bool ControlServer::Start(const std::filesystem::path& uiRoot, const std::filesystem::path& docsRoot, unsigned short preferredPort, const Callbacks& callbacks, std::string& error)
|
||||
{
|
||||
mUiRoot = uiRoot;
|
||||
mDocsRoot = docsRoot;
|
||||
mCallbacks = callbacks;
|
||||
|
||||
if (!InitializeWinsock(error))
|
||||
@@ -246,6 +249,12 @@ ControlServer::HttpResponse ControlServer::ServeGetRequest(const HttpRequest& re
|
||||
if (request.path == "/api/state")
|
||||
return { "200 OK", "application/json", mCallbacks.getStateJson ? mCallbacks.getStateJson() : "{}" };
|
||||
|
||||
if (request.path == "/openapi.yaml" || request.path == "/docs/openapi.yaml")
|
||||
return ServeOpenApiSpec();
|
||||
|
||||
if (request.path == "/docs" || request.path == "/docs/")
|
||||
return ServeSwaggerDocs();
|
||||
|
||||
if (request.path.size() > 1)
|
||||
{
|
||||
const HttpResponse assetResponse = ServeUiAsset(request.path.substr(1));
|
||||
@@ -265,6 +274,35 @@ ControlServer::HttpResponse ControlServer::ServeUiAsset(const std::string& relat
|
||||
: HttpResponse{ "200 OK", contentType, body };
|
||||
}
|
||||
|
||||
ControlServer::HttpResponse ControlServer::ServeOpenApiSpec() const
|
||||
{
|
||||
const std::filesystem::path specPath = mDocsRoot / "openapi.yaml";
|
||||
const std::string body = LoadTextFile(specPath);
|
||||
return body.empty()
|
||||
? HttpResponse{ "404 Not Found", "text/plain", "OpenAPI spec not found" }
|
||||
: HttpResponse{ "200 OK", GuessContentType(specPath), body };
|
||||
}
|
||||
|
||||
ControlServer::HttpResponse ControlServer::ServeSwaggerDocs() const
|
||||
{
|
||||
std::ostringstream html;
|
||||
html << "<!doctype html>\n"
|
||||
<< "<html lang=\"en\">\n"
|
||||
<< "<head>\n"
|
||||
<< " <meta charset=\"utf-8\">\n"
|
||||
<< " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
|
||||
<< " <title>Video Shader Toys API Docs</title>\n"
|
||||
<< " <link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\">\n"
|
||||
<< "</head>\n"
|
||||
<< "<body>\n"
|
||||
<< " <div id=\"swagger-ui\"></div>\n"
|
||||
<< " <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>\n"
|
||||
<< " <script>SwaggerUIBundle({url:'/docs/openapi.yaml',dom_id:'#swagger-ui'});</script>\n"
|
||||
<< "</body>\n"
|
||||
<< "</html>\n";
|
||||
return { "200 OK", "text/html", html.str() };
|
||||
}
|
||||
|
||||
ControlServer::HttpResponse ControlServer::HandleApiPost(const HttpRequest& request)
|
||||
{
|
||||
JsonValue root;
|
||||
@@ -449,12 +487,16 @@ std::string ControlServer::LoadUiAsset(const std::string& relativePath, std::str
|
||||
return std::string();
|
||||
|
||||
const std::filesystem::path assetPath = mUiRoot / sanitizedPath;
|
||||
std::ifstream input(assetPath, std::ios::binary);
|
||||
contentType = GuessContentType(assetPath);
|
||||
return LoadTextFile(assetPath);
|
||||
}
|
||||
|
||||
std::string ControlServer::LoadTextFile(const std::filesystem::path& path) const
|
||||
{
|
||||
std::ifstream input(path, std::ios::binary);
|
||||
if (!input)
|
||||
return std::string();
|
||||
|
||||
contentType = GuessContentType(assetPath);
|
||||
|
||||
std::ostringstream buffer;
|
||||
buffer << input.rdbuf();
|
||||
return buffer.str();
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
ControlServer();
|
||||
~ControlServer();
|
||||
|
||||
bool Start(const std::filesystem::path& uiRoot, unsigned short preferredPort, const Callbacks& callbacks, std::string& error);
|
||||
bool Start(const std::filesystem::path& uiRoot, const std::filesystem::path& docsRoot, unsigned short preferredPort, const Callbacks& callbacks, std::string& error);
|
||||
void Stop();
|
||||
void BroadcastState();
|
||||
|
||||
@@ -76,11 +76,14 @@ private:
|
||||
HttpResponse RouteHttpRequest(const HttpRequest& request);
|
||||
HttpResponse ServeGetRequest(const HttpRequest& request) const;
|
||||
HttpResponse ServeUiAsset(const std::string& relativePath) const;
|
||||
HttpResponse ServeOpenApiSpec() const;
|
||||
HttpResponse ServeSwaggerDocs() const;
|
||||
HttpResponse HandleApiPost(const HttpRequest& request);
|
||||
bool InvokePostRoute(const std::string& path, const JsonValue& root, std::string& actionError);
|
||||
bool SendWebSocketText(SOCKET clientSocket, const std::string& payload);
|
||||
void BroadcastStateLocked();
|
||||
std::string LoadUiAsset(const std::string& relativePath, std::string& contentType) const;
|
||||
std::string LoadTextFile(const std::filesystem::path& path) const;
|
||||
std::string BuildJsonResponse(bool success, const std::string& error = std::string()) const;
|
||||
static std::string Base64Encode(const unsigned char* data, DWORD dataLength);
|
||||
static std::string ComputeWebSocketAcceptKey(const std::string& clientKey);
|
||||
@@ -89,6 +92,7 @@ private:
|
||||
|
||||
private:
|
||||
std::filesystem::path mUiRoot;
|
||||
std::filesystem::path mDocsRoot;
|
||||
Callbacks mCallbacks;
|
||||
UniqueSocket mListenSocket;
|
||||
unsigned short mPort;
|
||||
|
||||
@@ -836,7 +836,7 @@ bool OpenGLComposite::InitOpenGLState()
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!mControlServer->Start(mRuntimeHost->GetUiRoot(), mRuntimeHost->GetServerPort(), callbacks, runtimeError))
|
||||
if (!mControlServer->Start(mRuntimeHost->GetUiRoot(), mRuntimeHost->GetDocsRoot(), mRuntimeHost->GetServerPort(), callbacks, runtimeError))
|
||||
{
|
||||
MessageBoxA(NULL, runtimeError.c_str(), "Local control server failed to start", MB_OK);
|
||||
return false;
|
||||
|
||||
@@ -1388,6 +1388,7 @@ bool RuntimeHost::ResolvePaths(std::string& error)
|
||||
|
||||
const std::filesystem::path builtUiRoot = mRepoRoot / "ui" / "dist";
|
||||
mUiRoot = std::filesystem::exists(builtUiRoot) ? builtUiRoot : (mRepoRoot / "ui");
|
||||
mDocsRoot = mRepoRoot / "docs";
|
||||
mConfigPath = mRepoRoot / "config" / "runtime-host.json";
|
||||
mShaderRoot = mRepoRoot / mConfig.shaderLibrary;
|
||||
mRuntimeRoot = mRepoRoot / "runtime";
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
|
||||
const std::filesystem::path& GetRepoRoot() const { return mRepoRoot; }
|
||||
const std::filesystem::path& GetUiRoot() const { return mUiRoot; }
|
||||
const std::filesystem::path& GetDocsRoot() const { return mDocsRoot; }
|
||||
const std::filesystem::path& GetRuntimeRoot() const { return mRuntimeRoot; }
|
||||
unsigned short GetServerPort() const { return mServerPort; }
|
||||
unsigned GetMaxTemporalHistoryFrames() const { return mConfig.maxTemporalHistoryFrames; }
|
||||
@@ -118,6 +119,7 @@ private:
|
||||
PersistentState mPersistentState;
|
||||
std::filesystem::path mRepoRoot;
|
||||
std::filesystem::path mUiRoot;
|
||||
std::filesystem::path mDocsRoot;
|
||||
std::filesystem::path mShaderRoot;
|
||||
std::filesystem::path mRuntimeRoot;
|
||||
std::filesystem::path mPresetRoot;
|
||||
|
||||
@@ -6,6 +6,11 @@ Tracked files:
|
||||
|
||||
- `templates/`: source templates used to generate shader runtime code.
|
||||
|
||||
Packaged documentation:
|
||||
|
||||
- `../docs/openapi.yaml`: OpenAPI/Swagger spec for the local control API.
|
||||
- `http://127.0.0.1:<serverPort>/docs`: Swagger UI page served by the native control server.
|
||||
|
||||
Generated files:
|
||||
|
||||
- `shader_cache/active_shader_wrapper.slang`: generated Slang wrapper for the active shader/layer.
|
||||
|
||||
Reference in New Issue
Block a user