Regalamiunsorriso/FACEAI_INTEGRATION_PLAN.md
MaddoScientisto da362c201f feat: Add FaceAI integration with handoff and return functionality
- Introduced a new workspace for FaceAI in package.json.
- Implemented FaceAI handoff logic in faceai_handoff.php, including identity verification and token signing.
- Created faceai_return.php to handle return requests from FaceAI, validating tokens and forwarding results.
- Developed faceai_simulator.php and faceai_simulator_view.php for simulating the FaceAI interface with demo photos.
- Enhanced rus-ecom-240621.js to support new FaceAI features, including dynamic URL building and button integration.
- Added faceai_config.php for configuration management, including environment variable handling and utility functions.
- Updated HTML structure and styles in simulator view for better user experience.
2026-04-07 19:53:40 +02:00

17 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
  • 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

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.
  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.

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.
  • 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.
  • 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.
  • 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.
  • Return a completed result set usable by the legacy filter handoff.

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
    • 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.

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.

The lowest-risk way to do that is to update www/_js/rus-ecom-240621.js so that on the race page it:

  • detects #tipoPuntoFoto
  • removes that select from the rendered UI
  • 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, language, and exact returnUrl

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
  • 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.

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

  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.
  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 polls until the processing job completes.
  9. Once the result is ready, FaceAI redirects the browser back to the original race page on www.
  10. 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.
  11. User opens and downloads photos exactly as they do today, through the legacy site.

Result And Download Strategy

Do not duplicate either the final photo listing view or the download-credit logic in FaceAI.

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.

This keeps the business rule in one place and avoids mismatched counters.

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.

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.

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/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
  • POST /api/searches/:id/cancel optional

Internal worker API or queue contract

Input job:

  • request id
  • race id
  • selfie storage path
  • user id
  • email
  • timeout policy

Output job:

  • success or failure
  • match list
  • logs
  • processing duration
  • legacy-renderable result reference

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.

Recommended behavior:

  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.

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.

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

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.

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.

Rollout Plan

Phase 1: spike and contracts

  • Confirm that a minimal JSP or existing-controller 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.
  • Define how legacy will accept a FaceAI result reference and render a filtered race page.

Phase 2: legacy launch integration

  • Update www/_js/rus-ecom-240621.js to remove the dropdown from the UI and insert the FaceAI button.
  • Add the legacy auth bridge endpoint.
  • Pass raceId, lang, and returnUrl into the FaceAI launch.
  • Add the legacy return endpoint or result-aware race filter path.

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.

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.

Phase 5: legacy filtered-results integration

  • Redirect results back to the legacy race page.
  • Verify that the legacy page can render only the matched id set.
  • Verify that photo-credit subtraction still happens only on successful legacy downloads.

Phase 6: optional future enhancements

  • 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.

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?

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
  • all downloads still served by the legacy site
  • one lightweight auth bridge only
  • one lightweight return-filter bridge only

This version gives the new experience without moving the fragile parts of the old platform.