docs: auto-generate API route documentation

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Id0d1f9769b7ccdbf83d5fa78adef62e46a6a6964
This commit is contained in:
raf 2026-03-21 02:18:48 +03:00
commit 7a0a009ced
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
40 changed files with 17444 additions and 1 deletions

BIN
Cargo.lock generated

Binary file not shown.

View file

@ -1,5 +1,5 @@
[workspace] [workspace]
members = ["crates/*"] members = ["crates/*", "xtask"]
exclude = ["crates/pinakes-core/tests/fixtures/test-plugin"] exclude = ["crates/pinakes-core/tests/fixtures/test-plugin"]
resolver = "3" resolver = "3"
@ -92,6 +92,9 @@ http = "1.4.0"
wasmtime = { version = "42.0.1", features = ["component-model"] } wasmtime = { version = "42.0.1", features = ["component-model"] }
wit-bindgen = "0.53.1" wit-bindgen = "0.53.1"
tempfile = "3.26.0" tempfile = "3.26.0"
utoipa = { version = "5.4.0", features = ["axum_extras", "uuid", "chrono"] }
utoipa-axum = { version = "0.2.0" }
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
# See: # See:
# <https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html> # <https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html>

117
docs/api/analytics.md Normal file
View file

@ -0,0 +1,117 @@
# Analytics
Usage analytics and viewing history
## Endpoints
### POST /api/v1/analytics/events
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Event recorded |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/analytics/most-viewed
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `limit` | query | No | Maximum number of results |
| `offset` | query | No | Pagination offset |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Most viewed media |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/analytics/recently-viewed
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `limit` | query | No | Maximum number of results |
| `offset` | query | No | Pagination offset |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Recently viewed media |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/media/{id}/progress
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Watch progress |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### PUT /api/v1/media/{id}/progress
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Progress updated |
| 400 | Bad request |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---

27
docs/api/audit.md Normal file
View file

@ -0,0 +1,27 @@
# Audit
Audit log entries
## Endpoints
### GET /api/v1/audit
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Page size |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Audit log entries |
| 401 | Unauthorized |
| 500 | Internal server error |
---

103
docs/api/auth.md Normal file
View file

@ -0,0 +1,103 @@
# Auth
Authentication and session management
## Endpoints
### POST /api/v1/auth/login
**Authentication:** Not required
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Login successful |
| 400 | Bad request |
| 401 | Invalid credentials |
| 500 | Internal server error |
---
### POST /api/v1/auth/logout
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Logged out |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/auth/me
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Current user info |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/auth/refresh
Refresh the current session, extending its expiry by the configured
duration.
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Session refreshed |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/auth/revoke-all
Revoke all sessions for the current user
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | All sessions revoked |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/auth/sessions
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Active sessions |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---

27
docs/api/backup.md Normal file
View file

@ -0,0 +1,27 @@
# Backup
Database backup
## Endpoints
### POST /api/v1/admin/backup
Create a database backup and return it as a downloadable file.
POST /api/v1/admin/backup
For `SQLite`: creates a backup via VACUUM INTO and returns the file.
For `PostgreSQL`: returns unsupported error (use `pg_dump` instead).
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Backup file download |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---

208
docs/api/books.md Normal file
View file

@ -0,0 +1,208 @@
# Books
Book metadata, series, authors, and reading progress
## Endpoints
### GET /api/v1/books
List all books with optional search filters
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `isbn` | query | No | Filter by ISBN |
| `author` | query | No | Filter by author |
| `series` | query | No | Filter by series |
| `publisher` | query | No | Filter by publisher |
| `language` | query | No | Filter by language |
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Pagination limit |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of books |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/books/authors
List all authors with book counts
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Pagination limit |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Authors with book counts |
| 401 | Unauthorized |
---
### GET /api/v1/books/authors/{name}/books
Get books by a specific author
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `name` | path | Yes | Author name |
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Pagination limit |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Books by author |
| 401 | Unauthorized |
---
### GET /api/v1/books/reading-list
Get user's reading list
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `status` | query | No | Filter by reading status |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Reading list |
| 401 | Unauthorized |
---
### GET /api/v1/books/series
List all series with book counts
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of series with counts |
| 401 | Unauthorized |
---
### GET /api/v1/books/series/{name}
Get books in a specific series
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `name` | path | Yes | Series name |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Books in series |
| 401 | Unauthorized |
---
### GET /api/v1/books/{id}/metadata
Get book metadata by media ID
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Book metadata |
| 401 | Unauthorized |
| 404 | Not found |
---
### GET /api/v1/books/{id}/progress
Get reading progress for a book
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Reading progress |
| 401 | Unauthorized |
| 404 | Not found |
---
### PUT /api/v1/books/{id}/progress
Update reading progress for a book
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 204 | Progress updated |
| 400 | Bad request |
| 401 | Unauthorized |
---

