File size: 2,753 Bytes
dfa877e f4d870a dfa877e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | # API contract
All routes are served by the Next.js app. Auth is either:
- `Cookie: ll_session=<jwt>` (web) — set by the OAuth callback.
- `Authorization: Bearer <jwt>` (mobile) — JWT delivered via the `langlearn://auth/callback?token=…` redirect.
The JWT is signed HS256 with `AUTH_SECRET` and contains `{ userId, hfUsername }`. TTL is 7 days.
## Auth
### `GET /api/auth/login`
Starts the HuggingFace OAuth dance.
Query params:
- `client=mobile` — if set, the callback will 302 to `langlearn://auth/callback?token=…` instead of setting a cookie. Otherwise, cookie + 302 to `/practice`.
### `GET /api/auth/callback/huggingface`
Handles HF's redirect back. Validates state + verifier cookies, exchanges code, upserts user, mints JWT. Never call directly.
### `POST /api/auth/logout`
Clears the session cookie. For mobile, the client just drops the stored JWT.
## Profile
### `GET /api/me`
```json
{
"user": { "id": "uuid", "hfUsername": "alice", "email": "a@x.y", "avatarUrl": "…" },
"profile": { "nativeLang": "en", "targetLang": "es", "level": "A2" } | null
}
```
### `PUT /api/me/profile`
Body:
```json
{ "nativeLang": "en", "targetLang": "es", "level": "A2" }
```
Responses: 200 with `{ profile }`, 400 on validation errors.
Mobile bearer-token requests also receive `{ token }` with a refreshed JWT carrying the updated profile.
## Stories
### `GET /api/stories`
Lists stories matching the user's profile (targetLang + level). Response:
```json
{ "stories": [{ "id", "title", "targetLang", "level", "createdAt" }], "profile": {...} }
```
### `POST /api/stories`
Generates a new story for the user's profile.
Body (optional): `{ "topic": "a walk in the park" }`.
Response: `{ "story": { id, title, content, vocab, ... } }`.
### `GET /api/stories/:id`
`{ "story": { ... full story ... } }`
### `POST /api/stories/:id/read`
Marks the story as read by the current user.
## Translate
### `POST /api/translate`
```json
{ "text": "perro", "from": "es", "to": "en" }
```
Response: `{ "translation": "dog", "cached": true? }`.
## Vision
### `POST /api/vision/analyze`
Multipart form with field `image` (≤ 8 MB). Response:
```json
{
"id": "uuid",
"caption": "a cat sitting on a couch",
"objects": [
{ "label": "cat", "translation": "gato", "box": [x1,y1,x2,y2], "score": 0.92 }
],
"sentences": [
{ "target": "El gato está sobre el sofá.", "gloss": "The cat is on the sofa." }
],
"imageUrl": "https://…" // empty if blob storage isn't configured
}
```
## Error shape
All failures return `{ "error": "code", "message"?: "…" }` with an appropriate HTTP status.
Common codes: `unauthenticated` (401), `invalid_input` (400), `no_profile` (400), `inference_failed` (502), `image_too_large` (413).
|