{"openapi":"3.1.0","info":{"title":"OpenAPI definition","version":"v0"},"servers":[{"url":"/"}],"paths":{"/api/media/internal/files":{"post":{"tags":["Internal Files"],"summary":"Upload file (service-to-service)","description":"Multipart upload used by other nx-* services over the internal network. Stores the\nfile under `{tenantSlug}/public/<directory>` and returns its id + relative public URL.\nNaming is always UUID. Access is gated by the nginx ACL on `/internal/**` — no JWT,\nno `Tenant-Id` header; `tenantId` is a body part.\n","operationId":"upload","parameters":[{"name":"tenantId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"directory","in":"query","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Missing file/tenantId or invalid directory","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Tenant root or public directory not provisioned","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"201":{"description":"File uploaded","content":{"*/*":{"schema":{"$ref":"#/components/schemas/InternalFileResponse"}}}}}}},"/api/media/internal/files/sync":{"post":{"tags":["Internal Files"],"summary":"Sync untracked files from filesystem","description":"Scans the given directory on disk and creates DB records for any files that are\npresent on the filesystem but missing from the `files` table. Used after bulk-copy\ndeploys of predefined assets (icons, email images). Content type is detected by Tika.\n`tenantId` is part of the JSON body; null/absent = common scope.\n","operationId":"sync","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncFilesRequest"}}},"required":true},"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"200":{"description":"Sync summary","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SyncResultResponse"}}}}}}},"/api/media/files":{"get":{"tags":["Files"],"summary":"List files in a directory (root if directoryId is omitted)","operationId":"list","parameters":[{"name":"directory-id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Zero-based page index (0..N)","required":false,"schema":{"type":"integer","default":0,"minimum":0}},{"name":"size","in":"query","description":"The size of the page to be returned","required":false,"schema":{"type":"integer","default":20,"minimum":1}},{"name":"sort","in":"query","description":"Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.","required":false,"schema":{"type":"array","default":["name,ASC"],"items":{"type":"string"}}}],"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"200":{"description":"List of files","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelFileResponse"}}}}}},"post":{"tags":["Files"],"summary":"Upload files","operationId":"upload_1","requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string","format":"binary"}},"upload":{"$ref":"#/components/schemas/FilesUploadRequest"}},"required":["files"]},"encoding":{"upload":{"contentType":"application/json"}}}}},"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Empty file list or limit exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"201":{"description":"All files uploaded successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UploadResultResponse"}}}},"207":{"description":"Some files failed to upload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UploadResultResponse"}}}}}}},"/api/media/directories":{"get":{"tags":["Directories"],"summary":"List directories","operationId":"list_1","parameters":[{"name":"parent-id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Zero-based page index (0..N)","required":false,"schema":{"type":"integer","default":0,"minimum":0}},{"name":"size","in":"query","description":"The size of the page to be returned","required":false,"schema":{"type":"integer","default":20,"minimum":1}},{"name":"sort","in":"query","description":"Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.","required":false,"schema":{"type":"array","default":["name,ASC"],"items":{"type":"string"}}}],"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"200":{"description":"Page of directories","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelDirectoryDetailResponse"}}}}}},"post":{"tags":["Directories"],"summary":"Create a directory","operationId":"create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDirectoryRequest"}}},"required":true},"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Access denied","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Parent directory not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"201":{"description":"Directory created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/DirectoryResponse"}}}}}}},"/api/media/files/{id}/name":{"patch":{"tags":["Files"],"summary":"Rename a file","operationId":"rename","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameFileRequest"}}},"required":true},"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"File not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"200":{"description":"File renamed","content":{"*/*":{"schema":{"$ref":"#/components/schemas/FileResponse"}}}}}}},"/api/media/directories/{id}/name":{"patch":{"tags":["Directories"],"summary":"Rename a directory","operationId":"rename_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameDirectoryRequest"}}},"required":true},"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Common directory cannot be modified","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Directory not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"200":{"description":"Directory renamed","content":{"*/*":{"schema":{"$ref":"#/components/schemas/DirectoryResponse"}}}}}}},"/api/media/internal/files/{id}":{"get":{"tags":["Internal Files"],"summary":"Download file (service-to-service)","description":"Streams the file bytes. `tenantId` is a query param; 404 when the file belongs to a\ndifferent tenant. No range requests — internal clients fetch the full body.\n","operationId":"download","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"tenantId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Missing tenantId","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"File not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"200":{"description":"File stream","content":{"*/*":{"schema":{"type":"string","format":"binary"}}}}}},"delete":{"tags":["Internal Files"],"summary":"Delete file (service-to-service)","description":"Used by callers to roll back an upload after a failed write in their own domain.\n`tenantId` is a query param; cross-tenant requests return 404.\n","operationId":"delete","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"tenantId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Missing tenantId","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"File not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"204":{"description":"File deleted"}}}},"/api/media/directories/{id}":{"get":{"tags":["Directories"],"summary":"Get a directory by id","operationId":"get","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Directory not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"200":{"description":"Directory found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/DirectoryDetailResponse"}}}}}},"delete":{"tags":["Directories"],"summary":"Delete a directory","operationId":"delete_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Common directory cannot be modified","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Directory not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"204":{"description":"Directory deleted"}}}},"/api/media/directories/tree":{"get":{"tags":["Directories"],"summary":"Get directory tree","operationId":"tree","responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"200":{"description":"Directory tree","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DirectoryTreeResponse"}}}}}}}},"/api/media/files/{id}":{"delete":{"tags":["Files"],"summary":"Delete a file","operationId":"delete_2","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"404":{"description":"File not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"500":{"description":"Internal Server Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"204":{"description":"File deleted"}}}}},"components":{"schemas":{"ProblemDetail":{"type":"object","properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer","format":"int32"},"detail":{"type":"string"},"instance":{"type":"string","format":"uri"},"properties":{"type":"object","additionalProperties":{}}}},"InternalFileResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"relativePath":{"type":"string"},"contentType":{"type":"string"},"sizeBytes":{"type":"integer","format":"int64"}}},"SyncFilesRequest":{"type":"object","properties":{"tenantId":{"type":"string","format":"uuid"},"directoryId":{"type":"string","format":"uuid"}},"required":["directoryId"]},"FailedFileResponse":{"type":"object","properties":{"originalName":{"type":"string"},"error":{"type":"string"}}},"FileResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"relativePath":{"type":"string"},"contentType":{"type":"string"},"sizeBytes":{"type":"integer","format":"int64"},"directoryId":{"type":"string","format":"uuid"},"common":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"SyncResultResponse":{"type":"object","properties":{"created":{"type":"array","items":{"$ref":"#/components/schemas/FileResponse"}},"skipped":{"type":"array","items":{"type":"string"}},"failed":{"type":"array","items":{"$ref":"#/components/schemas/FailedFileResponse"}}}},"FilesUploadRequest":{"type":"object","properties":{"directoryId":{"type":"string","format":"uuid"},"naming":{"type":"string","enum":["UUID","ORIGINAL"]}},"required":["directoryId","naming"]},"UploadResultResponse":{"type":"object","properties":{"uploaded":{"type":"array","items":{"$ref":"#/components/schemas/FileResponse"}},"failed":{"type":"array","items":{"$ref":"#/components/schemas/FailedFileResponse"}}}},"CreateDirectoryRequest":{"type":"object","properties":{"parentId":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1}},"required":["name"]},"DirectoryResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tenantId":{"type":"string","format":"uuid"},"parentId":{"type":"string","format":"uuid"},"name":{"type":"string"},"common":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"RenameFileRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1}},"required":["name"]},"RenameDirectoryRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1}},"required":["name"]},"PageMetadata":{"type":"object","properties":{"size":{"type":"integer","format":"int64"},"number":{"type":"integer","format":"int64"},"totalElements":{"type":"integer","format":"int64"},"totalPages":{"type":"integer","format":"int64"}}},"PagedModelFileResponse":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/FileResponse"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"DirectoryDetailResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tenantId":{"type":"string","format":"uuid"},"parentId":{"type":"string","format":"uuid"},"name":{"type":"string"},"common":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"fileCount":{"type":"integer","format":"int64"},"totalSize":{"type":"integer","format":"int64"}}},"PagedModelDirectoryDetailResponse":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/DirectoryDetailResponse"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"DirectoryTreeResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tenantId":{"type":"string","format":"uuid"},"parentId":{"type":"string","format":"uuid"},"name":{"type":"string"},"common":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"fileCount":{"type":"integer","format":"int64"},"totalSize":{"type":"integer","format":"int64"},"children":{"type":"array","items":{}}}}}}}