157
docs/api/collections.md Normal file
View file

@ -0,0 +1,157 @@
# Collections
Media collections
## Endpoints
### GET /api/v1/collections
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of collections |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/collections
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Collection created |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### GET /api/v1/collections/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Collection ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Collection |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### DELETE /api/v1/collections/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Collection ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Collection deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### GET /api/v1/collections/{id}/members
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Collection ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Collection members |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### POST /api/v1/collections/{id}/members
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Collection ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Member added |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### DELETE /api/v1/collections/{id}/members/{media_id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Collection ID |
| `media_id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Member removed |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---

120
docs/api/config.md Normal file
View file

@ -0,0 +1,120 @@
# Config
Server configuration
## Endpoints
### GET /api/v1/config
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Current server configuration |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/config/roots
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Updated configuration |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### DELETE /api/v1/config/roots
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Updated configuration |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### PATCH /api/v1/config/scanning
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Updated configuration |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### GET /api/v1/config/ui
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | UI configuration |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### PATCH /api/v1/config/ui
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Updated UI configuration |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---

51
docs/api/database.md Normal file
View file

@ -0,0 +1,51 @@
# Database
Database administration
## Endpoints
### POST /api/v1/admin/database/clear
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Database cleared |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### GET /api/v1/admin/database/stats
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Database statistics |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/admin/database/vacuum
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Database vacuumed |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---

20
docs/api/duplicates.md Normal file
View file

@ -0,0 +1,20 @@
# Duplicates
Duplicate media detection
## Endpoints
### GET /api/v1/media/duplicates
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Duplicate groups |
| 401 | Unauthorized |
| 500 | Internal server error |
---

71
docs/api/enrichment.md Normal file
View file

@ -0,0 +1,71 @@
# Enrichment
External metadata enrichment
## Endpoints
### POST /api/v1/media/enrich/batch
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Enrichment job submitted |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/{id}/enrich
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Enrichment job submitted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### GET /api/v1/media/{id}/metadata/external
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | External metadata |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---

42
docs/api/export.md Normal file
View file

@ -0,0 +1,42 @@
# Export
Media library export
## Endpoints
### POST /api/v1/export
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Export job submitted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/export/options
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Export job submitted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---

63
docs/api/health.md Normal file
View file

@ -0,0 +1,63 @@
# Health
Server health checks
## Endpoints
### GET /api/v1/health
Comprehensive health check - includes database, filesystem, and cache status
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Health status |
---
### GET /api/v1/health/detailed
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Detailed health status |
---
### GET /api/v1/health/live
Liveness probe - just checks if the server is running
Returns 200 OK if the server process is alive
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Server is alive |
---
### GET /api/v1/health/ready
Readiness probe - checks if the server can serve requests
Returns 200 OK if database is accessible
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Server is ready |
| 503 | Server not ready |
---

99
docs/api/integrity.md Normal file
View file

@ -0,0 +1,99 @@
# Integrity
Library integrity checks and repairs
## Endpoints
### POST /api/v1/admin/integrity/orphans/detect
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Orphan detection job submitted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/admin/integrity/orphans/resolve
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Orphans resolved |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/admin/integrity/thumbnails/cleanup
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Thumbnail cleanup job submitted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/admin/integrity/thumbnails/generate
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Thumbnail generation job submitted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/admin/integrity/verify
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Integrity verification job submitted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---

62
docs/api/jobs.md Normal file
View file

@ -0,0 +1,62 @@
# Jobs
Background job management
## Endpoints
### GET /api/v1/jobs
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of jobs |
| 401 | Unauthorized |
| 403 | Forbidden |
---
### GET /api/v1/jobs/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Job ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Job details |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### POST /api/v1/jobs/{id}/cancel
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Job ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Job cancelled |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---

640
docs/api/media.md Normal file
View file

@ -0,0 +1,640 @@
# Media
Media item management
## Endpoints
### GET /api/v1/media
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Page size |
| `sort` | query | No | Sort field |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of media items |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### DELETE /api/v1/media
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | All media deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/batch/collection
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Batch collection result |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/batch/delete
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Batch delete result |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/batch/move
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Batch move result |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/batch/tag
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Batch tag result |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/batch/update
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Batch update result |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### GET /api/v1/media/count
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media count |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/media/import
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media imported |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/import/batch
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Batch import results |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/import/directory
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Directory import results |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/import/options
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media imported |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### POST /api/v1/media/import/preview
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Directory preview |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### GET /api/v1/media/trash
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Page size |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Trashed media items |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### DELETE /api/v1/media/trash
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Trash emptied |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### GET /api/v1/media/trash/info
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Trash info |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/media/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media item |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### PATCH /api/v1/media/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Updated media item |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### DELETE /api/v1/media/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### PUT /api/v1/media/{id}/custom-fields
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Custom field set |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### DELETE /api/v1/media/{id}/custom-fields/{name}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
| `name` | path | Yes | Custom field name |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Custom field deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### POST /api/v1/media/{id}/move
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Moved media item |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### POST /api/v1/media/{id}/open
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media opened |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### DELETE /api/v1/media/{id}/permanent
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
| `permanent` | query | No | Set to 'true' for permanent deletion |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### POST /api/v1/media/{id}/rename
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Renamed media item |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### POST /api/v1/media/{id}/restore
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media restored |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### GET /api/v1/media/{id}/stream
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media stream |
| 206 | Partial content |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### GET /api/v1/media/{id}/thumbnail
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Thumbnail image |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### DELETE /api/v1/media/{id}/trash
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media moved to trash |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---

142
docs/api/notes.md Normal file
View file

@ -0,0 +1,142 @@
# Notes
Markdown notes link graph
## Endpoints
### GET /api/v1/media/{id}/backlinks
Get backlinks (incoming links) to a media item.
GET /api/v1/media/{id}/backlinks
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Backlinks |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### GET /api/v1/media/{id}/outgoing-links
Get outgoing links from a media item.
GET /api/v1/media/{id}/outgoing-links
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Outgoing links |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### POST /api/v1/media/{id}/reindex-links
Re-extract links from a media item.
POST /api/v1/media/{id}/reindex-links
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Links reindexed |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### GET /api/v1/notes/graph
Get graph data for visualization.
GET /api/v1/notes/graph?center={uuid}&depth={n}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `center` | query | No | Center node ID |
| `depth` | query | No | Traversal depth (max 5, default 2) |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Graph data |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/notes/resolve-links
Resolve all unresolved links in the database.
POST /api/v1/notes/resolve-links
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Links resolved |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/notes/unresolved-count
Get count of unresolved links.
GET /api/v1/notes/unresolved-count
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Unresolved link count |
| 401 | Unauthorized |
| 500 | Internal server error |
---

12810
docs/api/openapi.json Normal file

File diff suppressed because it is too large Load diff

57
docs/api/photos.md Normal file
View file

@ -0,0 +1,57 @@
# Photos
Photo timeline and map view
## Endpoints
### GET /api/v1/photos/map
Get photos in a bounding box for map view
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `lat1` | query | Yes | Bounding box latitude 1 |
| `lon1` | query | Yes | Bounding box longitude 1 |
| `lat2` | query | Yes | Bounding box latitude 2 |
| `lon2` | query | Yes | Bounding box longitude 2 |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Map markers |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/photos/timeline
Get timeline of photos grouped by date
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `group_by` | query | No | Grouping: day, month, year |
| `year` | query | No | Filter by year |
| `month` | query | No | Filter by month |
| `limit` | query | No | Max items (default 10000) |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Photo timeline groups |
| 401 | Unauthorized |
| 500 | Internal server error |
---

229
docs/api/playlists.md Normal file
View file

@ -0,0 +1,229 @@
# Playlists
Media playlists
## Endpoints
### GET /api/v1/playlists
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of playlists |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/playlists
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Playlist created |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/playlists/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Playlist ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Playlist details |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### PATCH /api/v1/playlists/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Playlist ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Playlist updated |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### DELETE /api/v1/playlists/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Playlist ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Playlist deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### GET /api/v1/playlists/{id}/items
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Playlist ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Playlist items |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### POST /api/v1/playlists/{id}/items
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Playlist ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Item added |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### PATCH /api/v1/playlists/{id}/items/reorder
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Playlist ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Item reordered |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### DELETE /api/v1/playlists/{id}/items/{media_id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Playlist ID |
| `media_id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Item removed |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### POST /api/v1/playlists/{id}/shuffle
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Playlist ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Shuffled playlist items |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---

