From cc1388cc9e65439aecc87702219ead1a0fbb3e2c Mon Sep 17 00:00:00 2001 From: Blomios Date: Sun, 19 Jul 2026 00:04:34 +0200 Subject: [PATCH] feat(server): scaffolding du serveur Dart headless hexagonal (ticket #47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajoute le nouveau paquet server/ (architecture hexagonale : api, domain, application, infrastructure/postgres, infrastructure/security, migrations, scripts) avec un premier endpoint GET /health. dart pub get OK, dart analyze clean, dart test 1/1 vert, serveur démarré manuellement et validé (GET /health -> 200 {"status":"ok"}). Co-Authored-By: Claude Opus 4.8 --- server/README.md | 51 +++ server/analysis_options.yaml | 1 + server/bin/server.dart | 17 + server/lib/api/router.dart | 16 + server/lib/application/README.md | 5 + server/lib/domain/README.md | 6 + server/lib/infrastructure/postgres/README.md | 5 + server/lib/infrastructure/security/README.md | 5 + server/migrations/README.md | 4 + server/pubspec.lock | 405 +++++++++++++++++++ server/pubspec.yaml | 14 + server/scripts/README.md | 5 + server/test/health_test.dart | 18 + 13 files changed, 552 insertions(+) create mode 100644 server/README.md create mode 100644 server/analysis_options.yaml create mode 100644 server/bin/server.dart create mode 100644 server/lib/api/router.dart create mode 100644 server/lib/application/README.md create mode 100644 server/lib/domain/README.md create mode 100644 server/lib/infrastructure/postgres/README.md create mode 100644 server/lib/infrastructure/security/README.md create mode 100644 server/migrations/README.md create mode 100644 server/pubspec.lock create mode 100644 server/pubspec.yaml create mode 100644 server/scripts/README.md create mode 100644 server/test/health_test.dart diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..7eaad89 --- /dev/null +++ b/server/README.md @@ -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. diff --git a/server/analysis_options.yaml b/server/analysis_options.yaml new file mode 100644 index 0000000..572dd23 --- /dev/null +++ b/server/analysis_options.yaml @@ -0,0 +1 @@ +include: package:lints/recommended.yaml diff --git a/server/bin/server.dart b/server/bin/server.dart new file mode 100644 index 0000000..9375207 --- /dev/null +++ b/server/bin/server.dart @@ -0,0 +1,17 @@ +import 'dart:io'; + +import 'package:gametime_server/api/router.dart'; +import 'package:shelf/shelf_io.dart' as shelf_io; + +Future main(List 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}', + ); +} diff --git a/server/lib/api/router.dart b/server/lib/api/router.dart new file mode 100644 index 0000000..dce7fcd --- /dev/null +++ b/server/lib/api/router.dart @@ -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); +} diff --git a/server/lib/application/README.md b/server/lib/application/README.md new file mode 100644 index 0000000..d26d3be --- /dev/null +++ b/server/lib/application/README.md @@ -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`. diff --git a/server/lib/domain/README.md b/server/lib/domain/README.md new file mode 100644 index 0000000..7260459 --- /dev/null +++ b/server/lib/domain/README.md @@ -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. diff --git a/server/lib/infrastructure/postgres/README.md b/server/lib/infrastructure/postgres/README.md new file mode 100644 index 0000000..5afddc0 --- /dev/null +++ b/server/lib/infrastructure/postgres/README.md @@ -0,0 +1,5 @@ +# PostgreSQL infrastructure + +Placeholder for PostgreSQL repository adapters. + +The schema and concrete persistence code are planned for ticket #49. diff --git a/server/lib/infrastructure/security/README.md b/server/lib/infrastructure/security/README.md new file mode 100644 index 0000000..8297849 --- /dev/null +++ b/server/lib/infrastructure/security/README.md @@ -0,0 +1,5 @@ +# Security infrastructure + +Placeholder for password hashing, token signing and token verification. + +Authentication is planned for ticket #48. diff --git a/server/migrations/README.md b/server/migrations/README.md new file mode 100644 index 0000000..0d9e9b4 --- /dev/null +++ b/server/migrations/README.md @@ -0,0 +1,4 @@ +# Migrations + +PostgreSQL migrations will be added with the server persistence schema in +ticket #49. diff --git a/server/pubspec.lock b/server/pubspec.lock new file mode 100644 index 0000000..e14a0b3 --- /dev/null +++ b/server/pubspec.lock @@ -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" diff --git a/server/pubspec.yaml b/server/pubspec.yaml new file mode 100644 index 0000000..99945d0 --- /dev/null +++ b/server/pubspec.yaml @@ -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 diff --git a/server/scripts/README.md b/server/scripts/README.md new file mode 100644 index 0000000..53046b2 --- /dev/null +++ b/server/scripts/README.md @@ -0,0 +1,5 @@ +# Scripts + +Operational scripts for the server package. + +Docker image publishing is planned for ticket #52. diff --git a/server/test/health_test.dart b/server/test/health_test.dart new file mode 100644 index 0000000..5093cce --- /dev/null +++ b/server/test/health_test.dart @@ -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'}); + }); +}