Ajoute l'endpoint api/share_api.dart, les use cases de partage (application/share_use_cases.dart) et l'adapter Postgres (infrastructure/postgres/share_repository.dart). L'acceptation d'un partage crée une nouvelle ressource synced_resources avec de nouveaux IDs pour le destinataire, sans jamais modifier la ressource source de l'émetteur ; gestion des conflits révoqué/déjà répondu. dart pub get OK, dart analyze clean, dart test 24/24 vert. Use case d'acceptation relu manuellement et jugé correct ; pas de test de bout en bout contre un vrai PostgreSQL faute d'accès Docker dans ce sandbox. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
138 lines
4.0 KiB
Markdown
138 lines
4.0 KiB
Markdown
# GameTime server
|
|
|
|
Headless Dart server for future GameTime account, sync and sharing features.
|
|
This package is separate from the Flutter app at the repository root.
|
|
|
|
## Local run
|
|
|
|
Install dependencies from this directory:
|
|
|
|
```bash
|
|
dart pub get
|
|
```
|
|
|
|
Run the server:
|
|
|
|
```bash
|
|
dart run bin/server.dart
|
|
```
|
|
|
|
The HTTP server binds to `0.0.0.0` and reads `PORT` from the environment.
|
|
When `PORT` is not set, it listens on `8080`.
|
|
|
|
```bash
|
|
PORT=9090 dart run bin/server.dart
|
|
```
|
|
|
|
## PostgreSQL
|
|
|
|
Ticket #49 adds the initial sync-ready PostgreSQL schema and a minimal
|
|
connection/migration utility.
|
|
|
|
Database environment variables:
|
|
|
|
- `DATABASE_HOST`: PostgreSQL host.
|
|
- `DATABASE_PORT`: PostgreSQL port, defaults to `5432` when omitted.
|
|
- `DATABASE_NAME`: database name.
|
|
- `DATABASE_USER`: database user.
|
|
- `DATABASE_PASSWORD`: database password.
|
|
|
|
Apply migrations from `server/`:
|
|
|
|
```bash
|
|
DATABASE_HOST=localhost \
|
|
DATABASE_PORT=5432 \
|
|
DATABASE_NAME=gametime \
|
|
DATABASE_USER=gametime \
|
|
DATABASE_PASSWORD=gametime \
|
|
dart run bin/migrate.dart
|
|
```
|
|
|
|
Migrations are read from `server/migrations/` in alphabetical order. The v1
|
|
runner is intentionally simple; SQL files use idempotent DDL where practical.
|
|
|
|
The PostgreSQL integration test is skipped unless `TEST_DATABASE_URL` is set:
|
|
|
|
```bash
|
|
TEST_DATABASE_URL=postgres://gametime:gametime@localhost:5432/gametime dart test
|
|
```
|
|
|
|
Healthcheck:
|
|
|
|
```bash
|
|
curl http://localhost:8080/health
|
|
```
|
|
|
|
Expected response:
|
|
|
|
```json
|
|
{"status":"ok"}
|
|
```
|
|
|
|
## Authentication
|
|
|
|
Ticket #48 adds account registration, login, logout and bearer-token request
|
|
authentication.
|
|
|
|
Endpoints:
|
|
|
|
- `POST /auth/register` with `{ "email": "...", "password": "...", "displayName": "..." }`.
|
|
- `POST /auth/login` with `{ "email": "...", "password": "...", "deviceLabel": "..." }`.
|
|
- `POST /auth/logout` with `Authorization: Bearer <token>`.
|
|
|
|
Passwords are stored with PBKDF2-HMAC-SHA256 via `package:cryptography`, using a
|
|
per-password random salt. API tokens are opaque random values; only a SHA-256
|
|
hash of the token is stored in PostgreSQL.
|
|
|
|
## Sync
|
|
|
|
Ticket #50 adds authenticated incremental sync endpoints using simple
|
|
last-write-wins conflict resolution based on `clientUpdatedAt`.
|
|
|
|
Endpoints:
|
|
|
|
- `POST /sync/push` with `Authorization: Bearer <token>`.
|
|
- `GET /sync/pull?since=<serverCursor>` with `Authorization: Bearer <token>`.
|
|
- `POST /sync/exchange` with `Authorization: Bearer <token>`.
|
|
|
|
`serverCursor` is an ISO8601 UTC timestamp. For push, it is the greatest
|
|
`server_updated_at` currently known for the authenticated user after applying
|
|
the batch. For pull, it is the greatest `serverUpdatedAt` returned, or the
|
|
server clock if no resource is returned.
|
|
|
|
`POST /sync/exchange` applies push first, then returns the pull payload with
|
|
`pushResults` included so per-item validation errors remain visible to the
|
|
client.
|
|
|
|
## Sharing
|
|
|
|
Ticket #51 adds authenticated targeted sharing for programs and workout
|
|
templates.
|
|
|
|
Endpoints:
|
|
|
|
- `POST /shares` with `Authorization: Bearer <token>`.
|
|
- `GET /shares/inbox` with `Authorization: Bearer <token>`.
|
|
- `POST /shares/{id}/accept` with `Authorization: Bearer <token>`.
|
|
- `POST /shares/{id}/decline` with `Authorization: Bearer <token>`.
|
|
- `POST /shares/{id}/revoke` with `Authorization: Bearer <token>`.
|
|
|
|
Creating a share stores a snapshot payload and creates pending recipient rows
|
|
for known recipient emails. Unknown emails are reported as `unresolvedEmails`
|
|
without failing the whole request. Accepting a share creates a new
|
|
`synced_resources` copy owned by the recipient with a server-generated
|
|
`clientId`; the sender's original resource is never modified. Accept returns
|
|
`409` for revoked or already answered shares, while missing shares or recipients
|
|
return `404`.
|
|
|
|
## Scope
|
|
|
|
Ticket #47 only scaffolds the Dart server, the hexagonal directory layout and
|
|
the `/health` endpoint. Ticket #49 adds the first PostgreSQL schema. Ticket #48
|
|
adds authentication. Ticket #50 adds sync. Ticket #51 adds targeted sharing.
|
|
|
|
Upcoming tickets will fill the empty adapters and use cases:
|
|
|
|
- #52: Docker and registry packaging.
|
|
- #53: API, contract and integration tests.
|