209
docs/api/plugins.md Normal file
View file

@ -0,0 +1,209 @@
# Plugins
Plugin management
## Endpoints
### GET /api/v1/plugins
List all installed plugins
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of plugins |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/plugins
Install a plugin from URL or file path
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Plugin installed |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
---
### POST /api/v1/plugins/events
Receive a plugin event emitted from the UI and dispatch it to interested
server-side event-handler plugins via the pipeline.
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Event received |
| 401 | Unauthorized |
---
### GET /api/v1/plugins/ui/pages
List all UI pages provided by loaded plugins
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Plugin UI pages |
| 401 | Unauthorized |
---
### GET /api/v1/plugins/ui/theme
List merged CSS custom property overrides from all enabled plugins
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Plugin UI theme extensions |
| 401 | Unauthorized |
---
### GET /api/v1/plugins/ui/widgets
List all UI widgets provided by loaded plugins
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Plugin UI widgets |
| 401 | Unauthorized |
---
### GET /api/v1/plugins/{id}
Get a specific plugin by ID
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Plugin ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Plugin details |
| 401 | Unauthorized |
| 404 | Not found |
---
### DELETE /api/v1/plugins/{id}
Uninstall a plugin
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Plugin ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Plugin uninstalled |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### POST /api/v1/plugins/{id}/reload
Reload a plugin (for development)
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Plugin ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Plugin reloaded |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### PATCH /api/v1/plugins/{id}/toggle
Enable or disable a plugin
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Plugin ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Plugin toggled |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---

