reorg documentation
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
# 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, `_`, `-`; 1–30 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.
|
||||
Reference in New Issue
Block a user