Regalamiunsorriso/FACEAI_INTEGRATION_PLAN.md

442 lines
18 KiB
Markdown
Raw Normal View History

2026-04-07 18:02:17 +02:00
# FaceAI Integration Plan
## Goal
Integrate a new face-ID search application at `faceai.regalamiunsorriso.it` with the legacy site at `www.regalamiunsorriso.it`, while keeping changes to the legacy site small and preserving the current download/account rules.
The new app will:
- start from the race photo view page
- search only within the current race
- accept a selfie upload
- process the request inside FaceAI
- return the user to the original race page with the results filtered to only the matched images
- keep photo opening and downloading entirely on the legacy site
- use polling only in v1 if processing takes time
2026-04-07 18:02:17 +02:00
## What Exists Today
Relevant current integration points in the legacy site:
- The race photo page still uses a `select` with id `tipoPuntoFoto` in `www/fotoCR.jsp` and `www/fotoCR-en.jsp`.
- The client-side navigation for race photo search is built in `www/_js/rus-ecom-240621.js` through `searching()`, `searchingTPF()`, `searchingPF()`, `goPage()`, and `mostraFoto()`.
- The logged-in user is exposed from the Java session as `utenteLogon` and related session beans in shared JSP includes such as `www/_inc_header.jsp`.
- The current photo modal and download gate are driven by `www/fotoView.jsp`, where download access is checked with `user.puoScaricareFoto()` or free-race logic.
- The actual download link still goes through the existing legacy file path and business rules, so photo-credit subtraction should remain on the legacy side instead of being reimplemented in Node.
## Important Constraint
There is one hard technical constraint to make explicit:
The legacy site identity is held in the Java web session, not in PHP. That means a pure PHP-only bridge on `www` cannot securely determine who the user is from the existing session cookie unless there is already an external shared auth service.
Because of that, the recommended plan needs one of these two options:
1. Preferred: a very small server-side bridge on the legacy Java or JSP side, or at the reverse proxy level with app support, to mint a trusted handoff token for FaceAI.
2026-04-07 18:02:17 +02:00
2. Fallback: a separate login flow for FaceAI.
If the requirement is strict single sign-on based on the current site session, option 1 is the only realistic path.
There is also an operational constraint from the implementation side:
The bridge should be designed so it can be deployed without setting up a full local Java development environment and without recompiling the existing application locally. That makes a tiny JSP-based handoff endpoint or a minimal existing-controller extension preferable to adding new compiled Java modules.
2026-04-07 18:02:17 +02:00
## Recommended Architecture
Use three deployable parts:
1. Legacy site at `www.regalamiunsorriso.it`
2. New user-facing app at `faceai.regalamiunsorriso.it`
3. Separate async processing service for face matching, deployed independently and fed through a queue
### 1. Legacy site responsibilities
- Keep authentication, membership checks, and download-credit subtraction as the source of truth.
- Launch the FaceAI app from the race page.
- Issue a short-lived signed handoff token that identifies the user and race context.
- Accept the final FaceAI match result and turn it into a legacy race-page filter.
2026-04-07 18:02:17 +02:00
- Continue to serve original photo downloads so existing counters and permissions remain unchanged.
### 2. FaceAI app responsibilities
- Read the handoff token or FaceAI session cookie.
- Show the legacy-like header and navigation.
- Check whether the mounted FaceAI dataset exists for the selected race before enabling uploads.
2026-04-07 18:02:17 +02:00
- Let the user upload a selfie.
- Create a race-scoped search request.
- Poll job status or show queued state.
- Return a stable list of matched legacy photo identifiers.
- Redirect the user back to the legacy race page with a filter payload that reproduces the matched set.
2026-04-07 18:02:17 +02:00
- Preserve the page the user came from and offer a one-click return.
### 3. Processing service responsibilities
- Receive a race-scoped search job.
- Queue requests and process them one by one.
- Resolve `year/monthFolder/raceFolder` inside the mounted dataset root, take the first `.pkl` file in that race directory, and run the external face-recognition program against it.
2026-04-07 18:02:17 +02:00
- Return match results with confidence and photo ids or file identifiers.
- Return a completed result set usable by the legacy filter handoff.
2026-04-07 18:02:17 +02:00
## Authentication And Cookie Strategy
## Recommendation
Do not try to make the Node app directly trust the legacy `JSESSIONID` cookie.
Even if the cookie domain is widened to `.regalamiunsorriso.it`, the Node app still cannot safely resolve that session unless both apps share the same session store and session format. That would be more invasive than a handoff bridge.
Instead:
1. User clicks the new FaceAI button on the legacy race page.
2. Legacy site creates a short-lived signed token with:
- legacy user id
- email
- display name
- language
- access flags for FaceAI
- race id
- race slug or descriptor
- race storage metadata needed to resolve the mounted FaceAI dataset:
- `year`
- `monthFolder` like `04.APRILE`
- `raceFolder` like `LIVORNO` or `PISA`
2026-04-07 18:02:17 +02:00
- current page URL as `returnUrl`
- expiry time, ideally 1 to 5 minutes
3. Browser is redirected to `https://faceai.regalamiunsorriso.it/auth/callback?token=...`
4. FaceAI validates the token and sets its own cookie on `.regalamiunsorriso.it`, for example `rus_faceai_session`
5. FaceAI uses its own session cookie afterward
This gives the new app shared-domain cookies while avoiding direct dependency on the Java session internals.
### Preferred bridge implementation shape
Given the local environment constraint, the bridge should preferably be one of these:
- a tiny JSP endpoint that reads the existing session beans and performs a redirect
- a minimal addition to an already-existing legacy action/controller endpoint
- a reverse-proxy-assisted signed redirect if the platform already supports auth subrequests
Avoid a plan that requires introducing new compiled Java packages as the first step.
2026-04-07 18:02:17 +02:00
## Access Check
The handoff token should already include whether the feature is allowed. That check should be done on the legacy side where the real account state already exists.
Minimal validation inputs:
- logged-in user exists
- account is active enough to use the feature
To avoid unnecessary database reads, compute this from already-loaded session/account state when possible. Only hit the database if the existing session object does not contain enough information.
## Minimal Changes To The Old Site
The smallest practical change set on the legacy site is:
### Frontend change
Remove the old `tipoPuntoFoto` select from the user flow and replace it with the FaceAI launch button.
2026-04-07 18:02:17 +02:00
The lowest-risk way to do that is to update `www/_js/rus-ecom-240621.js` so that on the race page it:
2026-04-07 18:02:17 +02:00
- detects `#tipoPuntoFoto`
- removes that select from the rendered UI
2026-04-07 18:02:17 +02:00
- inserts a `Face ID` button in the same area
- builds the launch URL using the current race context and current page URL
- carries `raceId`, race description or slug, `raceYear`, `raceMonthFolder`, `raceFolder`, language, and exact `returnUrl`
2026-04-07 18:02:17 +02:00
This avoids fragile JSP layout edits and keeps the change deployable as a single JS asset update.
### Server-side bridge change
Add one minimal auth bridge endpoint on the legacy stack. It can be:
- a JSP-backed endpoint
- a servlet endpoint
- or an existing controller action if the platform already has one suitable for custom commands
That endpoint should:
- read the current legacy session
- verify the user and active-membership access
2026-04-07 18:02:17 +02:00
- generate the signed handoff token
- redirect to FaceAI
If this endpoint truly cannot be added, then single sign-on should be considered blocked and the plan should switch to a separate login flow.
### Legacy return filter change
The old site needs one small return-integration path so FaceAI can send the user back to the race page showing only the matched images.
That should be implemented as one of these:
- a new legacy endpoint that accepts a signed FaceAI result token and loads the matched photo set into the request or session before rendering the race page
- an extension of the existing photo search flow so it can accept a FaceAI result id and fetch the matched photo ids server-side
This is preferable to putting the matched ids directly in the browser URL, because the result set may be long and should remain tamper-resistant.
2026-04-07 18:02:17 +02:00
## FaceAI App Structure
The target folder is `faceai/`, and this workspace now contains an implemented scaffold there.
2026-04-07 18:02:17 +02:00
Suggested structure:
```text
faceai/
app/
frontend/ # Vue UI
backend/ # Node API for auth/session/job orchestration
shared/ # shared types and config
docker/
Dockerfile
nginx.conf
docs/
api-contracts/
```
## FaceAI User Flow
1. User opens a race photo page on `www`.
2. The old `tipoPuntoFoto` dropdown is removed from the visible UI and replaced by a `Face ID` button.
2026-04-07 18:02:17 +02:00
3. User clicks the button.
4. Legacy bridge validates session and redirects to FaceAI with signed context.
5. FaceAI shows a page styled like the old site, including a matching header and a clear `Back to race page` action.
6. User uploads a selfie.
7. FaceAI creates a search job with `userId`, `raceId`, `requestId`, and selfie file reference.
8. FaceAI checks the mounted race directory immediately and, if no `.pkl` is present for that race, disables processing and offers only the return path.
9. FaceAI polls until the processing job completes.
10. Once the result is ready, FaceAI redirects the browser back to the original race page on `www`.
11. The legacy site resolves the matched photo ids and renders the race page filtered to those photos only, similar in spirit to the existing pettorale-based flow.
12. User opens and downloads photos exactly as they do today, through the legacy site.
2026-04-07 18:02:17 +02:00
## Result And Download Strategy
Do not duplicate either the final photo listing view or the download-credit logic in FaceAI.
2026-04-07 18:02:17 +02:00
Instead:
- FaceAI should store legacy photo identifiers, not its own download copies.
- FaceAI v1 should not become a second gallery UI for final results.
- When matching is complete, FaceAI should hand the result back to the legacy site.
- The legacy site should render the final matched-photo page using its existing race photo UI and existing photo modal/download endpoints.
2026-04-07 18:02:17 +02:00
This keeps the business rule in one place and avoids mismatched counters.
### Recommended handoff model for match results
Use a signed result reference instead of passing the whole match list in the browser.
Recommended flow:
1. FaceAI stores the match result under a `resultId`.
2. FaceAI redirects to something like `https://www.regalamiunsorriso.it/faceai-return?...` with:
- `resultId`
- `raceId`
- signed token
3. Legacy endpoint validates the token.
4. Legacy endpoint fetches the matched legacy photo ids from FaceAI or from a shared temporary store.
5. Legacy endpoint renders the normal race page constrained to that id set.
This avoids URL-length issues and reduces tampering risk.
2026-04-07 18:02:17 +02:00
## Matching Result Model
The processing service should return at least:
- `raceId`
- `requestId`
- `status`
- `submittedAt`
- `completedAt`
- `matches[]`
Each match should contain:
- `photoId` compatible with legacy photo endpoints
- `score` or confidence
- `capturedAt` if known
- `puntoFoto` or checkpoint info if available
For v1, `photoId` is the most important field. If the legacy page is the final renderer, thumbnails can remain a legacy concern after redirect.
2026-04-07 18:02:17 +02:00
Race scope is mandatory. The service must never search globally by default.
The mounted dataset layout is now assumed to be:
```text
/mounted-pkl-root/
2026/
04.APRILE/
PISA/
any-file-name.pkl
LIVORNO/
any-file-name.pkl
```
The `.pkl` filename does not matter. The first `.pkl` found at the race root is the one passed to the matcher.
2026-04-07 18:02:17 +02:00
## Async Processing Design
Use an API plus worker model.
### Public FaceAI backend API
- `POST /api/auth/callback` or `GET /auth/callback?token=...`
- `GET /api/session`
- `POST /api/searches`
- `GET /api/searches/:id`
- `GET /api/searches/:id/results`
- `GET /api/searches/:id/redirect`
2026-04-07 18:02:17 +02:00
- `POST /api/searches/:id/cancel` optional
### Internal worker API or queue contract
Input job:
- request id
- race id
- race storage metadata: `year`, `monthFolder`, `raceFolder`
2026-04-07 18:02:17 +02:00
- selfie storage path
- user id
- email
- timeout policy
Output job:
- success or failure
- match list
- logs
- processing duration
- legacy-renderable result reference
2026-04-07 18:02:17 +02:00
### Queue choice
Any of these are reasonable:
- Redis plus BullMQ for simpler Docker deployment
- RabbitMQ if stronger broker semantics are preferred
- orchestrator-native job queue if the platform already provides it
For this use case, Redis plus BullMQ is the most pragmatic default.
## Polling And Timeout Strategy For V1
V1 should use polling only and should not send email.
2026-04-07 18:02:17 +02:00
Recommended behavior:
2026-04-07 18:02:17 +02:00
1. FaceAI submits the job.
2. Browser polls `GET /api/searches/:id`.
3. While waiting, FaceAI shows a queue or processing state.
4. When complete, FaceAI redirects to the legacy filtered-result page.
2026-04-07 18:02:17 +02:00
Recommended timeout split:
- up to 30 seconds: keep the user on the page with polling
- beyond 30 seconds: keep polling but show a clear long-running state and a manual retry or refresh path
Email can be revisited in a later phase after the core handoff flow is stable.
2026-04-07 18:02:17 +02:00
## Database Usage
Keep database usage minimal.
Recommended storage responsibilities:
- Legacy DB remains authoritative for users, membership state, photo ownership/rules, and download counters.
- FaceAI DB stores only:
- face search requests
- job status
- uploaded selfie metadata
- result references to legacy photo ids
- audit fields
2026-04-07 18:02:17 +02:00
Avoid copying full user profiles or photo business state into the FaceAI database.
## Legacy Look And Navigation
The FaceAI app should feel like part of the existing site, but it should not depend on JSP includes at runtime.
Recommended approach:
- Copy the visual structure of `www/_inc_header.jsp` into a Vue header component.
- Keep the same main logo, colors, and top navigation destinations.
- Show account actions based on FaceAI session state.
- Add a prominent `Back to race results` link using the captured `returnUrl`.
For v1, the header should be a very close copy, not just a lightweight brand reference. The goal is that the user should feel they are still inside the same site family during the upload and waiting flow.
2026-04-07 18:02:17 +02:00
This is safer than trying to embed the old JSP header directly into a Node app.
## Security Requirements
- Handoff token must be signed and short-lived.
- FaceAI session cookie must be `HttpOnly`, `Secure`, and `SameSite=Lax` unless a stricter policy breaks the flow.
- Uploaded selfies should have a short retention period.
- Face search results must be visible only to the requesting user.
- Queue jobs must be race-scoped and tied to the authenticated user.
- Result handoff back to legacy must be signed and must not trust raw photo ids coming from the browser.
2026-04-07 18:02:17 +02:00
## Rollout Plan
### Phase 1: spike and contracts
- Confirm that a minimal JSP or existing-controller auth bridge endpoint is possible.
2026-04-07 18:02:17 +02:00
- Define the signed token payload.
- Define the worker input and output contract.
- Confirm which legacy photo id is stable enough to use in FaceAI results.
- Define how legacy will accept a FaceAI result reference and render a filtered race page.
2026-04-07 18:02:17 +02:00
### Phase 2: legacy launch integration
- Update `www/_js/rus-ecom-240621.js` to remove the dropdown from the UI and insert the FaceAI button.
2026-04-07 18:02:17 +02:00
- Add the legacy auth bridge endpoint.
- Pass `raceId`, `lang`, `returnUrl`, `raceYear`, `raceMonthFolder`, and `raceFolder` into the FaceAI launch.
- Add the legacy return endpoint or result-aware race filter path.
2026-04-07 18:02:17 +02:00
### Phase 3: FaceAI app shell
- Create `faceai/` app structure.
- Implement auth callback and FaceAI session cookie.
- Build the legacy-style header and return navigation.
- Add selfie upload UI and request status page.
- Implement polling-only job completion flow.
2026-04-07 18:02:17 +02:00
### Phase 4: processing service
- Add queue and worker.
- Integrate the external face-recognition program.
- Return matched legacy photo ids and a stored result reference suitable for legacy rendering.
2026-04-07 18:02:17 +02:00
### Phase 5: legacy filtered-results integration
2026-04-07 18:02:17 +02:00
- Redirect results back to the legacy race page.
- Verify that the legacy page can render only the matched id set.
2026-04-07 18:02:17 +02:00
- Verify that photo-credit subtraction still happens only on successful legacy downloads.
### Phase 6: optional future enhancements
2026-04-07 18:02:17 +02:00
- Add email or offline completion flow if polling-only v1 proves insufficient.
- Add richer FaceAI-side previews only if needed after the legacy handoff works reliably.
2026-04-07 18:02:17 +02:00
## Open Questions To Resolve Early
1. Which existing legacy endpoint is the best place to implement the FaceAI return filter flow?
2. Should the return flow fetch matched photo ids directly from FaceAI, or from a shared short-lived store?
3. What is the acceptable selfie retention period for privacy compliance?
4. Should long-running polling survive page refresh via persisted request id in the FaceAI session?
5. Does the legacy race page need an explicit visual label that the current listing comes from FaceAI results?
2026-04-07 18:02:17 +02:00
## Recommended First Implementation
For the first version, keep the scope strict:
- launch from one race page only
- remove `tipoPuntoFoto` from the user-facing race search UI
- polling only, no email
- final results rendered on the legacy race page, not inside FaceAI
2026-04-07 18:02:17 +02:00
- all downloads still served by the legacy site
- one lightweight auth bridge only
- one lightweight return-filter bridge only
2026-04-07 18:02:17 +02:00
This version gives the new experience without moving the fragile parts of the old platform.