View file

@ -0,0 +1,62 @@
# Saved_searches
Saved search queries
## Endpoints
### GET /api/v1/searches
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of saved searches |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/searches
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Search saved |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### DELETE /api/v1/searches/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Saved search ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Saved search deleted |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---

42
docs/api/scan.md Normal file
View file

@ -0,0 +1,42 @@
# Scan
Directory scanning
## Endpoints
### POST /api/v1/scan
Trigger a scan as a background job. Returns the job ID immediately.
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Scan job submitted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### GET /api/v1/scan/status
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Scan status |
| 401 | Unauthorized |
---

View file

@ -0,0 +1,62 @@
# Scheduled_tasks
Scheduled background tasks
## Endpoints
### GET /api/v1/scheduled-tasks
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of scheduled tasks |
| 401 | Unauthorized |
| 403 | Forbidden |
---
### POST /api/v1/scheduled-tasks/{id}/run
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Task ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Task triggered |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### POST /api/v1/scheduled-tasks/{id}/toggle
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Task ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Task toggled |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---

51
docs/api/search.md Normal file
View file

@ -0,0 +1,51 @@
# Search
Full-text media search
## Endpoints
### GET /api/v1/search
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `q` | query | Yes | Search query |
| `sort` | query | No | Sort order |
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Pagination limit |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Search results |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/search
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Search results |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---

282
docs/api/shares.md Normal file
View file

@ -0,0 +1,282 @@
# Shares
Media sharing and notifications
## Endpoints
### GET /api/v1/notifications/shares
Get unread share notifications
GET /api/notifications/shares
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Unread notifications |
| 401 | Unauthorized |
---
### POST /api/v1/notifications/shares/read-all
Mark all notifications as read
POST /api/notifications/shares/read-all
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | All notifications marked as read |
| 401 | Unauthorized |
---
### POST /api/v1/notifications/shares/{id}/read
Mark a notification as read
POST /api/notifications/shares/{id}/read
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Notification ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Notification marked as read |
| 401 | Unauthorized |
---
### GET /api/v1/shared/{token}
Access a public shared resource
GET /api/shared/{token}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `token` | path | Yes | Share token |
| `password` | query | No | Share password if required |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Shared content |
| 401 | Unauthorized |
| 404 | Not found |
---
### POST /api/v1/shares
Create a new share
POST /api/shares
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Share created |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/shares/batch/delete
Batch delete shares
POST /api/shares/batch/delete
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Shares deleted |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
---
### GET /api/v1/shares/incoming
List incoming shares (shares shared with me)
GET /api/shares/incoming
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Pagination limit |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Incoming shares |
| 401 | Unauthorized |
---
### GET /api/v1/shares/outgoing
List outgoing shares (shares I created)
GET /api/shares/outgoing
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Pagination limit |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Outgoing shares |
| 401 | Unauthorized |
---
### GET /api/v1/shares/{id}
Get share details
GET /api/shares/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Share ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Share details |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### PATCH /api/v1/shares/{id}
Update a share
PATCH /api/shares/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Share ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Share updated |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### DELETE /api/v1/shares/{id}
Delete (revoke) a share
DELETE /api/shares/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Share ID |
#### Responses
| Status | Description |
|--------|-------------|
| 204 | Share deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### GET /api/v1/shares/{id}/activity
Get share activity log
GET /api/shares/{id}/activity
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Share ID |
| `offset` | query | No | Pagination offset |
| `limit` | query | No | Pagination limit |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Share activity |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---

