docs(openapi): add file storage API specifications
Add 13 endpoints for chunked upload, file management, and folder CRUD with 6 new schemas. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -23,6 +23,18 @@
|
||||
{
|
||||
"name": "Applications",
|
||||
"description": "Application definition CRUD and job submission"
|
||||
},
|
||||
{
|
||||
"name": "File Uploads",
|
||||
"description": "Chunked file upload with SHA256 dedup, breakpoint resume, and instant upload"
|
||||
},
|
||||
{
|
||||
"name": "Files",
|
||||
"description": "File listing, metadata, download (with Range support), and deletion"
|
||||
},
|
||||
{
|
||||
"name": "Folders",
|
||||
"description": "Folder CRUD with materialized path hierarchy"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
@@ -1103,6 +1115,649 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/uploads": {
|
||||
"post": {
|
||||
"tags": ["File Uploads"],
|
||||
"operationId": "initUpload",
|
||||
"summary": "Initialize a chunked upload session",
|
||||
"description": "Creates an upload session for chunked file upload. If a file with the same SHA256 already exists, returns the existing file immediately (instant upload / dedup).",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/InitUploadRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Upload session created",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UploadSessionResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"200": {
|
||||
"description": "Dedup hit - file already exists, returns existing file",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/FileResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/uploads/{sessionId}/chunks/{chunkIndex}": {
|
||||
"put": {
|
||||
"tags": ["File Uploads"],
|
||||
"operationId": "uploadChunk",
|
||||
"summary": "Upload a single chunk",
|
||||
"description": "Uploads a file chunk for the specified session. Chunks can be uploaded in any order and are idempotent.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/SessionId"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/parameters/ChunkIndex"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"multipart/form-data": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["chunk"],
|
||||
"properties": {
|
||||
"chunk": {
|
||||
"type": "string",
|
||||
"format": "binary",
|
||||
"description": "File chunk data"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Chunk uploaded successfully"
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Upload session not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/uploads/{sessionId}/complete": {
|
||||
"post": {
|
||||
"tags": ["File Uploads"],
|
||||
"operationId": "completeUpload",
|
||||
"summary": "Complete an upload session",
|
||||
"description": "Finalizes the upload by merging all chunks via ComposeObject. Supports retry if previously failed.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/SessionId"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Upload completed, returns file",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/FileResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request or missing chunks",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Upload session not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/uploads/{sessionId}": {
|
||||
"get": {
|
||||
"tags": ["File Uploads"],
|
||||
"operationId": "getUploadStatus",
|
||||
"summary": "Get upload session status",
|
||||
"description": "Returns the current status of an upload session including list of uploaded chunk indices.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/SessionId"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Upload session status",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UploadSessionResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Upload session not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": ["File Uploads"],
|
||||
"operationId": "cancelUpload",
|
||||
"summary": "Cancel an upload session",
|
||||
"description": "Cancels an in-progress upload, deletes uploaded chunks from MinIO, and removes the session.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/SessionId"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Upload cancelled successfully"
|
||||
},
|
||||
"404": {
|
||||
"description": "Upload session not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files": {
|
||||
"get": {
|
||||
"tags": ["Files"],
|
||||
"operationId": "listFiles",
|
||||
"summary": "List files with pagination",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"schema": { "type": "integer", "default": 1 }
|
||||
},
|
||||
{
|
||||
"name": "page_size",
|
||||
"in": "query",
|
||||
"schema": { "type": "integer", "default": 20 }
|
||||
},
|
||||
{
|
||||
"name": "folder_id",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"description": "Filter by folder ID",
|
||||
"schema": { "type": "integer", "format": "int64" }
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"description": "Search files by name",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of files",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/FileListResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/{id}": {
|
||||
"get": {
|
||||
"tags": ["Files"],
|
||||
"operationId": "getFile",
|
||||
"summary": "Get file metadata",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/FileId"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "File metadata",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/FileResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "File not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": ["Files"],
|
||||
"operationId": "deleteFile",
|
||||
"summary": "Delete a file",
|
||||
"description": "Soft-deletes a file. If no other files reference the same blob, the blob is removed from MinIO.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/FileId"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "File deleted successfully"
|
||||
},
|
||||
"404": {
|
||||
"description": "File not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/{id}/download": {
|
||||
"get": {
|
||||
"tags": ["Files"],
|
||||
"operationId": "downloadFile",
|
||||
"summary": "Download a file",
|
||||
"description": "Downloads the file content. Supports HTTP Range header for partial content delivery.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/FileId"
|
||||
},
|
||||
{
|
||||
"name": "Range",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"description": "Byte range for partial content",
|
||||
"example": "bytes=0-1023",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Full file content",
|
||||
"content": {
|
||||
"application/octet-stream": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"206": {
|
||||
"description": "Partial content",
|
||||
"content": {
|
||||
"application/octet-stream": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "File not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/folders": {
|
||||
"post": {
|
||||
"tags": ["Folders"],
|
||||
"operationId": "createFolder",
|
||||
"summary": "Create a folder",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateFolderRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Folder created",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/FolderResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"get": {
|
||||
"tags": ["Folders"],
|
||||
"operationId": "listFolders",
|
||||
"summary": "List folders",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "parent_id",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"description": "Parent folder ID (null for root)",
|
||||
"schema": { "type": "integer", "format": "int64" }
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of folders",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/FolderResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/files/folders/{id}": {
|
||||
"get": {
|
||||
"tags": ["Folders"],
|
||||
"operationId": "getFolder",
|
||||
"summary": "Get folder details",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/FolderId"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Folder details",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/FolderResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Folder not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": ["Folders"],
|
||||
"operationId": "deleteFolder",
|
||||
"summary": "Delete a folder",
|
||||
"description": "Deletes an empty folder. Cannot delete folders that contain files or sub-folders.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/FolderId"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Folder deleted successfully"
|
||||
},
|
||||
"400": {
|
||||
"description": "Folder is not empty",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Folder not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
@@ -1143,6 +1798,46 @@
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
},
|
||||
"SessionId": {
|
||||
"name": "sessionId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "Upload session ID",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
},
|
||||
"ChunkIndex": {
|
||||
"name": "chunkIndex",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "Chunk index (0-based)",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"FileId": {
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "File ID",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
},
|
||||
"FolderId": {
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "Folder ID",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
@@ -1789,6 +2484,198 @@
|
||||
"description": "Items per page"
|
||||
}
|
||||
}
|
||||
},
|
||||
"InitUploadRequest": {
|
||||
"type": "object",
|
||||
"required": ["file_name", "file_size", "sha256", "chunk_size"],
|
||||
"properties": {
|
||||
"file_name": {
|
||||
"type": "string",
|
||||
"description": "File name"
|
||||
},
|
||||
"file_size": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Total file size in bytes"
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"description": "SHA256 hash of the entire file (hex encoded)"
|
||||
},
|
||||
"chunk_size": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Chunk size in bytes (must be >= min_chunk_size config)"
|
||||
},
|
||||
"folder_id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Target folder ID (null for root)",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"UploadSessionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Session ID"
|
||||
},
|
||||
"file_name": {
|
||||
"type": "string",
|
||||
"description": "File name"
|
||||
},
|
||||
"file_size": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Total file size in bytes"
|
||||
},
|
||||
"chunk_size": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Chunk size in bytes"
|
||||
},
|
||||
"total_chunks": {
|
||||
"type": "integer",
|
||||
"description": "Total number of chunks"
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"description": "SHA256 hash of the entire file"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["pending", "uploading", "merging", "completed", "failed", "cancelled", "expired"],
|
||||
"description": "Session status"
|
||||
},
|
||||
"uploaded_chunks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
},
|
||||
"description": "List of uploaded chunk indices"
|
||||
},
|
||||
"expires_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Session expiration time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FileResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "File ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "File name"
|
||||
},
|
||||
"folder_id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Parent folder ID",
|
||||
"nullable": true
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "File size in bytes"
|
||||
},
|
||||
"mime_type": {
|
||||
"type": "string",
|
||||
"description": "MIME type"
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"description": "SHA256 hash"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FileListResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"files": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/FileResponse"
|
||||
},
|
||||
"description": "List of files"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"description": "Total number of matching files"
|
||||
},
|
||||
"page": {
|
||||
"type": "integer",
|
||||
"description": "Current page number"
|
||||
},
|
||||
"page_size": {
|
||||
"type": "integer",
|
||||
"description": "Items per page"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FolderResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Folder ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Folder name"
|
||||
},
|
||||
"parent_id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Parent folder ID (null for root)",
|
||||
"nullable": true
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Materialized path (e.g. /data/reports/)"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateFolderRequest": {
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Folder name (no path traversal characters)"
|
||||
},
|
||||
"parent_id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Parent folder ID (null for root)",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user