merge(main): scaffolding du serveur Dart headless hexagonal (ticket #47)
Fusionne feature/server-47-scaffolding — dart analyze clean, dart
test 1/1 vert, serveur démarré et GET /health validé (200
{"status":"ok"}). Premier ticket du chantier serveur (#47 à #53).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -13,3 +13,4 @@
|
||||
- [gametime-ux-exercise-default-targets](gametime-ux-exercise-default-targets.md) — memory note gametime-ux-exercise-default-targets
|
||||
- [gametime-ux-series-counter](gametime-ux-series-counter.md) — memory note gametime-ux-series-counter
|
||||
- [gametime-ux-exercise-media-viewer](gametime-ux-exercise-media-viewer.md) — memory note gametime-ux-exercise-media-viewer
|
||||
- [gametime-server-architecture-sync-sharing](gametime-server-architecture-sync-sharing.md) — memory note gametime-server-architecture-sync-sharing
|
||||
|
||||
200
.ideai/memory/gametime-server-architecture-sync-sharing.md
Normal file
200
.ideai/memory/gametime-server-architecture-sync-sharing.md
Normal file
@ -0,0 +1,200 @@
|
||||
---
|
||||
name: gametime-server-architecture-sync-sharing
|
||||
description: memory note gametime-server-architecture-sync-sharing
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
# GameTime — Architecture serveur headless, sync et partage
|
||||
|
||||
Décision serveur pour le ticket #46.
|
||||
|
||||
## Stack retenue
|
||||
|
||||
- Langage/runtime : Dart serveur.
|
||||
- Framework HTTP : `shelf` + `shelf_router`.
|
||||
- Base serveur : PostgreSQL.
|
||||
- Containerisation : Docker + `docker-compose.yaml` dans `server/`.
|
||||
|
||||
Raison : cohérence forte avec le client Flutter/Dart et les contrats métier existants, faible surface framework, testabilité correcte, packaging Docker simple avec image officielle Dart, PostgreSQL robuste pour comptes, tokens, ownership, sync incrémentale et partage ciblé.
|
||||
|
||||
Alternatives écartées pour la v1 :
|
||||
- FastAPI/Python : excellent écosystème, mais introduit un second langage et des DTO à dupliquer.
|
||||
- Node/NestJS : robuste mais plus lourd et moins cohérent avec l'existant.
|
||||
- Serverpod : intéressant en Dart, mais plus structurant/opinionated que nécessaire pour un serveur API headless simple.
|
||||
|
||||
## Architecture hexagonale serveur
|
||||
|
||||
Sous-répertoire dédié : `server/`.
|
||||
|
||||
Couches attendues :
|
||||
- `domain/` : entités serveur et invariants purs.
|
||||
- `application/` : use cases, ports repositories/services, DTO API indépendants de Shelf/PostgreSQL.
|
||||
- `infrastructure/postgres/` : adapters repositories PostgreSQL, migrations.
|
||||
- `infrastructure/security/` : hash password, token signing/verification, clock/id providers.
|
||||
- `api/` : routes Shelf, middleware auth, mapping request/response.
|
||||
- `bin/server.dart` : composition root.
|
||||
|
||||
Le domaine serveur ne dépend pas de Shelf, Docker ou PostgreSQL.
|
||||
|
||||
## Entités serveur
|
||||
|
||||
- `UserAccount` : id serveur, email/login unique, passwordHash, displayName?, createdAt, updatedAt, disabledAt?.
|
||||
- `AuthSession` / refresh token : id, userId, tokenHash, issuedAt, expiresAt, revokedAt?, userAgent?, deviceLabel?.
|
||||
- `SyncedResource` : ressource possédée par un utilisateur pour Exercise, Program, WorkoutTemplate, WorkoutHistory, MediaAsset metadata.
|
||||
- `Share` : partage ciblé vers un ou plusieurs comptes, jamais public ouvert.
|
||||
- `ShareRecipient` : user destinataire + statut `pending|accepted|declined|revoked`.
|
||||
|
||||
## Modèle sync serveur
|
||||
|
||||
Une table générique `synced_resources` est acceptable pour la v1 afin d'éviter de dupliquer tout le schéma Drift côté serveur. Champs recommandés :
|
||||
|
||||
- `server_id` UUID primary key.
|
||||
- `owner_user_id` FK users.
|
||||
- `resource_type` enum text : `exercise|program|workoutTemplate|workoutHistory|mediaAsset`.
|
||||
- `client_id` text : id local stable du client.
|
||||
- `payload_json` jsonb : snapshot de la ressource côté client.
|
||||
- `schema_version` int.
|
||||
- `client_updated_at` timestamptz.
|
||||
- `server_updated_at` timestamptz.
|
||||
- `deleted_at` timestamptz nullable.
|
||||
- `origin_device_id` text nullable.
|
||||
|
||||
Contraintes/index :
|
||||
- unique `(owner_user_id, resource_type, client_id)`.
|
||||
- index `(owner_user_id, resource_type, server_updated_at)`.
|
||||
- index `(owner_user_id, server_updated_at)` pour pull global.
|
||||
|
||||
Les médias binaires ne sont pas couverts en profondeur par #46 : stocker d'abord les métadonnées et prévoir le port `MediaObjectStore` pour ajout futur.
|
||||
|
||||
## Protocole sync LWW v1
|
||||
|
||||
Stratégie : last-write-wins simple basé sur `clientUpdatedAt`. En cas d'égalité, tie-breaker stable côté serveur (`serverUpdatedAt`, puis `serverId` si nécessaire). Pas de résolution interactive en v1.
|
||||
|
||||
Endpoints recommandés :
|
||||
|
||||
### `POST /sync/push`
|
||||
|
||||
Requête :
|
||||
|
||||
```json
|
||||
{
|
||||
"deviceId": "...",
|
||||
"items": [
|
||||
{
|
||||
"resourceType": "exercise",
|
||||
"clientId": "...",
|
||||
"schemaVersion": 3,
|
||||
"clientUpdatedAt": "2026-07-18T10:00:00Z",
|
||||
"deletedAt": null,
|
||||
"payload": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Réponse :
|
||||
|
||||
```json
|
||||
{
|
||||
"serverCursor": "...",
|
||||
"results": [
|
||||
{
|
||||
"resourceType": "exercise",
|
||||
"clientId": "...",
|
||||
"serverId": "...",
|
||||
"status": "accepted|ignoredOlder|conflictLwwApplied|error",
|
||||
"serverUpdatedAt": "2026-07-18T10:00:01Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /sync/pull?since=<cursor>`
|
||||
|
||||
Retourne toutes les ressources de l'utilisateur modifiées après le curseur serveur, soft deletes inclus.
|
||||
|
||||
Réponse :
|
||||
|
||||
```json
|
||||
{
|
||||
"serverCursor": "...",
|
||||
"items": [
|
||||
{
|
||||
"resourceType": "workoutTemplate",
|
||||
"clientId": "...",
|
||||
"serverId": "...",
|
||||
"schemaVersion": 3,
|
||||
"clientUpdatedAt": "...",
|
||||
"serverUpdatedAt": "...",
|
||||
"deletedAt": null,
|
||||
"payload": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /sync/exchange` optionnel
|
||||
|
||||
Combine push puis pull pour simplifier le futur client Flutter.
|
||||
|
||||
## Partage ciblé
|
||||
|
||||
Le partage n'est pas un lien public. Un utilisateur authentifié envoie un snapshot de `program` ou `workoutTemplate` à des destinataires identifiés.
|
||||
|
||||
Endpoints :
|
||||
- `POST /shares` : créer un partage vers un ou plusieurs comptes.
|
||||
- `GET /shares/inbox` : lister les partages reçus.
|
||||
- `POST /shares/{id}/accept` : importer/copier la ressource dans l'espace du destinataire.
|
||||
- `POST /shares/{id}/decline`.
|
||||
- `POST /shares/{id}/revoke` pour l'émetteur.
|
||||
|
||||
À l'acceptation, créer une nouvelle ressource syncable détenue par le destinataire avec nouveaux IDs côté serveur et payload importable côté client. Ne jamais modifier la ressource source de l'émetteur.
|
||||
|
||||
## Structure `server/`
|
||||
|
||||
Structure cible :
|
||||
|
||||
```text
|
||||
server/
|
||||
pubspec.yaml
|
||||
README.md
|
||||
Dockerfile
|
||||
docker-compose.yaml
|
||||
.env.example
|
||||
bin/
|
||||
server.dart
|
||||
lib/
|
||||
domain/
|
||||
application/
|
||||
infrastructure/
|
||||
postgres/
|
||||
security/
|
||||
config/
|
||||
api/
|
||||
migrations/
|
||||
scripts/
|
||||
push-gitea-image.sh
|
||||
test/
|
||||
```
|
||||
|
||||
`docker-compose.yaml` doit laisser libres via variables :
|
||||
- bind host/IP de la machine Docker.
|
||||
- port API exposé.
|
||||
- URL publique HTTPS derrière reverse proxy.
|
||||
- origine/IP reverse proxy autorisée si contrôle réseau ajouté.
|
||||
- paramètres PostgreSQL (`POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, volume).
|
||||
- secrets auth/JWT.
|
||||
|
||||
Le script `scripts/push-gitea-image.sh` ne doit contenir aucune URL/identifiant en dur. Il accepte registry/image/tag/user/token via variables d'environnement ou arguments.
|
||||
|
||||
## Tickets créés
|
||||
|
||||
- #47 `[Server] Scaffolding serveur Dart headless hexagonal`.
|
||||
- #48 `[Server] Auth comptes utilisateurs et tokens API`, dépend de #47.
|
||||
- #49 `[Server] Schéma PostgreSQL sync-ready GameTime`, dépend de #47.
|
||||
- #50 `[Server] API de synchronisation incrémentale LWW`, dépend de #48 et #49.
|
||||
- #51 `[Server] Partage ciblé de programmes et séances entre comptes`, dépend de #48 et #49.
|
||||
- #52 `[Server] Packaging Docker Compose et push Gitea Registry`, dépend de #47.
|
||||
- #53 `[Server] Tests API, contrats OpenAPI et vérification d'intégration`, dépend de #50, #51 et #52.
|
||||
|
||||
Ordre recommandé : #47, puis #48 et #49 en parallèle, puis #50 et #51, puis #52, puis #53.
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#44"
|
||||
version: 4
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784379295672
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||
updatedAt: 1784406980453
|
||||
---
|
||||
|
||||
@ -2,15 +2,15 @@
|
||||
id: "c63e88a9-5868-4bd0-a71b-ebc4e7e6552a"
|
||||
number: 44
|
||||
title: "[Bug] Je veux pouvoir mettre un score par défaut à 0."
|
||||
status: "open"
|
||||
status: "qa"
|
||||
priority: "medium"
|
||||
sprint: "abc4f969-b169-45f7-988c-daeeab762201"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||
createdAt: 1784379259225
|
||||
updatedAt: 1784379295672
|
||||
version: 4
|
||||
updatedAt: 1784406980453
|
||||
version: 5
|
||||
---
|
||||
Je veux pouvoir renseigner un score par défaut à 0 (je ne parle pas du nombre de répétition qui doit lui rester comme actuellement).
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#45"
|
||||
version: 4
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784379490098
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||
updatedAt: 1784406980638
|
||||
---
|
||||
|
||||
@ -2,15 +2,15 @@
|
||||
id: "2f075be4-bcb8-4fdd-89fc-2f897e97bde4"
|
||||
number: 45
|
||||
title: "[Bug] Affichage de répétition en séance à 0"
|
||||
status: "open"
|
||||
status: "qa"
|
||||
priority: "medium"
|
||||
sprint: "abc4f969-b169-45f7-988c-daeeab762201"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||
createdAt: 1784379414910
|
||||
updatedAt: 1784379490098
|
||||
version: 4
|
||||
updatedAt: 1784406980638
|
||||
version: 5
|
||||
---
|
||||
Quand je suis en séance sur un exercice qui demande un nombre de répétition, l'affichage m'affiche 0 alors que le nombre de répétition est set à une valeur > 0 dans l'exercice. Il devrait être à la même valeur
|
||||
6
.ideai/tickets/47/carnet.md
Normal file
6
.ideai/tickets/47/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#47"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedAt: 1784411985578
|
||||
---
|
||||
16
.ideai/tickets/47/issue.md
Normal file
16
.ideai/tickets/47/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "f3283335-8f96-40aa-88a4-bcc58a530ad2"
|
||||
number: 47
|
||||
title: "[Server] Scaffolding serveur Dart headless hexagonal"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#46","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
createdAt: 1784411985578
|
||||
updatedAt: 1784411985578
|
||||
version: 1
|
||||
---
|
||||
Créer le sous-répertoire `server/` à la racine avec une application Dart headless basée sur `shelf`/`shelf_router`, structure hexagonale (`domain/`, `application/`, `infrastructure/`, `api/`), configuration env, healthcheck et tests de base. Inclure `Dockerfile`, `.env.example`, `docker-compose.yaml` paramétrable et un README d'exploitation minimal. Le serveur doit écouter derrière reverse proxy sans HTTPS interne obligatoire.
|
||||
6
.ideai/tickets/48/carnet.md
Normal file
6
.ideai/tickets/48/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#48"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedAt: 1784411990980
|
||||
---
|
||||
16
.ideai/tickets/48/issue.md
Normal file
16
.ideai/tickets/48/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "6df36ec4-94cd-438b-a2ee-10c022f6848a"
|
||||
number: 48
|
||||
title: "[Server] Auth comptes utilisateurs et tokens API"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#46","kind":"relatesTo"},{"target":"#47","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
createdAt: 1784411990980
|
||||
updatedAt: 1784411990980
|
||||
version: 1
|
||||
---
|
||||
Implémenter le domaine serveur `UserAccount` et `AuthSession` : création de compte, connexion, hash de mot de passe Argon2id ou bcrypt, émission de tokens d'accès + refresh tokens, middleware d'auth API, révocation/logout. Stockage PostgreSQL. Endpoints attendus : `POST /auth/register`, `POST /auth/login`, `POST /auth/refresh`, `POST /auth/logout`, `GET /me`. Aucun écran client dans ce ticket.
|
||||
6
.ideai/tickets/49/carnet.md
Normal file
6
.ideai/tickets/49/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#49"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedAt: 1784411997184
|
||||
---
|
||||
16
.ideai/tickets/49/issue.md
Normal file
16
.ideai/tickets/49/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "c73b39be-061c-44b4-b5c3-f482878fe58d"
|
||||
number: 49
|
||||
title: "[Server] Schéma PostgreSQL sync-ready GameTime"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#46","kind":"relatesTo"},{"target":"#47","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
createdAt: 1784411997184
|
||||
updatedAt: 1784411997184
|
||||
version: 1
|
||||
---
|
||||
Créer le modèle serveur PostgreSQL pour les ressources synchronisables : Exercise, Program, WorkoutTemplate, WorkoutHistory, MediaAsset metadata, plus tables d'ownership par utilisateur. Chaque ressource doit stocker `ownerUserId`, `clientId`, `serverId`, `payloadJson`, `clientUpdatedAt`, `serverUpdatedAt`, `deletedAt`, `schemaVersion`, `originDeviceId`. Prévoir contraintes d'unicité `(owner_user_id, resource_type, client_id)`, index pull incrémental `(owner_user_id, resource_type, server_updated_at)`, migrations et tests repository.
|
||||
6
.ideai/tickets/50/carnet.md
Normal file
6
.ideai/tickets/50/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#50"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedAt: 1784412001974
|
||||
---
|
||||
16
.ideai/tickets/50/issue.md
Normal file
16
.ideai/tickets/50/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "983aa02b-7577-4fde-b77a-12355df0efdb"
|
||||
number: 50
|
||||
title: "[Server] API de synchronisation incrémentale LWW"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#46","kind":"relatesTo"},{"target":"#48","kind":"dependsOn"},{"target":"#49","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
createdAt: 1784412001974
|
||||
updatedAt: 1784412001974
|
||||
version: 1
|
||||
---
|
||||
Implémenter les ports/use cases et endpoints de sync incrémentale authentifiée. Endpoints : `POST /sync/push`, `GET /sync/pull?since=...`, optionnel `POST /sync/exchange`. Stratégie v1 last-write-wins basée sur `clientUpdatedAt` puis tie-breaker `serverUpdatedAt`/`serverId`. Supporter upsert, soft delete, réponses par item (`accepted`, `ignoredOlder`, `conflictLwwApplied`, `error`) et cursor serveur monotone. Ne pas intégrer le client Flutter dans ce ticket.
|
||||
6
.ideai/tickets/51/carnet.md
Normal file
6
.ideai/tickets/51/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#51"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedAt: 1784412007538
|
||||
---
|
||||
16
.ideai/tickets/51/issue.md
Normal file
16
.ideai/tickets/51/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "850ce975-99c3-4ee4-8d39-759ecc55139c"
|
||||
number: 51
|
||||
title: "[Server] Partage ciblé de programmes et séances entre comptes"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#46","kind":"relatesTo"},{"target":"#48","kind":"dependsOn"},{"target":"#49","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
createdAt: 1784412007538
|
||||
updatedAt: 1784412007538
|
||||
version: 1
|
||||
---
|
||||
Implémenter le domaine de partage ciblé : `Share`, destinataires par compte utilisateur, statut `pending|accepted|declined|revoked`, payload snapshot autonome de Program ou WorkoutTemplate. Endpoints : `POST /shares`, `GET /shares/inbox`, `POST /shares/{id}/accept`, `POST /shares/{id}/decline`, `POST /shares/{id}/revoke`. À l'acceptation, créer une copie importable dans l'espace du destinataire, sans lien public ouvert.
|
||||
6
.ideai/tickets/52/carnet.md
Normal file
6
.ideai/tickets/52/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#52"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedAt: 1784412011625
|
||||
---
|
||||
16
.ideai/tickets/52/issue.md
Normal file
16
.ideai/tickets/52/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "28ebc668-c209-4e6a-b123-252a38a3c784"
|
||||
number: 52
|
||||
title: "[Server] Packaging Docker Compose et push Gitea Registry"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#46","kind":"relatesTo"},{"target":"#47","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
createdAt: 1784412011625
|
||||
updatedAt: 1784412011625
|
||||
version: 1
|
||||
---
|
||||
Finaliser l'exploitation container : `server/docker-compose.yaml` paramétrable via `.env` pour host bind, port API interne/externe, origine reverse proxy, URL publique HTTPS, config PostgreSQL, secrets, volumes persistants. Ajouter `server/scripts/push-gitea-image.sh` acceptant registry, image, tag, username/token via variables d'environnement ou arguments, sans valeur en dur. Documenter le flux build/push/run.
|
||||
6
.ideai/tickets/53/carnet.md
Normal file
6
.ideai/tickets/53/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#53"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedAt: 1784412015509
|
||||
---
|
||||
16
.ideai/tickets/53/issue.md
Normal file
16
.ideai/tickets/53/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "d5fce252-f3c4-499f-ad0a-d3c81ea13305"
|
||||
number: 53
|
||||
title: "[Server] Tests API, contrats OpenAPI et vérification d'intégration"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#46","kind":"relatesTo"},{"target":"#50","kind":"dependsOn"},{"target":"#51","kind":"dependsOn"},{"target":"#52","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"}
|
||||
createdAt: 1784412015509
|
||||
updatedAt: 1784412015509
|
||||
version: 1
|
||||
---
|
||||
Ajouter la couverture de validation serveur : tests unitaires domain/application, tests repository PostgreSQL, tests API auth/sync/share, fixtures de payload GameTime, vérification Docker Compose locale. Produire un contrat OpenAPI ou équivalent documentant auth, sync et partage pour les futurs tickets d'intégration Flutter.
|
||||
@ -1,3 +1,3 @@
|
||||
{
|
||||
"nextNumber": 47
|
||||
"nextNumber": 54
|
||||
}
|
||||
@ -501,25 +501,25 @@
|
||||
"issueRef": "#44",
|
||||
"path": "44",
|
||||
"title": "[Bug] Je veux pouvoir mettre un score par défaut à 0.",
|
||||
"status": "open",
|
||||
"status": "qa",
|
||||
"priority": "medium",
|
||||
"sprint": "abc4f969-b169-45f7-988c-daeeab762201",
|
||||
"assignedAgentIds": [
|
||||
"57695b92-24d0-4876-837c-76116e70a6ae"
|
||||
],
|
||||
"updatedAt": 1784379295672
|
||||
"updatedAt": 1784406980453
|
||||
},
|
||||
{
|
||||
"issueRef": "#45",
|
||||
"path": "45",
|
||||
"title": "[Bug] Affichage de répétition en séance à 0",
|
||||
"status": "open",
|
||||
"status": "qa",
|
||||
"priority": "medium",
|
||||
"sprint": "abc4f969-b169-45f7-988c-daeeab762201",
|
||||
"assignedAgentIds": [
|
||||
"57695b92-24d0-4876-837c-76116e70a6ae"
|
||||
],
|
||||
"updatedAt": 1784379490098
|
||||
"updatedAt": 1784406980638
|
||||
},
|
||||
{
|
||||
"issueRef": "#46",
|
||||
@ -532,6 +532,76 @@
|
||||
"57695b92-24d0-4876-837c-76116e70a6ae"
|
||||
],
|
||||
"updatedAt": 1784380502263
|
||||
},
|
||||
{
|
||||
"issueRef": "#47",
|
||||
"path": "47",
|
||||
"title": "[Server] Scaffolding serveur Dart headless hexagonal",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784411985578
|
||||
},
|
||||
{
|
||||
"issueRef": "#48",
|
||||
"path": "48",
|
||||
"title": "[Server] Auth comptes utilisateurs et tokens API",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784411990980
|
||||
},
|
||||
{
|
||||
"issueRef": "#49",
|
||||
"path": "49",
|
||||
"title": "[Server] Schéma PostgreSQL sync-ready GameTime",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784411997184
|
||||
},
|
||||
{
|
||||
"issueRef": "#50",
|
||||
"path": "50",
|
||||
"title": "[Server] API de synchronisation incrémentale LWW",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784412001974
|
||||
},
|
||||
{
|
||||
"issueRef": "#51",
|
||||
"path": "51",
|
||||
"title": "[Server] Partage ciblé de programmes et séances entre comptes",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784412007538
|
||||
},
|
||||
{
|
||||
"issueRef": "#52",
|
||||
"path": "52",
|
||||
"title": "[Server] Packaging Docker Compose et push Gitea Registry",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784412011625
|
||||
},
|
||||
{
|
||||
"issueRef": "#53",
|
||||
"path": "53",
|
||||
"title": "[Server] Tests API, contrats OpenAPI et vérification d'intégration",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784412015509
|
||||
}
|
||||
]
|
||||
}
|
||||
51
server/README.md
Normal file
51
server/README.md
Normal file
@ -0,0 +1,51 @@
|
||||
# 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
|
||||
```
|
||||
|
||||
Healthcheck:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8080/health
|
||||
```
|
||||
|
||||
Expected response:
|
||||
|
||||
```json
|
||||
{"status":"ok"}
|
||||
```
|
||||
|
||||
## Scope
|
||||
|
||||
Ticket #47 only scaffolds the Dart server, the hexagonal directory layout and
|
||||
the `/health` endpoint.
|
||||
|
||||
Upcoming tickets will fill the empty adapters and use cases:
|
||||
|
||||
- #48: user authentication and API tokens.
|
||||
- #49: PostgreSQL schema and repositories.
|
||||
- #50: incremental sync API.
|
||||
- #51: targeted sharing.
|
||||
- #52: Docker and registry packaging.
|
||||
- #53: API, contract and integration tests.
|
||||
1
server/analysis_options.yaml
Normal file
1
server/analysis_options.yaml
Normal file
@ -0,0 +1 @@
|
||||
include: package:lints/recommended.yaml
|
||||
17
server/bin/server.dart
Normal file
17
server/bin/server.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:gametime_server/api/router.dart';
|
||||
import 'package:shelf/shelf_io.dart' as shelf_io;
|
||||
|
||||
Future<void> main(List<String> arguments) async {
|
||||
final port = int.tryParse(Platform.environment['PORT'] ?? '') ?? 8080;
|
||||
final server = await shelf_io.serve(
|
||||
buildApiHandler(),
|
||||
InternetAddress.anyIPv4,
|
||||
port,
|
||||
);
|
||||
|
||||
print(
|
||||
'GameTime server listening on ${server.address.address}:${server.port}',
|
||||
);
|
||||
}
|
||||
16
server/lib/api/router.dart
Normal file
16
server/lib/api/router.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:shelf/shelf.dart';
|
||||
import 'package:shelf_router/shelf_router.dart';
|
||||
|
||||
Handler buildApiHandler() {
|
||||
final router = Router()
|
||||
..get('/health', (Request request) {
|
||||
return Response.ok(
|
||||
jsonEncode({'status': 'ok'}),
|
||||
headers: {'content-type': 'application/json'},
|
||||
);
|
||||
});
|
||||
|
||||
return const Pipeline().addMiddleware(logRequests()).addHandler(router.call);
|
||||
}
|
||||
5
server/lib/application/README.md
Normal file
5
server/lib/application/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Application
|
||||
|
||||
Server use cases, ports and API DTOs independent from Shelf and PostgreSQL.
|
||||
|
||||
Concrete adapters are wired from `bin/server.dart`.
|
||||
6
server/lib/domain/README.md
Normal file
6
server/lib/domain/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Domain
|
||||
|
||||
Pure server domain entities and invariants.
|
||||
|
||||
This layer must not import Shelf, PostgreSQL adapters, Docker configuration or
|
||||
other infrastructure concerns.
|
||||
5
server/lib/infrastructure/postgres/README.md
Normal file
5
server/lib/infrastructure/postgres/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# PostgreSQL infrastructure
|
||||
|
||||
Placeholder for PostgreSQL repository adapters.
|
||||
|
||||
The schema and concrete persistence code are planned for ticket #49.
|
||||
5
server/lib/infrastructure/security/README.md
Normal file
5
server/lib/infrastructure/security/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Security infrastructure
|
||||
|
||||
Placeholder for password hashing, token signing and token verification.
|
||||
|
||||
Authentication is planned for ticket #48.
|
||||
4
server/migrations/README.md
Normal file
4
server/migrations/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Migrations
|
||||
|
||||
PostgreSQL migrations will be added with the server persistence schema in
|
||||
ticket #49.
|
||||
405
server/pubspec.lock
Normal file
405
server/pubspec.lock
Normal file
@ -0,0 +1,405 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "9a3386eea899815698dd55995277cf7cb8572ee52b399a6edfb7ae2b50e5fc19"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "105.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "62993bed6eadbe9596c5c20d5c167e7bc563c5fe266657a04ddeb93bdb84f4c9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.1.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.1"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
cli_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_config
|
||||
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.15.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
http_methods:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_methods
|
||||
sha256: "6bccce8f1ec7b5d701e7921dca35e202d425b57e317ba1a37f2638590e29e566"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: lints
|
||||
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.20"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_preamble
|
||||
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: ffcf4cf3d6c0b74ac43708d9f56625506e8a68aa935abe9d267a7330f320eb5d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.2"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
shelf:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shelf
|
||||
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_packages_handler
|
||||
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
shelf_router:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shelf_router
|
||||
sha256: f5e5d492440a7fb165fe1e2e1a623f31f734d3370900070b2b1e0d0428d59864
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.4"
|
||||
shelf_static:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_maps
|
||||
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.13"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: "0d5ba5602ec3baa28c8ce365e1efc5575969c765f45c554a3e167dc7945b9c30"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.31.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "475610b2aa23c19687cce2961e44b0cc57cafe220f67c2b80201231b2a07fbe7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.13"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: a39c204a4fc7a7ccb04a2b985e359fda3cc37e45e0b8ac61c3fb1a05aa832132
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.19"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.2.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.11.0 <4.0.0"
|
||||
14
server/pubspec.yaml
Normal file
14
server/pubspec.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
name: gametime_server
|
||||
description: Headless Dart server for GameTime sync and sharing.
|
||||
publish_to: 'none'
|
||||
|
||||
environment:
|
||||
sdk: ^3.10.0
|
||||
|
||||
dependencies:
|
||||
shelf: ^1.4.2
|
||||
shelf_router: ^1.1.4
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^6.0.0
|
||||
test: ^1.25.14
|
||||
5
server/scripts/README.md
Normal file
5
server/scripts/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Scripts
|
||||
|
||||
Operational scripts for the server package.
|
||||
|
||||
Docker image publishing is planned for ticket #52.
|
||||
18
server/test/health_test.dart
Normal file
18
server/test/health_test.dart
Normal file
@ -0,0 +1,18 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:gametime_server/api/router.dart';
|
||||
import 'package:shelf/shelf.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('GET /health returns ok status', () async {
|
||||
final handler = buildApiHandler();
|
||||
final response = await handler(
|
||||
Request('GET', Uri.parse('http://localhost/health')),
|
||||
);
|
||||
|
||||
expect(response.statusCode, 200);
|
||||
expect(response.headers['content-type'], contains('application/json'));
|
||||
expect(jsonDecode(await response.readAsString()), {'status': 'ok'});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user