196
docs/api/social.md Normal file
View file

@ -0,0 +1,196 @@
# Social
Ratings, comments, favorites, and share links
## Endpoints
### GET /api/v1/favorites
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | User favorites |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/favorites
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Added to favorites |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### DELETE /api/v1/favorites/{media_id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `media_id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Removed from favorites |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/media/share
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Share link created |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/media/{id}/comments
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media comments |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/media/{id}/comments
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Comment added |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/media/{id}/rate
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Rating saved |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/media/{id}/ratings
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media ratings |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/shared/media/{token}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `token` | path | Yes | Share token |
| `password` | query | No | Share password |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Shared media |
| 401 | Unauthorized |
| 404 | Not found |
---

20
docs/api/statistics.md Normal file
View file

@ -0,0 +1,20 @@
# Statistics
Library statistics
## Endpoints
### GET /api/v1/statistics
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Library statistics |
| 401 | Unauthorized |
| 500 | Internal server error |
---

115
docs/api/streaming.md Normal file
View file

@ -0,0 +1,115 @@
# Streaming
HLS and DASH adaptive streaming
## Endpoints
### GET /api/v1/media/{id}/stream/dash/manifest.mpd
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | DASH manifest |
| 400 | Bad request |
| 401 | Unauthorized |
| 404 | Not found |
---
### GET /api/v1/media/{id}/stream/dash/{profile}/{segment}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
| `profile` | path | Yes | Transcode profile name |
| `segment` | path | Yes | Segment filename |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | DASH segment data |
| 202 | Segment not yet available |
| 400 | Bad request |
| 401 | Unauthorized |
---
### GET /api/v1/media/{id}/stream/hls/master.m3u8
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | HLS master playlist |
| 401 | Unauthorized |
| 404 | Not found |
---
### GET /api/v1/media/{id}/stream/hls/{profile}/playlist.m3u8
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
| `profile` | path | Yes | Transcode profile name |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | HLS variant playlist |
| 400 | Bad request |
| 401 | Unauthorized |
| 404 | Not found |
---
### GET /api/v1/media/{id}/stream/hls/{profile}/{segment}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
| `profile` | path | Yes | Transcode profile name |
| `segment` | path | Yes | Segment filename |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | HLS segment data |
| 202 | Segment not yet available |
| 400 | Bad request |
| 401 | Unauthorized |
---

120
docs/api/subtitles.md Normal file
View file

@ -0,0 +1,120 @@
# Subtitles
Media subtitle management
## Endpoints
### GET /api/v1/media/{id}/subtitles
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Subtitles |
| 401 | Unauthorized |
| 404 | Not found |
---
### POST /api/v1/media/{id}/subtitles
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Subtitle added |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/media/{media_id}/subtitles/{subtitle_id}/content
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `media_id` | path | Yes | Media item ID |
| `subtitle_id` | path | Yes | Subtitle ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Subtitle content |
| 401 | Unauthorized |
| 404 | Not found |
---
### DELETE /api/v1/subtitles/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Subtitle ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Subtitle deleted |
| 401 | Unauthorized |
| 404 | Not found |
---
### PATCH /api/v1/subtitles/{id}/offset
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Subtitle ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Offset updated |
| 401 | Unauthorized |
| 404 | Not found |
---

412
docs/api/sync.md Normal file
View file

