OpenAPI spec at /api/docs with a Scalar viewer, opt-in via FEAT_API_DOCS
This commit is contained in:
251 files changed
+32497
-66
No files matched your search
@@ -120,6 +120,17 @@ async function getOwnContainerInfo(containerId: string | undefined): Promise<any
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/system - Aggregated system, Docker, runtime and database info
|
||||
*
|
||||
* @openapi
|
||||
* summary: Return aggregated system info — Docker daemon, host, Node.js runtime, database, and object counts for an environment
|
||||
* query: env:integer ID of the environment to collect Docker info for (Docker fields are null when omitted or unreachable) (from GET /api/environments)
|
||||
* resp-200: {docker:object, host:object, runtime:object!, database:object!, stats:object!}
|
||||
* resp-200-example: {"docker":{"version":"27.0.3","apiVersion":"1.46"},"host":{"name":"docker-host","cpus":8,"memory":16777216000},"runtime":{"runtimeName":"Node.js","platform":"linux"},"database":{"type":"SQLite","schemaVersion":42},"stats":{"containers":{"total":10,"running":8,"stopped":2},"images":25,"volumes":5,"networks":4,"stacks":3}}
|
||||
* resp-403: Permission denied, or (enterprise) no access to this environment
|
||||
* resp-500: Failed to fetch system info
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ url, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
|
||||
|
||||
@@ -8,6 +8,18 @@ const SKIP_DF_COLLECTION = process.env.SKIP_DF_COLLECTION === 'true' || process.
|
||||
|
||||
const DISK_USAGE_TIMEOUT = 15000; // 15 second timeout
|
||||
|
||||
/**
|
||||
* GET /api/system/disk - Docker disk usage for an environment
|
||||
*
|
||||
* @openapi
|
||||
* summary: Return Docker disk usage (df) for an environment, or null when collection is disabled, times out, or fails
|
||||
* query: env:integer! ID of the environment to query disk usage for (from GET /api/environments)
|
||||
* resp-200: {diskUsage:object}
|
||||
* resp-200-desc: diskUsage is null when SKIP_DF_COLLECTION is set, or the query times out or errors
|
||||
* resp-200-example: {"diskUsage":{"LayersSize":1420000000,"Images":[],"Containers":[],"Volumes":[]}}
|
||||
* resp-400: Environment ID is required
|
||||
* resp-403: Permission denied, or (enterprise) no access to this environment
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ url, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
|
||||
|
||||
@@ -18,7 +18,16 @@ export interface FileEntry {
|
||||
* POST /api/system/files
|
||||
* Create a directory
|
||||
*
|
||||
* Body: { path: string }
|
||||
* @openapi
|
||||
* summary: Create a directory on Dockhand's local filesystem (absolute path, no traversal, non-protected)
|
||||
* body: {path:string!}
|
||||
* body-example: {"path":"/docker/stacks/myapp"}
|
||||
* resp-200: {success:boolean!, path:string!}
|
||||
* resp-200-example: {"success":true,"path":"/docker/stacks/myapp"}
|
||||
* resp-400: Path is missing, not absolute, or contains ".."
|
||||
* resp-403: Permission denied, or the path is protected
|
||||
* resp-409: Path already exists
|
||||
* resp-500: Failed to create directory
|
||||
*/
|
||||
export const POST: RequestHandler = async ({ request, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
@@ -65,8 +74,15 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
|
||||
* GET /api/system/files
|
||||
* Browse Dockhand's local filesystem (for mount browsing)
|
||||
*
|
||||
* Query params:
|
||||
* - path: Directory path to list
|
||||
* @openapi
|
||||
* summary: List the entries of a directory on Dockhand's local filesystem (protected paths are hidden)
|
||||
* query: path:string Absolute directory path to list (defaults to "/")
|
||||
* resp-200: {path:string!, parent:string, entries:array<{name:string!, path:string!, type:string!, size:integer!, mtime:string!, mode:string!}>!}
|
||||
* resp-200-example: {"path":"/docker","parent":"/","entries":[{"name":"stacks","path":"/docker/stacks","type":"directory","size":4096,"mtime":"2026-07-01T10:00:00.000Z","mode":"755"}]}
|
||||
* resp-400: The path exists but is not a directory
|
||||
* resp-403: Permission denied, or the path is protected
|
||||
* resp-404: Path not found
|
||||
* resp-500: Failed to list directory
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ url, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
|
||||
@@ -8,8 +8,15 @@ import { isProtectedPath } from '$lib/server/fs-guard';
|
||||
* GET /api/system/files/content
|
||||
* Read file content from Dockhand's local filesystem
|
||||
*
|
||||
* Query params:
|
||||
* - path: File path to read
|
||||
* @openapi
|
||||
* summary: Read a text file from Dockhand's local filesystem (max 10MB, protected paths denied)
|
||||
* query: path:string! Absolute path of the file to read
|
||||
* resp-200: {path:string!, content:string!, size:integer!, mtime:string!}
|
||||
* resp-200-example: {"path":"/docker/stacks/myapp/compose.yaml","content":"services:\n app:\n image: nginx","size":42,"mtime":"2026-07-01T10:00:00.000Z"}
|
||||
* resp-400: Path is missing, points to a directory, or the file exceeds 10MB
|
||||
* resp-403: Permission denied, or the path is protected
|
||||
* resp-404: File not found
|
||||
* resp-500: Failed to read file
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ url, cookies }) => {
|
||||
const auth = await authorize(cookies);
|
||||
|
||||
Reference in new issue
Block a user