docs(openspec): move-image-cropper change — specs synced, change archived
image-cropper capability added; tools-app landing + config requirements updated (internal /cropper link, credentials-via-env carve-out).
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-09-30
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
`id-image-bot` is three things: a Telegram intake bot (handlers disabled in
|
||||||
|
code), a picture-review pipeline (Azure queue `id-images`, Minio `files`
|
||||||
|
bucket, nodered review/process endpoints), and the operator web tool ("Smart
|
||||||
|
Photo Editor": Vue 2 + vue-croppa, phone/OTP login against a2loft). Only the
|
||||||
|
web tool moves; the bot and its deployment stay. The a2loft save route
|
||||||
|
(`POST /:domainCode/profile/picture`) accepts only `?phone=&otp=` — no key
|
||||||
|
variant — so the operator OTP survives, but only at save time. See proposal.md
|
||||||
|
for motivation.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
- One login story: YOUreka session everywhere; the cropper adds only a
|
||||||
|
save-time OTP (a2loft's requirement).
|
||||||
|
- Uniform `crop_image` authorization over every proxied cropper endpoint,
|
||||||
|
cheap for the portal (read cache, live at save).
|
||||||
|
- Faithful behavior: same queue/review/nodered flows and review-reason list.
|
||||||
|
|
||||||
|
**Non-Goals:**
|
||||||
|
- Moving the Telegram bot; decommissioning `spxa.yrka.app`.
|
||||||
|
- Changing the review/reject workflow or the review-reason catalog.
|
||||||
|
- Replacing toastr/swal UX patterns wholesale.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. **Namespaced router, `tools/cropper.js` with DI.** `createCropperRouter({
|
||||||
|
minio, queue, fetch, requireApi, cropGate, a2loftBase, ... })` — the same
|
||||||
|
injectable pattern as the app factory, so tests fake minio/queue/upstreams.
|
||||||
|
Mounted at `/cropper` in server.js; the UI uses relative request paths.
|
||||||
|
2. **Unified login.** The bot app's phone+OTP middleware validated every
|
||||||
|
request; here the YOUreka session replaces it, and the one a2loft call that
|
||||||
|
needs an OTP (picture save) takes an operator-entered OTP forwarded with the
|
||||||
|
session phone and the picture's domain. Phone prefill from `/auth/session`.
|
||||||
|
3. **`crop_image` gate with read cache.** `permit.js` grows `cropPermission(
|
||||||
|
phone, {force})` beside the domain checker: same verifyOnce plumbing,
|
||||||
|
`relation: 'crop_image'`, object `domain:idid`, no fallback; results cached
|
||||||
|
per phone for 5 minutes, save forces live. Router-level middleware covers
|
||||||
|
every cropper route uniformly.
|
||||||
|
4. **Credentials via env, URLs hardcoded.** `MINIO_ACCESS_KEY`,
|
||||||
|
`MINIO_SECRET_KEY`, `AZURE_STORAGE_CONNECTION_STRING`,
|
||||||
|
`NODERED_HTTP_USERNAME`, `NODERED_HTTP_PASSWORD`, `CARVEKIT_API_KEY` — same
|
||||||
|
names the bot app used, so an existing `.env` is drop-in. Clients are built
|
||||||
|
only when the env is present; without it the router answers 503 and the
|
||||||
|
rest of the app is unaffected.
|
||||||
|
5. **Restyle to the tools design language.** Same stylesheet and panel
|
||||||
|
structure as `/fingerprint` (navbar, step chips, badges, buttons); toastr
|
||||||
|
stays for toasts. No behavior change beyond namespacing and auth.
|
||||||
|
6. **Native `FormData`** (Node 22) replaces the `form-data` dependency for the
|
||||||
|
cleanup proxy.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- [a2loft `validateOtp` may not recognize the YOUreka session phone format] →
|
||||||
|
the fingerprint walkthrough already saw a2loft accept the session phone on
|
||||||
|
lookups; the field pass confirms on the save path before cutover.
|
||||||
|
- [Cached crop gate lets a revoked operator act for ≤5 minutes] → reads only;
|
||||||
|
the save verifies live, and a2loft's OTP check remains the hard gate.
|
||||||
|
- [Review/process endpoints depend on nodered availability] → unchanged from
|
||||||
|
the bot app; failures surface as route errors, not crashes.
|
||||||
|
- [Two auth domains (mnet/ucth) disappear from the login] → the save validates
|
||||||
|
against the picture's domain; the field pass exercises a real item.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
1. Land the cropper in `tools/` while `id-image-bot` keeps running unchanged.
|
||||||
|
2. Field pass: login → gate → load a real queued item (or by ID) → crop →
|
||||||
|
clean → save with OTP; confirm the picture lands in a2loft.
|
||||||
|
3. Repoint the landing card (done in the same change); leave `spxa.yrka.app`
|
||||||
|
running until the team confirms the new home.
|
||||||
|
4. Rollback: the old deployment still serves the tool.
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
None — auth unification, secret handling and scope were confirmed with the
|
||||||
|
operator.
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# Proposal: move-image-cropper
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
The tools landing page sends operators to an external deployment for identity
|
||||||
|
photo cropping (`spxa.yrka.app` — the proposal's original text said
|
||||||
|
`smart-pxa.yrka.app`, which was wrong), with a separate phone/OTP login and no
|
||||||
|
shared session. Moving the cropper into the tools app unifies auth, deployment
|
||||||
|
and the UI, and puts its externally-proxied endpoints (background cleanup,
|
||||||
|
picture storage) behind the same authorization as the rest of the tools.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- **BREAKING**: Add the Image Cropper ("Smart Photo Editor") to the tools app
|
||||||
|
at `/cropper` — page + namespaced API router (`/cropper/picture/*`,
|
||||||
|
`/cropper/profile/picture`, `/cropper/dequeue`, `/cropper/api/*`) — replacing
|
||||||
|
the landing page's external link.
|
||||||
|
- **Unified login**: the cropper's phone/OTP "Artist Login" is removed. Identity
|
||||||
|
comes from the YOUreka session; a single OTP is entered only when saving to
|
||||||
|
a2loft (the only call a2loft requires it for).
|
||||||
|
- **Crop permission gate**: page load, picture loading, background cleanup and
|
||||||
|
saving each verify `crop_image` on `domain:idid` (subsystem `idid`) via the
|
||||||
|
portal OpenFGA endpoint — fail closed; reads may serve a 5-minute cached
|
||||||
|
result, saving always checks live.
|
||||||
|
- **Restyle**: the cropper UI adopts the tools design language (navbar, cards,
|
||||||
|
badges) instead of the old grey standalone page.
|
||||||
|
- **Credentials via environment**: Minio, Azure storage, nodered and Carvekit
|
||||||
|
credentials come from `process.env` (documented in `.env.example`) — the sole
|
||||||
|
exception to the hardcoded-configuration rule; URLs stay hardcoded.
|
||||||
|
- The Telegram intake bot stays in `id-image-bot` untouched; the old cropper
|
||||||
|
deployment remains until this one is field-proven.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
- `image-cropper`: the `/cropper` photo review/crop tool — permission gate,
|
||||||
|
picture loading, background cleanup, save to a2loft with OTP.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `tools-app`: landing page links the cropper internally (`/cropper`), and the
|
||||||
|
hardcoded-configuration requirement gains a credentials-via-environment
|
||||||
|
carve-out.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- New code: `tools/cropper.js` (DI router), `tools/public/assets/cropper/`,
|
||||||
|
permit.js generalization (`crop_image` check with cache).
|
||||||
|
- New dependencies: `minio`, `azure-storage`. Removed dependency risk: none
|
||||||
|
(`form-data` replaced by native `FormData`).
|
||||||
|
- New env vars: Minio keys, Azure storage connection string, nodered
|
||||||
|
credentials, Carvekit key (`.env.example` documents all).
|
||||||
|
- External systems unchanged: Azure queue `id-images`, Minio `files` bucket,
|
||||||
|
`nodered.a2loft.net`, `removebg.itcass.net`, `apis.a2loft.net`.
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# Spec Delta: image-cropper
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
The Image Cropper at `/cropper`: operators review, crop, clean and save
|
||||||
|
identity photos submitted for YOUreka ID domain profiles, with every proxied
|
||||||
|
action authorized by the crop_image relation.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Crop permission gate
|
||||||
|
Access to the cropper SHALL require the `crop_image` relation on `domain:idid`
|
||||||
|
(subsystem `idid`) for the signed-in operator's phone, verified against the
|
||||||
|
portal OpenFGA endpoint and failing closed on denial or error. Read paths
|
||||||
|
(page load, picture loading, background cleanup, dequeue) MAY serve a cached
|
||||||
|
result for up to 5 minutes; saving SHALL always verify live.
|
||||||
|
|
||||||
|
#### Scenario: Permitted operator opens the cropper
|
||||||
|
- **WHEN** an operator with the crop_image relation requests `/cropper`
|
||||||
|
- **THEN** the editor page loads
|
||||||
|
|
||||||
|
#### Scenario: Non-permitted operator is kept out
|
||||||
|
- **WHEN** an operator without the crop_image relation requests the cropper
|
||||||
|
page
|
||||||
|
- **THEN** the page renders a not-permitted explanation with no editor UI
|
||||||
|
|
||||||
|
#### Scenario: Cached gate on reads
|
||||||
|
- **WHEN** a read action occurs within 5 minutes of the operator's last
|
||||||
|
permission check
|
||||||
|
- **THEN** the cached result is used without calling the portal
|
||||||
|
|
||||||
|
### Requirement: Picture loading is gated and namespaced
|
||||||
|
The cropper SHALL load review pictures only through its namespaced routes
|
||||||
|
(`/cropper/picture/next`, `/cropper/picture/load/:id`,
|
||||||
|
`/cropper/picture/:file_name`) behind the session and crop gates. A denied
|
||||||
|
route SHALL return 403 without contacting the queue, storage, or review
|
||||||
|
services.
|
||||||
|
|
||||||
|
#### Scenario: Next queued picture
|
||||||
|
- **WHEN** the operator requests the next picture
|
||||||
|
- **THEN** the route proxies the Azure queue and returns the queue message
|
||||||
|
|
||||||
|
#### Scenario: Denied picture load does not reach services
|
||||||
|
- **WHEN** a request without the crop_image relation loads a picture route
|
||||||
|
- **THEN** the route answers 403 and makes no upstream calls
|
||||||
|
|
||||||
|
### Requirement: Background cleanup is gated
|
||||||
|
The background-cleanup proxy (`/cropper/api/removebg`) SHALL require the
|
||||||
|
session and crop gate, forwarding the cropped image to the cleanup service
|
||||||
|
only for permitted operators.
|
||||||
|
|
||||||
|
#### Scenario: Denied cleanup does not reach the cleanup service
|
||||||
|
- **WHEN** a request without the crop_image relation calls the cleanup proxy
|
||||||
|
- **THEN** it answers 403 without contacting the cleanup service
|
||||||
|
|
||||||
|
#### Scenario: Cleanup proxies the cleaned image
|
||||||
|
- **WHEN** a permitted operator submits a cropped image for cleanup
|
||||||
|
- **THEN** the route forwards it to the cleanup service and returns the
|
||||||
|
cleaned image
|
||||||
|
|
||||||
|
### Requirement: Saving requires a live gate and an OTP
|
||||||
|
Saving a cropped picture (`/cropper/profile/picture`) SHALL verify the crop
|
||||||
|
permission live (bypassing the cache) and SHALL require an OTP from the
|
||||||
|
operator, forwarding the session phone as the validating identity together
|
||||||
|
with the OTP and the picture's domain to a2loft. A denied or OTP-less save
|
||||||
|
SHALL not reach a2loft.
|
||||||
|
|
||||||
|
#### Scenario: Successful save
|
||||||
|
- **WHEN** a permitted operator saves with an OTP
|
||||||
|
- **THEN** the route verifies the live gate and forwards the picture to
|
||||||
|
a2loft with the session phone, OTP, and picture domain
|
||||||
|
|
||||||
|
#### Scenario: Denied save does not reach a2loft
|
||||||
|
- **WHEN** the live crop check denies, or the OTP is missing
|
||||||
|
- **THEN** the route answers 403/400 and makes no a2loft call
|
||||||
|
|
||||||
|
### Requirement: Unified login — no separate artist login
|
||||||
|
The cropper SHALL NOT implement its own login. The operator's identity comes
|
||||||
|
from the YOUreka session (phone prefilled), the picture's metadata carries the
|
||||||
|
domain, and the only credential the operator supplies is the save-time OTP.
|
||||||
|
|
||||||
|
#### Scenario: Phone prefilled from the session
|
||||||
|
- **WHEN** a signed-in operator opens the cropper
|
||||||
|
- **THEN** their phone is already known to the page without any login form
|
||||||
|
|
||||||
|
### Requirement: Cropper credentials come from the environment
|
||||||
|
The cropper's service credentials (Minio keys, Azure storage connection
|
||||||
|
string, nodered credentials, cleanup-service key) SHALL be read from
|
||||||
|
environment variables. When they are absent, the cropper routes SHALL answer
|
||||||
|
503 without attempting upstream calls; the application itself SHALL still
|
||||||
|
boot.
|
||||||
|
|
||||||
|
#### Scenario: Unconfigured deployment degrades gracefully
|
||||||
|
- **WHEN** the app starts without the cropper credentials
|
||||||
|
- **THEN** the other tools work and cropper routes answer 503
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Spec Delta: tools-app
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Landing page links the YOUreka tools
|
||||||
|
The app SHALL serve a landing page at `/` containing links to the Image
|
||||||
|
Cropper (`/cropper`) and to the Fingerprint Agent page (`/fingerprint`).
|
||||||
|
|
||||||
|
#### Scenario: Landing page renders both tool links
|
||||||
|
- **WHEN** a user opens the app root URL
|
||||||
|
- **THEN** the page shows a link to the Image Cropper pointing at
|
||||||
|
`/cropper` and a link to `/fingerprint`
|
||||||
|
|
||||||
|
### Requirement: Configuration is hardcoded
|
||||||
|
The app SHALL hardcode all configuration in source: port `4173`, portal
|
||||||
|
`https://portal.yrka.app`, PocketBase `https://pb.itcass.net`, a2loft
|
||||||
|
`https://apis.a2loft.net`, and agent `http://127.0.0.1:9000`. There SHALL be
|
||||||
|
no runtime configuration via environment variables or config files, with one
|
||||||
|
exception: the Image Cropper's service credentials (Minio keys, Azure storage
|
||||||
|
connection string, nodered credentials, cleanup-service key) SHALL come from
|
||||||
|
environment variables and SHALL NOT be stored in source.
|
||||||
|
|
||||||
|
#### Scenario: No configuration required to run
|
||||||
|
- **WHEN** the app is started with no environment variables or config files
|
||||||
|
- **THEN** it serves on port 4173 with the hardcoded external endpoints
|
||||||
|
|
||||||
|
#### Scenario: Cropper credentials from the environment
|
||||||
|
- **WHEN** the cropper credentials are supplied as environment variables
|
||||||
|
- **THEN** the cropper routes use them without any change to the hardcoded
|
||||||
|
URL/port configuration
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Permission plumbing
|
||||||
|
|
||||||
|
- [x] 1.1 Generalize `permit.js`: single-object `checkPermission` for an arbitrary relation, plus `cropPermission(phone, {force})` with the 5-minute per-phone cache; verify with `node --test` and stubbed fetch covering allow, deny, cache-hit (no portal call), expiry, and live-at-save
|
||||||
|
|
||||||
|
## 2. Cropper router
|
||||||
|
|
||||||
|
- [x] 2.1 Add `tools/cropper.js` — `createCropperRouter` with DI (minio, azure queue, fetch, nodered/removebg/a2loft config): router-level session + crop gate, picture next/load-by-id/image routes, review + review-agent routes, dequeue, removebg proxy (native FormData), save proxy (live gate + OTP + session phone + picture domain), baseUrl; 503 when credentials are absent; verify with `node --test` and fake clients/upstreams covering gate denials (403, no upstream calls), cache behavior, save proxying, and the unconfigured 503 path
|
||||||
|
|
||||||
|
## 3. UI
|
||||||
|
|
||||||
|
- [x] 3.1 Port the Smart Photo Editor to `tools/public/assets/cropper/` restyled to the tools design language (navbar, panels, badges): relative API paths, phone prefilled from `/auth/session`, no login step, OTP field at save, review-reason list and crop/clean flow preserved; landing card links `/cropper` (replacing the wrong external URL); verify in the browser that the page renders, gates apply, and the flow works against fakes
|
||||||
|
|
||||||
|
## 4. Wiring and docs
|
||||||
|
|
||||||
|
- [x] 4.1 Mount the router in `server.js` (env-gated client construction), add `.env.example` with the credential names, rebuild the Docker image, and document the cropper + env vars in the README; verify the app boots with no env (other tools fine, cropper 503) and with a fake env in Docker
|
||||||
|
|
||||||
|
## 5. Field verification
|
||||||
|
|
||||||
|
- [x] 5.1 Playwright field pass with a real item: YOUreka login → gate check → load queued picture (or by ID) → crop → clean → review → save with OTP; confirm the picture lands in a2loft and a wrong-OTP save fails cleanly
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# image-cropper Specification
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
The Image Cropper at `/cropper`: operators review, crop, clean and save
|
||||||
|
identity photos submitted for YOUreka ID domain profiles, with every proxied
|
||||||
|
action authorized by the crop_image relation.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Requirement: Crop permission gate
|
||||||
|
Access to the cropper SHALL require the `crop_image` relation on `domain:idid`
|
||||||
|
(subsystem `idid`) for the signed-in operator's phone, verified against the
|
||||||
|
portal OpenFGA endpoint and failing closed on denial or error. Read paths
|
||||||
|
(page load, picture loading, background cleanup, dequeue) MAY serve a cached
|
||||||
|
result for up to 5 minutes; saving SHALL always verify live.
|
||||||
|
|
||||||
|
#### Scenario: Permitted operator opens the cropper
|
||||||
|
- **WHEN** an operator with the crop_image relation requests `/cropper`
|
||||||
|
- **THEN** the editor page loads
|
||||||
|
|
||||||
|
#### Scenario: Non-permitted operator is kept out
|
||||||
|
- **WHEN** an operator without the crop_image relation requests the cropper
|
||||||
|
page
|
||||||
|
- **THEN** the page renders a not-permitted explanation with no editor UI
|
||||||
|
|
||||||
|
#### Scenario: Cached gate on reads
|
||||||
|
- **WHEN** a read action occurs within 5 minutes of the operator's last
|
||||||
|
permission check
|
||||||
|
- **THEN** the cached result is used without calling the portal
|
||||||
|
|
||||||
|
### Requirement: Picture loading is gated and namespaced
|
||||||
|
The cropper SHALL load review pictures only through its namespaced routes
|
||||||
|
(`/cropper/picture/next`, `/cropper/picture/load/:id`,
|
||||||
|
`/cropper/picture/:file_name`) behind the session and crop gates. A denied
|
||||||
|
route SHALL return 403 without contacting the queue, storage, or review
|
||||||
|
services.
|
||||||
|
|
||||||
|
#### Scenario: Next queued picture
|
||||||
|
- **WHEN** the operator requests the next picture
|
||||||
|
- **THEN** the route proxies the Azure queue and returns the queue message
|
||||||
|
|
||||||
|
#### Scenario: Denied picture load does not reach services
|
||||||
|
- **WHEN** a request without the crop_image relation loads a picture route
|
||||||
|
- **THEN** the route answers 403 and makes no upstream calls
|
||||||
|
|
||||||
|
### Requirement: Background cleanup is gated
|
||||||
|
The background-cleanup proxy (`/cropper/api/removebg`) SHALL require the
|
||||||
|
session and crop gate, forwarding the cropped image to the cleanup service
|
||||||
|
only for permitted operators.
|
||||||
|
|
||||||
|
#### Scenario: Denied cleanup does not reach the cleanup service
|
||||||
|
- **WHEN** a request without the crop_image relation calls the cleanup proxy
|
||||||
|
- **THEN** it answers 403 without contacting the cleanup service
|
||||||
|
|
||||||
|
#### Scenario: Cleanup proxies the cleaned image
|
||||||
|
- **WHEN** a permitted operator submits a cropped image for cleanup
|
||||||
|
- **THEN** the route forwards it to the cleanup service and returns the
|
||||||
|
cleaned image
|
||||||
|
|
||||||
|
### Requirement: Saving requires a live gate and an OTP
|
||||||
|
Saving a cropped picture (`/cropper/profile/picture`) SHALL verify the crop
|
||||||
|
permission live (bypassing the cache) and SHALL require an OTP from the
|
||||||
|
operator, forwarding the session phone as the validating identity together
|
||||||
|
with the OTP and the picture's domain to a2loft. A denied or OTP-less save
|
||||||
|
SHALL not reach a2loft.
|
||||||
|
|
||||||
|
#### Scenario: Successful save
|
||||||
|
- **WHEN** a permitted operator saves with an OTP
|
||||||
|
- **THEN** the route verifies the live gate and forwards the picture to
|
||||||
|
a2loft with the session phone, OTP, and picture domain
|
||||||
|
|
||||||
|
#### Scenario: Denied save does not reach a2loft
|
||||||
|
- **WHEN** the live crop check denies, or the OTP is missing
|
||||||
|
- **THEN** the route answers 403/400 and makes no a2loft call
|
||||||
|
|
||||||
|
### Requirement: Unified login — no separate artist login
|
||||||
|
The cropper SHALL NOT implement its own login. The operator's identity comes
|
||||||
|
from the YOUreka session (phone prefilled), the picture's metadata carries the
|
||||||
|
domain, and the only credential the operator supplies is the save-time OTP.
|
||||||
|
|
||||||
|
#### Scenario: Phone prefilled from the session
|
||||||
|
- **WHEN** a signed-in operator opens the cropper
|
||||||
|
- **THEN** their phone is already known to the page without any login form
|
||||||
|
|
||||||
|
### Requirement: Cropper credentials come from the environment
|
||||||
|
The cropper's service credentials (Minio keys, Azure storage connection
|
||||||
|
string, nodered credentials, cleanup-service key) SHALL be read from
|
||||||
|
environment variables. When they are absent, the cropper routes SHALL answer
|
||||||
|
503 without attempting upstream calls; the application itself SHALL still
|
||||||
|
boot.
|
||||||
|
|
||||||
|
#### Scenario: Unconfigured deployment degrades gracefully
|
||||||
|
- **WHEN** the app starts without the cropper credentials
|
||||||
|
- **THEN** the other tools work and cropper routes answer 503
|
||||||
@@ -11,14 +11,12 @@ operator credentials directly to third parties.
|
|||||||
|
|
||||||
### Requirement: Landing page links the YOUreka tools
|
### Requirement: Landing page links the YOUreka tools
|
||||||
The app SHALL serve a landing page at `/` containing links to the Image
|
The app SHALL serve a landing page at `/` containing links to the Image
|
||||||
Cropper (`https://smart-pxa.yrka.app`) and to the Fingerprint Agent page
|
Cropper (`/cropper`) and to the Fingerprint Agent page (`/fingerprint`).
|
||||||
(`/fingerprint`).
|
|
||||||
|
|
||||||
#### Scenario: Landing page renders both tool links
|
#### Scenario: Landing page renders both tool links
|
||||||
- **WHEN** a user opens the app root URL
|
- **WHEN** a user opens the app root URL
|
||||||
- **THEN** the page shows a link to the Image Cropper pointing at
|
- **THEN** the page shows a link to the Image Cropper pointing at
|
||||||
`https://smart-pxa.yrka.app` and a link to `/fingerprint`
|
`/cropper` and a link to `/fingerprint`
|
||||||
|
|
||||||
### Requirement: YOUreka Auth login establishes a cookie session
|
### Requirement: YOUreka Auth login establishes a cookie session
|
||||||
The app SHALL authenticate operators through the YOUreka portal web-auth
|
The app SHALL authenticate operators through the YOUreka portal web-auth
|
||||||
flow: create a web auth session at the portal with a local callback URL,
|
flow: create a web auth session at the portal with a local callback URL,
|
||||||
@@ -121,9 +119,17 @@ session; the OTP SHALL NOT be accepted without one.
|
|||||||
### Requirement: Configuration is hardcoded
|
### Requirement: Configuration is hardcoded
|
||||||
The app SHALL hardcode all configuration in source: port `4173`, portal
|
The app SHALL hardcode all configuration in source: port `4173`, portal
|
||||||
`https://portal.yrka.app`, PocketBase `https://pb.itcass.net`, a2loft
|
`https://portal.yrka.app`, PocketBase `https://pb.itcass.net`, a2loft
|
||||||
`https://apis.a2loft.net`, and agent `http://127.0.0.1:9000`. There SHALL
|
`https://apis.a2loft.net`, and agent `http://127.0.0.1:9000`. There SHALL be
|
||||||
be no runtime configuration via environment variables or config files.
|
no runtime configuration via environment variables or config files, with one
|
||||||
|
exception: the Image Cropper's service credentials (Minio keys, Azure storage
|
||||||
|
connection string, nodered credentials, cleanup-service key) SHALL come from
|
||||||
|
environment variables and SHALL NOT be stored in source.
|
||||||
|
|
||||||
#### Scenario: No configuration required to run
|
#### Scenario: No configuration required to run
|
||||||
- **WHEN** the app is started with no environment variables or config files
|
- **WHEN** the app is started with no environment variables or config files
|
||||||
- **THEN** it serves on port 4173 with the hardcoded external endpoints
|
- **THEN** it serves on port 4173 with the hardcoded external endpoints
|
||||||
|
|
||||||
|
#### Scenario: Cropper credentials from the environment
|
||||||
|
- **WHEN** the cropper credentials are supplied as environment variables
|
||||||
|
- **THEN** the cropper routes use them without any change to the hardcoded
|
||||||
|
URL/port configuration
|
||||||
Reference in New Issue
Block a user