feat(cropper): move the Image Cropper into the tools app at /cropper

- Smart Photo Editor ported from id-image-bot: queue/by-ID picture
  loading, vue-croppa editing, carvekit background cleanup, nodered
  review flow, a2loft save — restyled to the tools design language
- unified login: YOUreka session identity, phone prefilled, OTP only at
  save (a2loft's requirement); artist-login step removed
- crop_image @ domain:idid OpenFGA gate on every cropper route: cached
  5-min on reads, live on save, fail closed
- credentials (minio/azure/nodered/carvekit) via env, sole exception to
  hardcoded config; 503 when unconfigured; native FormData replaces
  form-data; new deps minio + azure-storage
- 10 new tests (35 total) with fake minio/queue/nodered/removebg/a2loft
This commit is contained in:
2026-10-01 01:13:30 +01:00
parent 531c419e32
commit d689434047
13 changed files with 2496 additions and 21 deletions
+3 -3
View File
@@ -124,8 +124,8 @@ test('landing page links both tools', async () => {
const res = await fetch(`${base}/`);
const html = await res.text();
assert.equal(res.status, 200);
assert.match(html, /smart-pxa\.yrka\.app/);
assert.match(html, /\/fingerprint/);
assert.match(html, /href="\/cropper"/);
assert.match(html, /href="\/fingerprint"/);
} finally {
await stop();
}
@@ -148,7 +148,7 @@ test('auth callback rejects unredeemable sessions without setting cookies', asyn
const { base, stop } = await bootApp({}, { cookieStatus: 'TimedOut' });
try {
const res = await fetch(`${base}/auth/callback?sessionId=s1`, manual);
assert.equal(res.status, 502);
assert.equal(res.status, 401);
assert.equal(cookieValue(res, 'session'), undefined);
const html = await res.text();
assert.match(html, /timed out/i);