Files
2026-04-08 19:37:33 +02:00

209 lines
4.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API reference
`bincio serve` exposes a JSON API on `/api/*`. In production, nginx proxies these routes from the public domain. In local development, Vite proxies them from `astro dev`.
All request and response bodies are `application/json`. Authentication uses an httpOnly session cookie (`bincio_session`).
---
## Authentication
### `GET /api/me`
Returns the currently authenticated user, or 404 if not logged in.
**Response 200**
```json
{
"handle": "dave",
"display_name": "Dave",
"is_admin": true
}
```
**Response 404** — not authenticated
---
### `POST /api/auth/login`
Rate-limited: 10 attempts per 15 minutes per IP.
**Request**
```json
{ "handle": "dave", "password": "your-password" }
```
**Response 200** — sets `bincio_session` cookie (httpOnly, SameSite=Lax, 30-day max-age)
```json
{ "ok": true, "handle": "dave", "display_name": "Dave" }
```
**Response 401** — invalid credentials
**Response 429** — rate limit exceeded
---
### `POST /api/auth/logout`
Deletes the session from the database and clears the cookie.
**Response 200**
```json
{ "ok": true }
```
---
## Registration
### `POST /api/register`
Creates a new user account using a valid invite code.
**Request**
```json
{
"code": "ABCD1234",
"handle": "alice",
"password": "my-password",
"display_name": "Alice"
}
```
Handle rules: lowercase letters, numbers, `_`, `-`; 130 characters.
Password: minimum 8 characters.
**Response 200** — sets session cookie, logs in immediately
```json
{ "ok": true, "handle": "alice" }
```
**Response 400** — invalid handle, password too short, or invalid/used invite code
**Response 409** — handle already taken
---
## Invites
All invite endpoints require authentication.
### `GET /api/invites`
Lists invite codes created by the current user.
**Response 200**
```json
[
{
"code": "ABCD1234",
"used": false,
"used_by": null,
"created_at": "2026-04-01T10:00:00Z",
"used_at": null
}
]
```
---
### `POST /api/invites`
Generates a new invite code for the current user. Regular users are limited to 3 invites; admins are unlimited.
**Response 200**
```json
{ "ok": true, "code": "EFGH5678" }
```
**Response 400** — invite limit reached
---
## Admin
### `GET /api/admin/users`
Lists all users. Admin only.
**Response 200**
```json
[
{
"handle": "dave",
"display_name": "Dave",
"is_admin": true,
"created_at": "2026-03-01T00:00:00Z"
}
]
```
**Response 403** — not an admin
---
## Write API
All write endpoints require authentication. Users can only read/write their own activities.
### `GET /api/activity/{activity_id}`
Returns the full activity JSON for an activity owned by the current user.
**Response 200** — BAS activity detail object
**Response 404** — activity not found or not owned by user
---
### `POST /api/activity/{activity_id}`
Writes a sidecar edit for an activity. Triggers an incremental shard rebuild if `--site-dir` was passed to `bincio serve`.
**Request**
```json
{
"title": "Epic climb",
"description": "Rode with friends.",
"sport": "cycling",
"private": false,
"highlight": false,
"gear": "Trek Domane"
}
```
All fields are optional. Only provided fields are written to the sidecar.
**Response 200**
```json
{ "ok": true }
```
---
### `POST /api/strava/sync`
Triggers a Strava sync for the current user's data directory. Uses the stored OAuth token in `{handle}/strava_token.json`.
**Response 200**
```json
{ "new_count": 3, "error_count": 0 }
```
---
## Error format
All errors follow FastAPI's default format:
```json
{ "detail": "Invalid credentials" }
```
---
## Notes
- The session cookie is `SameSite=Lax`. The server sets `secure=False` because TLS termination is handled by nginx/caddy. If you serve `bincio serve` directly on HTTPS (not recommended), set `secure=True` in `server.py`.
- There is no CSRF protection — the API relies on the same-origin constraint enforced by `SameSite=Lax` cookies.
- The CORS policy allows `localhost:*` origins for local development only. Cross-origin requests from production domains are blocked — all traffic must go through the nginx proxy.