@ -0,0 +1,412 @@
# Sync
Multi-device library synchronization
## Endpoints
### POST /api/v1/sync/ack
Acknowledge processed changes
POST /api/sync/ack
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Changes acknowledged |
| 400 | Bad request |
| 401 | Unauthorized |
---
### GET /api/v1/sync/changes
Get changes since cursor
GET /api/sync/changes
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `cursor` | query | No | Sync cursor |
| `limit` | query | No | Max changes (max 1000) |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Changes since cursor |
| 400 | Bad request |
| 401 | Unauthorized |
---
### GET /api/v1/sync/conflicts
List unresolved conflicts
GET /api/sync/conflicts
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Unresolved conflicts |
| 401 | Unauthorized |
---
### POST /api/v1/sync/conflicts/{id}/resolve
Resolve a sync conflict
POST /api/sync/conflicts/{id}/resolve
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Conflict ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Conflict resolved |
| 400 | Bad request |
| 401 | Unauthorized |
---
### GET /api/v1/sync/devices
List user's sync devices
GET /api/sync/devices
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of devices |
| 401 | Unauthorized |
---
### POST /api/v1/sync/devices
Register a new sync device
POST /api/sync/devices
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Device registered |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/sync/devices/{id}
Get device details
GET /api/sync/devices/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Device ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Device details |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### PUT /api/v1/sync/devices/{id}
Update a device
PUT /api/sync/devices/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Device ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Device updated |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### DELETE /api/v1/sync/devices/{id}
Delete a device
DELETE /api/sync/devices/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Device ID |
#### Responses
| Status | Description |
|--------|-------------|
| 204 | Device deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### POST /api/v1/sync/devices/{id}/token
Regenerate device token
POST /api/sync/devices/{id}/token
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Device ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Token regenerated |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### GET /api/v1/sync/download/{path}
Download a file for sync (supports Range header)
GET /api/sync/download/{*path}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `path` | path | Yes | File path |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | File content |
| 206 | Partial content |
| 401 | Unauthorized |
| 404 | Not found |
---
### POST /api/v1/sync/report
Report local changes from client
POST /api/sync/report
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Changes processed |
| 400 | Bad request |
| 401 | Unauthorized |
---
### POST /api/v1/sync/upload
Create an upload session for chunked upload
POST /api/sync/upload
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Upload session created |
| 400 | Bad request |
| 401 | Unauthorized |
---
### GET /api/v1/sync/upload/{id}
Get upload session status
GET /api/sync/upload/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Upload session ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Upload session status |
| 401 | Unauthorized |
| 404 | Not found |
---
### DELETE /api/v1/sync/upload/{id}
Cancel an upload session
DELETE /api/sync/upload/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Upload session ID |
#### Responses
| Status | Description |
|--------|-------------|
| 204 | Upload cancelled |
| 401 | Unauthorized |
| 404 | Not found |
---
### PUT /api/v1/sync/upload/{id}/chunks/{index}
Upload a chunk
PUT /api/sync/upload/{id}/chunks/{index}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Upload session ID |
| `index` | path | Yes | Chunk index |
#### Request Body
Chunk binary data
`Content-Type: application/octet-stream`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Chunk received |
| 400 | Bad request |
| 401 | Unauthorized |
| 404 | Not found |
---
### POST /api/v1/sync/upload/{id}/complete
Complete an upload session
POST /api/sync/upload/{id}/complete
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Upload session ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Upload completed |
| 400 | Bad request |
| 401 | Unauthorized |
| 404 | Not found |
---

157
docs/api/tags.md Normal file
View file

@ -0,0 +1,157 @@
# Tags
Media tag management
## Endpoints
### GET /api/v1/media/{media_id}/tags
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `media_id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Media tags |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### POST /api/v1/media/{media_id}/tags
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `media_id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Tag applied |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### DELETE /api/v1/media/{media_id}/tags/{tag_id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `media_id` | path | Yes | Media item ID |
| `tag_id` | path | Yes | Tag ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Tag removed |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---
### GET /api/v1/tags
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of tags |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/tags
**Authentication:** Required (Bearer JWT)
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Tag created |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### GET /api/v1/tags/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Tag ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Tag |
| 401 | Unauthorized |
| 404 | Not found |
| 500 | Internal server error |
---
### DELETE /api/v1/tags/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Tag ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Tag deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
| 500 | Internal server error |
---

86
docs/api/transcode.md Normal file
View file

@ -0,0 +1,86 @@
# Transcode
Video transcoding sessions
## Endpoints
### POST /api/v1/media/{id}/transcode
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Transcode job submitted |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/transcode
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of transcode sessions |
| 401 | Unauthorized |
---
### GET /api/v1/transcode/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Transcode session ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Transcode session details |
| 401 | Unauthorized |
| 404 | Not found |
---
### DELETE /api/v1/transcode/{id}
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Transcode session ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Transcode session cancelled |
| 401 | Unauthorized |
| 404 | Not found |
---

89
docs/api/upload.md Normal file
View file

@ -0,0 +1,89 @@
# Upload
File upload and managed storage
## Endpoints
### GET /api/v1/managed/stats
Get managed storage statistics
GET /api/managed/stats
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Managed storage statistics |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### GET /api/v1/media/{id}/download
Download a managed file
GET /api/media/{id}/download
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | File content |
| 400 | Bad request |
| 401 | Unauthorized |
| 404 | Not found |
---
### POST /api/v1/media/{id}/move-to-managed
Migrate an external file to managed storage
POST /api/media/{id}/move-to-managed
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | Media item ID |
#### Responses
| Status | Description |
|--------|-------------|
| 204 | File migrated |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---
### POST /api/v1/upload
Upload a file to managed storage
POST /api/upload
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | File uploaded |
| 400 | Bad request |
| 401 | Unauthorized |
| 500 | Internal server error |
---

