14 KiB
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
- show matched race photos with previews
- let the user open and download photos through the existing legacy download flow
- fall back to email delivery when the queue is long or processing is slow
What Exists Today
Relevant current integration points in the legacy site:
- The race photo page still uses a
selectwith idtipoPuntoFotoinwww/fotoCR.jspandwww/fotoCR-en.jsp. - The client-side navigation for race photo search is built in
www/_js/rus-ecom-240621.jsthroughsearching(),searchingTPF(),searchingPF(),goPage(), andmostraFoto(). - The logged-in user is exposed from the Java session as
utenteLogonand related session beans in shared JSP includes such aswww/_inc_header.jsp. - The current photo modal and download gate are driven by
www/fotoView.jsp, where download access is checked withuser.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:
- Preferred: a very small server-side bridge on the legacy Java side, or at the reverse proxy level with app support, to mint a trusted handoff token for FaceAI.
- 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.
Recommended Architecture
Use three deployable parts:
- Legacy site at
www.regalamiunsorriso.it - New user-facing app at
faceai.regalamiunsorriso.it - 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.
- 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.
- Let the user upload a selfie.
- Create a race-scoped search request.
- Poll job status or show queued state.
- Render matched photos and route download/open actions back to legacy endpoints.
- 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.
- Run the external face-recognition program.
- Return match results with confidence and photo ids or file identifiers.
- Mark long-running jobs for async completion and email fallback.
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:
- User clicks the new FaceAI button on the legacy race page.
- Legacy site creates a short-lived signed token with:
- legacy user id
- display name
- language
- access flags for FaceAI
- race id
- race slug or descriptor
- current page URL as
returnUrl - expiry time, ideally 1 to 5 minutes
- Browser is redirected to
https://faceai.regalamiunsorriso.it/auth/callback?token=... - FaceAI validates the token and sets its own cookie on
.regalamiunsorriso.it, for examplerus_faceai_session - FaceAI uses its own session cookie afterward
This gives the new app shared-domain cookies while avoiding direct dependency on the Java session internals.
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
- race is eligible for FaceAI
- optional plan or quota flag for face search access
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
Do not replace the dropdown in JSP markup first.
Instead, update www/_js/rus-ecom-240621.js so that on the race page it:
- detects
#tipoPuntoFoto - hides or disables that select for eligible races
- inserts a
Face IDbutton in the same area - builds the launch URL using the current race context and current page URL
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 access
- 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.
FaceAI App Structure
The requested target folder is faceai/. It does not currently exist in this workspace, so this plan assumes it will be created as a new app.
Suggested structure:
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
- User opens a race photo page on
www. - The old
tipoPuntoFotodropdown is replaced in the browser by aFace IDbutton. - User clicks the button.
- Legacy bridge validates session and redirects to FaceAI with signed context.
- FaceAI shows a page styled like the old site, including a matching header and a clear
Back to race pageaction. - User uploads a selfie.
- FaceAI creates a search job with
userId,raceId,requestId, and selfie file reference. - If the queue is short, FaceAI waits and then shows results.
- If processing is long, FaceAI tells the user the request will complete by email and stores the result for later retrieval.
- User opens a matched photo detail or download action.
- That action goes back through the legacy photo view/download endpoints so the current account checks and photo-count subtraction still apply.
Result And Download Strategy
Do not duplicate the download-credit logic in FaceAI.
Instead:
- FaceAI should store and display legacy photo identifiers, not its own download copies.
- When the user clicks a matched photo, FaceAI should open either:
- the existing legacy photo detail modal/page endpoint, or
- a dedicated legacy deep link for that photo
- When the user downloads, the request must end on the legacy side where
user.puoScaricareFoto()and the existing decrement rules already live.
This keeps the business rule in one place and avoids mismatched counters.
Matching Result Model
The processing service should return at least:
raceIdrequestIdstatussubmittedAtcompletedAtmatches[]
Each match should contain:
photoIdcompatible with legacy photo endpointspreviewUrlor enough file info to derive the thumbnailscoreor confidencecapturedAtif knownpuntoFotoor checkpoint info if available
Race scope is mandatory. The service must never search globally by default.
Async Processing Design
Use an API plus worker model.
Public FaceAI backend API
POST /api/auth/callbackorGET /auth/callback?token=...GET /api/sessionPOST /api/searchesGET /api/searches/:idGET /api/searches/:id/resultsPOST /api/searches/:id/canceloptional
Internal worker API or queue contract
Input job:
- request id
- race id
- selfie storage path
- user id
- timeout policy
Output job:
- success or failure
- match list
- logs
- processing duration
- email-required flag
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.
Email Fallback
If the job stays queued too long or exceeds a synchronous timeout:
- FaceAI stores the request in
queuedorprocessingstate. - Worker completes later.
- System emails the user with:
- race name
- request id
- summary of result count
- list of photo names or identifiers for the race, as requested
- optional direct link back to the FaceAI results page
Recommended timeout split:
- up to 15 to 30 seconds: keep user on the page with polling
- beyond that: switch to email fallback and let the user leave
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 and email status
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.jspinto 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 resultslink using the capturedreturnUrl.
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, andSameSite=Laxunless 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.
- Email contents should avoid exposing direct raw file paths.
Rollout Plan
Phase 1: spike and contracts
- Confirm whether a minimal legacy auth bridge endpoint is possible.
- 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.
Phase 2: legacy launch integration
- Update
www/_js/rus-ecom-240621.jsto replace the dropdown with a FaceAI button in the browser. - Add the legacy auth bridge endpoint.
- Pass
raceId,lang, andreturnUrlinto the FaceAI launch.
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.
Phase 4: processing service
- Add queue and worker.
- Integrate the external face-recognition program.
- Return matched legacy photo ids and previews.
Phase 5: download integration
- Deep-link results back to legacy photo view/download endpoints.
- Verify that photo-credit subtraction still happens only on successful legacy downloads.
Phase 6: async completion and email
- Add timeout-based fallback.
- Send race-scoped result emails with photo names and a link back to FaceAI.
Open Questions To Resolve Early
- Can the legacy site accept one minimal Java or JSP bridge endpoint for SSO handoff?
- Which exact account rule should control FaceAI access: active membership only, extra entitlement, race flag, or download quota?
- Which legacy endpoint is the best deep link for opening one photo from FaceAI results?
- Is the existing session cookie already scoped to
.regalamiunsorriso.it, or is it host-only today? - Should FaceAI results include only downloadable photos, or also visible-but-not-downloadable photos?
- What is the acceptable selfie retention period for privacy compliance?
- Should the email contain only photo names, or also signed result links?
Recommended First Implementation
For the first version, keep the scope strict:
- launch from one race page only
- synchronous search if the queue is short
- email fallback if it exceeds the timeout
- result cards with preview plus
Open on Regalami un Sorriso - all downloads still served by the legacy site
- one lightweight auth bridge only
This version gives the new experience without moving the fragile parts of the old platform.