feat(collector): review collector skeleton — apps/collector, its own Komodo stack on the reviewer's host
The far end of the Car Wash review outbox (wiki/concepts/vision-review-outbox.md): a small Fastify + SQLite service in the monorepo (shares the payload contract and the class vocabulary via @parking/shared), delivered to art-docker-station by its own stack so nothing booth-side lands there and nothing of it on a booth. - POST /ingest: bearer token per booth (constant-time), X-Booth-Id must match, multipart meta + JPEG (magic checked, 2 MB cap), meta validated against the contract, idempotent on the item id; crop stored at crops/<booth>/<item>.jpg on the volume + one items row. - /review + /api/*: the reviewer's screen served by the process (Basic auth, one login): one pending crop at a time, operator's pick and camera's pick beside it, one button/key per vocabulary class + unusable + skip; stats per booth and per hashed operator (agree / disagree / unusable — disagree = the reviewer's class is outside the operator's category). - GET /export/labels.csv: reviewed usable rows for training; formula-leading cells are neutralised (booth-supplied names). Crops stay on the volume for the trainer on the host. - Booth payload now carries operatorCategory.classes so the comparison needs no site setup. - Delivery: apps/collector/Dockerfile (monorepo context), docker-compose.collector.yml (bind to the overlay IP; commented `trainer` profile seam for the GPU), a third build step in build-images.yml, a `wash-collector` stack in komodo/resources.toml with one secret per booth referenced from both the collector's token list and the booth's own stack (park-2 lines templated, commented, DNS name for the URL). - Tests: app.test.ts (ingest ok/dup/refusals, review + stats + export, config). Image built and smoke-tested locally (health, ingest, duplicate, auth, verdict, export). Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
Generated
+52
@@ -18,6 +18,37 @@ importers:
|
||||
specifier: 6.0.3
|
||||
version: 6.0.3
|
||||
|
||||
apps/collector:
|
||||
dependencies:
|
||||
'@fastify/multipart':
|
||||
specifier: ^9.2.1
|
||||
version: 9.4.0
|
||||
'@parking/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/shared
|
||||
better-sqlite3:
|
||||
specifier: 12.10.1
|
||||
version: 12.10.1
|
||||
fastify:
|
||||
specifier: 5.8.5
|
||||
version: 5.8.5
|
||||
devDependencies:
|
||||
'@types/better-sqlite3':
|
||||
specifier: 7.6.13
|
||||
version: 7.6.13
|
||||
'@types/node':
|
||||
specifier: 25.9.3
|
||||
version: 25.9.3
|
||||
tsx:
|
||||
specifier: 4.22.4
|
||||
version: 4.22.4
|
||||
typescript:
|
||||
specifier: 6.0.3
|
||||
version: 6.0.3
|
||||
vitest:
|
||||
specifier: ^4.1.9
|
||||
version: 4.1.9(@types/node@25.9.3)(jsdom@25.0.1)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4))
|
||||
|
||||
apps/desktop:
|
||||
dependencies:
|
||||
'@tauri-apps/plugin-process':
|
||||
@@ -744,12 +775,18 @@ packages:
|
||||
'@fastify/ajv-compiler@4.0.5':
|
||||
resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==}
|
||||
|
||||
'@fastify/busboy@3.2.2':
|
||||
resolution: {integrity: sha512-yXSS27qPExaXeuLvMRMXOLtpipzfQYNjG3FkunDWKGfMYjKuhFXko9CVzqxm8jcF+lmtS9Fd89QNdh9XDjnbNg==}
|
||||
|
||||
'@fastify/cookie@11.0.2':
|
||||
resolution: {integrity: sha512-GWdwdGlgJxyvNv+QcKiGNevSspMQXncjMZ1J8IvuDQk0jvkzgWWZFNC2En3s+nHndZBGV8IbLwOI/sxCZw/mzA==}
|
||||
|
||||
'@fastify/cors@11.2.0':
|
||||
resolution: {integrity: sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw==}
|
||||
|
||||
'@fastify/deepmerge@3.2.1':
|
||||
resolution: {integrity: sha512-N5Oqvltoa2r9z1tbx4xjky0oRR60v+T47Ic4J1ukoVQcptLOrIdRnCSdTGmOmajZuHVKlTnfcmrjyqsGEW1ztA==}
|
||||
|
||||
'@fastify/error@4.2.0':
|
||||
resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
|
||||
|
||||
@@ -765,6 +802,9 @@ packages:
|
||||
'@fastify/merge-json-schemas@0.2.1':
|
||||
resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==}
|
||||
|
||||
'@fastify/multipart@9.4.0':
|
||||
resolution: {integrity: sha512-Z404bzZeLSXTBmp/trCBuoVFX28pM7rhv849Q5TsbTFZHuk1lc4QjQITTPK92DKVpXmNtJXeHSSc7GYvqFpxAQ==}
|
||||
|
||||
'@fastify/proxy-addr@5.1.0':
|
||||
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
|
||||
|
||||
@@ -3380,6 +3420,8 @@ snapshots:
|
||||
ajv-formats: 3.0.1(ajv@8.20.0)
|
||||
fast-uri: 3.1.2
|
||||
|
||||
'@fastify/busboy@3.2.2': {}
|
||||
|
||||
'@fastify/cookie@11.0.2':
|
||||
dependencies:
|
||||
cookie: 1.1.1
|
||||
@@ -3390,6 +3432,8 @@ snapshots:
|
||||
fastify-plugin: 5.1.0
|
||||
toad-cache: 3.7.1
|
||||
|
||||
'@fastify/deepmerge@3.2.1': {}
|
||||
|
||||
'@fastify/error@4.2.0': {}
|
||||
|
||||
'@fastify/fast-json-stringify-compiler@5.0.3':
|
||||
@@ -3410,6 +3454,14 @@ snapshots:
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
|
||||
'@fastify/multipart@9.4.0':
|
||||
dependencies:
|
||||
'@fastify/busboy': 3.2.2
|
||||
'@fastify/deepmerge': 3.2.1
|
||||
'@fastify/error': 4.2.0
|
||||
fastify-plugin: 5.1.0
|
||||
secure-json-parse: 4.1.0
|
||||
|
||||
'@fastify/proxy-addr@5.1.0':
|
||||
dependencies:
|
||||
'@fastify/forwarded': 3.0.1
|
||||
|
||||
Reference in New Issue
Block a user