207
docs/api/users.md Normal file
View file

@ -0,0 +1,207 @@
# Users
User and library access management
## Endpoints
### GET /api/v1/admin/users
List all users (admin only)
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of users |
| 401 | Unauthorized |
| 403 | Forbidden |
---
### POST /api/v1/admin/users
Create a new user (admin only)
**Authentication:** Required (Bearer JWT)
#### Request Body
username, password, role, and optional profile fields
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | User created |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 500 | Internal server error |
---
### GET /api/v1/admin/users/{id}
Get a specific user by ID
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | User ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | User details |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### PATCH /api/v1/admin/users/{id}
Update a user
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | User ID |
#### Request Body
Optional password, role, or profile fields to update
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | User updated |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### DELETE /api/v1/admin/users/{id}
Delete a user (admin only)
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | User ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | User deleted |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not found |
---
### GET /api/v1/admin/users/{id}/libraries
Get user's accessible libraries
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | User ID |
#### Responses
| Status | Description |
|--------|-------------|
| 200 | User libraries |
| 401 | Unauthorized |
| 403 | Forbidden |
---
### POST /api/v1/admin/users/{id}/libraries
Grant library access to a user (admin only)
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | User ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Access granted |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
---
### DELETE /api/v1/admin/users/{id}/libraries
Revoke library access from a user (admin only)
Uses a JSON body instead of a path parameter because `root_path` may contain
slashes that conflict with URL routing.
**Authentication:** Required (Bearer JWT)
#### Parameters
| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | Yes | User ID |
#### Request Body
`Content-Type: application/json`
See `docs/api/openapi.json` for the full schema.
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Access revoked |
| 400 | Bad request |
| 401 | Unauthorized |
| 403 | Forbidden |
---

34
docs/api/webhooks.md Normal file
View file

@ -0,0 +1,34 @@
# Webhooks
Webhook configuration
## Endpoints
### GET /api/v1/webhooks
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | List of configured webhooks |
| 401 | Unauthorized |
| 403 | Forbidden |
---
### POST /api/v1/webhooks/test
**Authentication:** Required (Bearer JWT)
#### Responses
| Status | Description |
|--------|-------------|
| 200 | Test webhook sent |
| 401 | Unauthorized |
| 403 | Forbidden |
---

17
xtask/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "xtask"
version = "0.1.0"
edition = "2024"
publish = false
[[bin]]
name = "xtask"
path = "src/main.rs"
[dependencies]
pinakes-server = { workspace = true }
utoipa = { workspace = true }
serde_json = { workspace = true }
[lints]
workspace = true

215
xtask/src/docs.rs Normal file
View file

