chore: initialize from backend template 0a6dd0e
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
# Fileserver HTTP contract
|
||||
|
||||
Every public endpoint is listed here. `FileserverDocumentationCoverageTest` scans the controllers
|
||||
and fails if one is missing, so this file cannot silently fall behind the code.
|
||||
|
||||
## Public endpoints
|
||||
|
||||
| Method | Path | Success | Notes |
|
||||
|---|---|---|---|
|
||||
| POST | `/v1/files` | `201` READY, `202` VERIFYING | multipart single upload |
|
||||
| POST | `/v1/files:raw` | `201`, `202` | the whole request body is the file |
|
||||
| POST | `/v1/files:batch` | `200` | ordered per-part results; explicitly non-atomic |
|
||||
| GET | `/v1/files/{fileId}` | `200` | public metadata; never a content key or path |
|
||||
| GET | `/v1/files/{fileId}/content` | `200`, `206`, `304` | download |
|
||||
| HEAD | `/v1/files/{fileId}/content` | `200`, `304` | identical headers, no body |
|
||||
| DELETE | `/v1/files/{fileId}` | `202`, `204` | logical delete first |
|
||||
| POST | `/v1/files/{fileId}:copy` | `202` | create-only target |
|
||||
| POST | `/v1/files/{fileId}:move` | `200` | logical namespace change only |
|
||||
| OPTIONS | `/v1/uploads` | `204` | tus capability discovery |
|
||||
| POST | `/v1/uploads` | `201` | tus creation |
|
||||
| HEAD | `/v1/uploads/{uploadId}` | `204` | tus offset |
|
||||
| PATCH | `/v1/uploads/{uploadId}` | `204` | tus append |
|
||||
| DELETE | `/v1/uploads/{uploadId}` | `204` | tus termination |
|
||||
| POST | `/v1/experimental/draft12/uploads` | `201` | Experimental; off by default |
|
||||
| PATCH | `/v1/experimental/draft12/uploads/{uploadId}` | `204` | Experimental; off by default |
|
||||
|
||||
## Management endpoints
|
||||
|
||||
Reachable only where both `app.fileserver-platform.enabled=true` and
|
||||
`app.fileserver-platform.admin.enabled=true`, gated at the servlet chain on
|
||||
`app.fileserver-platform.security.admin-roles`, and intended for a management
|
||||
port rather than the public one.
|
||||
|
||||
| Method | Path |
|
||||
|---|---|
|
||||
| GET | `/internal/fileserver/storage-health` |
|
||||
| GET | `/internal/fileserver/capabilities` |
|
||||
| GET | `/internal/fileserver/orphans` |
|
||||
| POST | `/internal/fileserver/orphans:reconcile` |
|
||||
| POST | `/internal/fileserver/files/{fileId}:reverify` |
|
||||
| POST | `/internal/fileserver/files/{fileId}:force-delete` |
|
||||
| GET | `/internal/fileserver/uploads/incomplete` |
|
||||
| POST | `/internal/fileserver/uploads:cleanup` |
|
||||
|
||||
## Status codes
|
||||
|
||||
| Status | Condition |
|
||||
|---:|---|
|
||||
| `200` | metadata, full GET, batch result, move |
|
||||
| `201` | file or upload created |
|
||||
| `202` | verification or physical cleanup deferred |
|
||||
| `204` | append, cancel, bodyless update |
|
||||
| `206` | satisfiable Range |
|
||||
| `304` | validator matched on GET or HEAD |
|
||||
| `400` | malformed header or header combination |
|
||||
| `401` | unauthenticated |
|
||||
| `403` / `404` | denied, or hidden under the existence-hiding profile |
|
||||
| `409` | state, offset, or lease conflict |
|
||||
| `410` | expired upload resource |
|
||||
| `411` | `require-content-length` profile with no length |
|
||||
| `412` | precondition failed |
|
||||
| `413` | size or quota policy violation |
|
||||
| `415` | upload media type not accepted |
|
||||
| `416` | unsatisfiable Range; carries the real length |
|
||||
| `422` | digest, signature, or scanner rejection |
|
||||
| `429` | transfer admission or rate limit |
|
||||
| `503` | storage or scanner unavailable |
|
||||
| `504` | downstream timeout |
|
||||
| `507` | out of storage capacity |
|
||||
|
||||
## Failure body
|
||||
|
||||
Every failure answers `application/problem+json` with a stable code and its URN:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "urn:fileserver:problem:upload-offset-mismatch",
|
||||
"title": "Upload offset mismatch",
|
||||
"status": 409,
|
||||
"code": "UPLOAD_OFFSET_MISMATCH",
|
||||
"retryable": true,
|
||||
"ambiguous": false,
|
||||
"reconciliationRequired": false,
|
||||
"traceId": "..."
|
||||
}
|
||||
```
|
||||
|
||||
The server-side exception message never appears. `ambiguous` is the field a client must read before
|
||||
retrying: an ambiguous failure may already have taken effect.
|
||||
|
||||
## Header contract
|
||||
|
||||
| Header | Contract |
|
||||
|---|---|
|
||||
| `Content-Type` | client value is a claim; the verified type is stored separately |
|
||||
| `Content-Disposition` | `attachment` by default; scriptable types are never inline |
|
||||
| `Accept-Ranges` | `bytes` |
|
||||
| `Range` | single range by default; multi-range only under an explicit budget |
|
||||
| `Content-Range` | actual range on `206`; the unsatisfied form on `416` |
|
||||
| `ETag` | strong validator derived from the SHA-256 |
|
||||
| `Last-Modified` | metadata publication instant, never a filesystem timestamp |
|
||||
| `Cache-Control` | `private, no-store` by default |
|
||||
| `X-Content-Type-Options` | always `nosniff` on a download |
|
||||
| `Retry-After` | on retryable `409`, `429`, `503`, and `504` |
|
||||
| `X-Accel-Redirect` | internal only; never forwarded to a client |
|
||||
Reference in New Issue
Block a user