Files
hermes-agent 75f75d8d19
Nix Flake Check / Nix Flake Check (push) Successful in 1m55s
Build and Test / Build and Test (push) Successful in 8m25s
Make API auth mandatory, manage tokens via web UI
BREAKING CHANGE: API authentication is now always required. The
PINCHFLAT_API_TOKEN env var is no longer used. Instead, tokens are
stored in the database and managed via Settings → API Access.

Changes:
- Add api_token column to settings table (migration)
- ApiAuthPlug reads from DB; returns 401 if no token configured
- Add API Access section to Settings page with generate/regenerate/revoke
- Add POST /settings/generate_api_token and /settings/revoke_api_token
- Remove api_token from config.exs and runtime.exs
- Update all API controller tests to set auth token in setup
- Update auth plug tests for mandatory authentication
- 1008 tests pass, zero warnings
2026-07-04 11:51:40 +00:00

104 lines
4.5 KiB
Markdown

# Pinchflat API
This fork adds a JSON REST API to Pinchflat for programmatic access, enabling
MCP server integration and other automation use cases.
## API Authentication
The API always requires a Bearer token — there is no open-access mode.
Generate a token from the web UI under **Settings → API Access**:
1. Navigate to Settings
2. Click "Generate API Token"
3. Copy the token (it won't be shown in full again)
Clients must send the token in the `Authorization` header:
```
Authorization: Bearer *** to regenerate or revoke the token at any time.
```
## API Endpoints
All endpoints are under `/api/v1`.
### Sources
| Method | Path | Description |
| ------ | --------------------------------------------------- | ------------------------------------ |
| GET | `/api/v1/sources` | List all sources |
| GET | `/api/v1/sources/:id` | Get a source with its pending tasks |
| POST | `/api/v1/sources` | Create a new source |
| PUT | `/api/v1/sources/:id` | Update a source |
| DELETE | `/api/v1/sources/:id` | Delete a source (marks for deletion) |
| POST | `/api/v1/sources/:source_id/force_download_pending` | Force download pending media |
| POST | `/api/v1/sources/:source_id/force_redownload` | Force re-download existing media |
| POST | `/api/v1/sources/:source_id/force_index` | Force re-index |
| POST | `/api/v1/sources/:source_id/force_metadata_refresh` | Force metadata refresh |
| POST | `/api/v1/sources/:source_id/sync_files_on_disk` | Sync files on disk |
### Media Items
| Method | Path | Description |
| ------ | --------------------------------------------- | ----------------------------------------------------- |
| GET | `/api/v1/media` | List all media items (optional `?source_id=X` filter) |
| GET | `/api/v1/media/search?q=query` | Search media items |
| GET | `/api/v1/media/:id` | Get a media item |
| PUT | `/api/v1/media/:id` | Update a media item |
| DELETE | `/api/v1/media/:id` | Delete media files |
| POST | `/api/v1/media/:media_item_id/force_download` | Force download a media item |
### Media Profiles
| Method | Path | Description |
| ------ | ---------------------------- | ----------------------- |
| GET | `/api/v1/media_profiles` | List all media profiles |
| GET | `/api/v1/media_profiles/:id` | Get a media profile |
| POST | `/api/v1/media_profiles` | Create a media profile |
| PUT | `/api/v1/media_profiles/:id` | Update a media profile |
| DELETE | `/api/v1/media_profiles/:id` | Delete a media profile |
### Settings
| Method | Path | Description |
| ------ | ------------------ | ----------------------------------------- |
| GET | `/api/v1/settings` | Get settings (route_token excluded) |
| PUT | `/api/v1/settings` | Update settings |
| GET | `/api/v1/app_info` | Get app info (version, environment, etc.) |
### Tasks
| Method | Path | Description |
| ------ | ------------------- | ----------------------------------------------- |
| GET | `/api/v1/tasks` | List all tasks (optional `?source_id=X` filter) |
| GET | `/api/v1/tasks/:id` | Get a task |
## Response Format
All responses are JSON. Successful responses use:
```json
{"data": ...}
```
Error responses use:
```json
{ "errors": { "field": "error message" } }
```
## Example: Create a Source
```bash
curl -X POST http://localhost:8945/api/v1/sources \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{
"source": {
"media_profile_id": 1,
"collection_type": "channel",
"original_url": "https://www.youtube.com/@SomeChannel"
}
}'
```