@ -0,0 +1,215 @@
use std::{collections::BTreeMap, fmt::Write as _};
use pinakes_server::api_doc::ApiDoc;
use utoipa::{
OpenApi,
openapi::{RefOr, Required, path::ParameterIn},
};
#[expect(
clippy::expect_used,
clippy::print_stdout,
reason = "Panics are acceptable here."
)]
pub fn run() {
let api = ApiDoc::openapi();
let out_dir = std::path::Path::new("docs/api");
std::fs::create_dir_all(out_dir).expect("create docs/api dir");
let json = serde_json::to_string_pretty(&api).expect("serialize openapi");
std::fs::write(out_dir.join("openapi.json"), &json)
.expect("write openapi.json");
println!("Written docs/api/openapi.json");
// Collect all operations grouped by tag.
let mut tag_ops: BTreeMap<
String,
Vec<(String, String, &utoipa::openapi::path::Operation)>,
> = BTreeMap::new();
for (path, item) in &api.paths.paths {
let method_ops: &[(&str, Option<&utoipa::openapi::path::Operation>)] = &[
("GET", item.get.as_ref()),
("POST", item.post.as_ref()),
("PUT", item.put.as_ref()),
("PATCH", item.patch.as_ref()),
("DELETE", item.delete.as_ref()),
];
for (method, maybe_op) in method_ops {
let Some(op) = maybe_op else { continue };
let tags = op.tags.as_deref().unwrap_or(&[]);
if tags.is_empty() {
tag_ops.entry("_untagged".to_owned()).or_default().push((
(*method).to_owned(),
path.clone(),
op,
));
} else {
for tag in tags {
tag_ops.entry(tag.clone()).or_default().push((
(*method).to_owned(),
path.clone(),
op,
));
}
}
}
}
// Build a lookup from tag name to description.
let tag_descriptions: BTreeMap<String, String> = api
.tags
.as_deref()
.unwrap_or(&[])
.iter()
.map(|t| {
let desc = t.description.as_deref().unwrap_or("").to_owned();
(t.name.clone(), desc)
})
.collect();
let mut files_written = 0usize;
for (tag_name, ops) in &tag_ops {
let description = tag_descriptions.get(tag_name).map_or("", String::as_str);
let mut md = String::new();
write!(md, "# {}\n\n", title_case(tag_name)).expect("write to String");
if !description.is_empty() {
write!(md, "{description}\n\n").expect("write to String");
}
md.push_str("## Endpoints\n\n");
for (method, path, op) in ops {
write_operation(&mut md, method, path, op);
}
let file_name = format!("{}.md", tag_name.replace('/', "_"));
let dest = out_dir.join(&file_name);
std::fs::write(&dest, &md).expect("write markdown file");
println!("Written docs/api/{file_name}");
files_written += 1;
}
println!(
"Done: wrote docs/api/openapi.json and {files_written} markdown files."
);
}
fn title_case(s: &str) -> String {
let mut chars = s.chars();
chars.next().map_or_else(String::new, |c| {
c.to_uppercase().collect::<String>() + chars.as_str()
})
}
#[expect(
clippy::expect_used,
reason = "write! on String is infallible, but clippy still warns on expect()"
)]
fn write_operation(
md: &mut String,
method: &str,
path: &str,
op: &utoipa::openapi::path::Operation,
) {
let summary = op.summary.as_deref().unwrap_or("");
let description = op.description.as_deref().unwrap_or("");
write!(md, "### {method} {path}\n\n").expect("write to String");
if !summary.is_empty() {
md.push_str(summary);
md.push('\n');
if !description.is_empty() {
md.push('\n');
md.push_str(description);
md.push('\n');
}
md.push('\n');
} else if !description.is_empty() {
write!(md, "{description}\n\n").expect("write to String");
}
// Authentication
let needs_auth = op.security.as_ref().is_none_or(|s| !s.is_empty());
if needs_auth {
md.push_str("**Authentication:** Required (Bearer JWT)\n\n");
} else {
md.push_str("**Authentication:** Not required\n\n");
}
// Parameters
if let Some(params) = &op.parameters {
if !params.is_empty() {
md.push_str("#### Parameters\n\n");
md.push_str("| Name | In | Required | Description |\n");
md.push_str("|------|----|----------|-------------|\n");
for p in params {
let location = param_in_str(&p.parameter_in);
let required = match p.required {
Required::True => "Yes",
Required::False => "No",
};
let desc = p
.description
.as_deref()
.unwrap_or("")
.replace('|', "\\|")
.replace('\n', " ");
writeln!(
md,
"| `{}` | {} | {} | {} |",
p.name, location, required, desc
)
.expect("write to String");
}
md.push('\n');
}
}
// Request body
if let Some(rb) = &op.request_body {
md.push_str("#### Request Body\n\n");
if let Some(desc) = &rb.description {
writeln!(md, "{desc}").expect("write to String");
}
for content_type in rb.content.keys() {
write!(md, "`Content-Type: {content_type}`\n\n")
.expect("write to String");
md.push_str("See `docs/api/openapi.json` for the full schema.\n\n");
}
}
// Responses
let responses = &op.responses;
if !responses.responses.is_empty() {
md.push_str("#### Responses\n\n");
md.push_str("| Status | Description |\n");
md.push_str("|--------|-------------|\n");
for (status, resp) in &responses.responses {
let raw = match resp {
RefOr::T(r) => r.description.as_str(),
RefOr::Ref(_) => "See schema",
};
let desc = raw.replace('|', "\\|").replace('\n', " ");
writeln!(md, "| {status} | {desc} |").expect("write to String");
}
md.push('\n');
}
md.push_str("---\n\n");
}
const fn param_in_str(pin: &ParameterIn) -> &'static str {
match pin {
ParameterIn::Path => "path",
ParameterIn::Query => "query",
ParameterIn::Header => "header",
ParameterIn::Cookie => "cookie",
}
}

19
xtask/src/main.rs Normal file
View file

@ -0,0 +1,19 @@
mod docs;
#[expect(clippy::print_stderr)]
fn main() {
let args: Vec<String> = std::env::args().collect();
match args.get(1).map(String::as_str) {
Some("docs") => docs::run(),
Some(cmd) => {
eprintln!("Unknown command: {cmd}");
std::process::exit(1);
},
None => {
eprintln!("Usage: cargo xtask <command>");
eprintln!("Commands:");
eprintln!(" docs Generate API documentation");
std::process::exit(1);
},
}
}