Symfony 8 SPA with Doctrine ORM, Symfony Security, vanilla JS frontend. Migrated from plain PHP (delight-im/auth + raw SQL) to full Symfony stack. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2.2 KiB
API Reference
All endpoints are under /api/. JSON in, JSON out. Auth via session cookie + remember-me.
Auth
POST /api/login
{ "email": "...", "password": "..." }
Returns { ok, email, name } or 401.
POST /api/logout
Invalidates session. Returns { ok: true }.
GET /api/me
Returns { ok, email, id, name } or { ok: false } (401) if not logged in.
PATCH /api/me
{ "name": "Max" }
Updates display name. Returns { ok, name }.
POST /api/register
{ "email": "...", "password": "...", "token": "<invite-token>", "name": "..." }
Requires a valid pending invite token. Registers + auto-logs in. Returns { ok, email, name }.
POST /api/reset-request
{ "email": "..." }
Sends password reset mail. Always returns { ok: true } (no email enumeration).
POST /api/reset-password
{ "selector": "...", "token": "...", "password": "..." }
POST /api/change-password
{ "old_password": "...", "new_password": "..." }
Goals
GET /api/goals
Returns array of goal objects for the authenticated user.
[{
"id": "1",
"name": "Liegestütz",
"unit": "Stück",
"daily": 50,
"days": 30,
"start": "2026-04-01 00:00:00",
"sets": { "2026-04-01": [20, 30], "2026-04-02": [50] }
}]
sets is a JSON object keyed by date (YYYY-MM-DD), values are arrays of logged amounts.
POST /api/goals
{ "name": "...", "unit": "Stück", "daily": 50, "days": 30, "start": "2026-04-01" }
PATCH /api/goals/{id}
Partial update — send only fields to change: name, unit, daily, days, sets.
DELETE /api/goals/{id}
Deletes goal. Only owner can delete.
Invites
POST /api/invite
{ "note": "Max" }
Creates a 7-day single-use invite token. Returns { url: "http://dudi.local/?invite=<token>" }.
GET /api/invites
Returns all invites created by the authenticated user.
[{
"url": "http://dudi.local/?invite=...", // null if used or expired
"note": "Max",
"status": "pending", // "pending" | "used" | "expired"
"created_at": "...",
"expires_at": "...",
"used_at": null,
"used_by_email": null
}]