2026-04-07 19:53:40 +02:00
< ? php
function faceai_env ( $key , $default = null )
{
$value = getenv ( $key );
return $value === false ? $default : $value ;
}
2026-04-12 15:21:33 +02:00
function faceai_env_flag ( $key , $default = false )
{
$value = strtolower ( trim (( string ) faceai_env ( $key , $default ? '1' : '0' )));
return in_array ( $value , array ( '1' , 'true' , 'yes' , 'on' ), true );
}
function faceai_request_host ()
{
if ( empty ( $_SERVER [ 'HTTP_HOST' ])) {
return '' ;
}
return strtolower ( trim (( string ) $_SERVER [ 'HTTP_HOST' ]));
}
function faceai_is_local_host ( $host )
{
$normalized = strtolower ( trim (( string ) $host ));
if ( $normalized === '' ) {
return false ;
}
$withoutPort = preg_replace ( '/:\d+$/' , '' , $normalized );
return in_array ( $withoutPort , array ( 'localhost' , '127.0.0.1' , '::1' ), true );
}
function faceai_request_targets_local_frontend ()
{
if ( faceai_is_local_host ( faceai_request_host ())) {
return true ;
}
$returnUrl = faceai_request_value ( 'returnUrl' );
if ( $returnUrl === '' ) {
return false ;
}
$host = parse_url ( $returnUrl , PHP_URL_HOST );
if ( ! is_string ( $host ) || $host === '' ) {
return false ;
}
return faceai_is_local_host ( $host );
}
function faceai_default_frontend_url ()
{
if ( faceai_request_targets_local_frontend ()) {
return 'http://localhost:3001' ;
}
return 'https://ai.regalamiunsorriso.it' ;
}
function faceai_default_backend_internal_url ()
{
if ( faceai_is_local_host ( faceai_request_host ())) {
return 'http://localhost:3001' ;
}
return 'https://ai.regalamiunsorriso.it' ;
}
2026-04-07 19:53:40 +02:00
function faceai_config ()
{
static $config = null ;
if ( $config !== null ) {
return $config ;
}
$config = array (
2026-04-18 10:58:04 +02:00
'feature_enabled' => faceai_env_flag ( 'FACEAI_FEATURE_ENABLED' , true ),
2026-04-12 15:21:33 +02:00
'frontend_url' => rtrim ( faceai_env ( 'FACEAI_FRONTEND_URL' , faceai_default_frontend_url ()), '/' ),
'backend_internal_url' => rtrim ( faceai_env ( 'FACEAI_BACKEND_INTERNAL_URL' , faceai_default_backend_internal_url ()), '/' ),
2026-04-18 10:58:04 +02:00
'shared_secret' => ( string ) faceai_env ( 'FACEAI_SHARED_SECRET' , 'disagio-spaghetti-science-lol-boh' ),
2026-04-12 15:21:33 +02:00
'allow_dev_handoff' => faceai_env_flag ( 'FACEAI_ALLOW_DEV_HANDOFF' , true ),
2026-04-07 19:53:40 +02:00
'identity_cookie' => ( string ) faceai_env ( 'FACEAI_IDENTITY_COOKIE' , 'rus_faceai_identity' ),
'return_forward_url' => rtrim (( string ) faceai_env ( 'FACEAI_RETURN_FORWARD_URL' , '' ), '/' )
);
return $config ;
}
function faceai_base64url_encode ( $value )
{
return rtrim ( strtr ( base64_encode ( $value ), '+/' , '-_' ), '=' );
}
function faceai_base64url_decode ( $value )
{
$padding = strlen ( $value ) % 4 ;
if ( $padding > 0 ) {
$value .= str_repeat ( '=' , 4 - $padding );
}
return base64_decode ( strtr ( $value , '-_' , '+/' ));
}
function faceai_sign_payload ( array $payload , $secret )
{
$body = faceai_base64url_encode ( json_encode ( $payload ));
$signature = hash_hmac ( 'sha256' , $body , $secret , true );
return $body . '.' . faceai_base64url_encode ( $signature );
}
function faceai_verify_payload ( $token , $secret )
{
if ( ! is_string ( $token ) || strpos ( $token , '.' ) === false ) {
throw new RuntimeException ( 'Invalid token format.' );
}
list ( $body , $signature ) = explode ( '.' , $token , 2 );
$expected = faceai_base64url_encode ( hash_hmac ( 'sha256' , $body , $secret , true ));
if ( ! hash_equals ( $expected , $signature )) {
throw new RuntimeException ( 'Invalid token signature.' );
}
$decoded = faceai_base64url_decode ( $body );
$payload = json_decode ( $decoded , true );
if ( ! is_array ( $payload )) {
throw new RuntimeException ( 'Invalid token payload.' );
}
if ( isset ( $payload [ 'expiresAt' ]) && ( int ) $payload [ 'expiresAt' ] < ( int ) round ( microtime ( true ) * 1000 )) {
throw new RuntimeException ( 'Token expired.' );
}
return $payload ;
}
function faceai_build_url ( $baseUrl , array $params )
{
return $baseUrl . ( strpos ( $baseUrl , '?' ) === false ? '?' : '&' ) . http_build_query ( $params );
}
2026-04-12 15:21:33 +02:00
function faceai_redirect_with_error ( $returnUrl , $message , $title = 'Face ID non disponibile' )
{
if ( is_string ( $returnUrl ) && trim ( $returnUrl ) !== '' ) {
header ( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' );
header ( 'Pragma: no-cache' );
header ( 'Location: ' . faceai_build_url ( $returnUrl , array (
'faceaiError' => '1' ,
'faceaiErrorTitle' => $title ,
'faceaiErrorMessage' => $message
)), true , 302 );
exit ;
}
faceai_render_message_page ( $title , $message , array (), 503 );
}
2026-04-07 19:53:40 +02:00
function faceai_request_value ( $key , $default = '' )
{
if ( ! isset ( $_GET [ $key ])) {
return $default ;
}
if ( is_array ( $_GET [ $key ])) {
return $default ;
}
return trim (( string ) $_GET [ $key ]);
}
function faceai_html ( $value )
{
return htmlspecialchars (( string ) $value , ENT_QUOTES , 'UTF-8' );
}
function faceai_resolve_identity ( array $config )
{
if ( ! empty ( $_COOKIE [ $config [ 'identity_cookie' ]])) {
$payload = faceai_verify_payload ( $_COOKIE [ $config [ 'identity_cookie' ]], $config [ 'shared_secret' ]);
if (( $payload [ 'type' ] ? ? '' ) !== 'legacy-identity' ) {
throw new RuntimeException ( 'Unexpected identity cookie payload.' );
}
return array (
'id' => ( string ) ( $payload [ 'userId' ] ? ? '' ),
'displayName' => ( string ) ( $payload [ 'displayName' ] ? ? '' ),
'email' => ( string ) ( $payload [ 'email' ] ? ? '' ),
'membershipStatus' => ( string ) ( $payload [ 'membershipStatus' ] ? ? 'inactive' )
);
}
if ( $config [ 'allow_dev_handoff' ]) {
$userId = faceai_request_value ( 'devUserId' );
if ( $userId !== '' ) {
return array (
'id' => $userId ,
'displayName' => faceai_request_value ( 'devDisplayName' , 'Local Test User' ),
'email' => faceai_request_value ( 'devEmail' , 'local.test@example.invalid' ),
'membershipStatus' => faceai_request_value ( 'devMembershipStatus' , 'active' )
);
}
}
return null ;
}
function faceai_render_message_page ( $title , $message , array $details = array (), $statusCode = 400 )
{
http_response_code ( $statusCode );
header ( 'Content-Type: text/html; charset=UTF-8' );
echo '<!doctype html><html lang="it"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">' ;
echo '<title>' . faceai_html ( $title ) . '</title>' ;
echo '<style>body{font-family:Georgia,serif;background:#f7f1e8;color:#2a231b;margin:0;padding:32px}main{max-width:900px;margin:0 auto;background:#fff;border:1px solid #ddcbb5;padding:24px}h1{margin-top:0}code{background:#f2ece2;padding:2px 5px}ul{padding-left:20px}li{margin:8px 0}</style>' ;
echo '</head><body><main>' ;
echo '<h1>' . faceai_html ( $title ) . '</h1>' ;
echo '<p>' . faceai_html ( $message ) . '</p>' ;
if ( ! empty ( $details )) {
echo '<ul>' ;
foreach ( $details as $detail ) {
echo '<li>' . faceai_html ( $detail ) . '</li>' ;
}
echo '</ul>' ;
}
echo '</main></body></html>' ;
exit ;
}
2026-04-20 19:29:22 +02:00
function faceai_render_storage_redirect_page ( $targetUrl , $storageKey , array $photoIds , $matchCount , $raceId = '' )
{
http_response_code ( 200 );
header ( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' );
header ( 'Pragma: no-cache' );
header ( 'Content-Type: text/html; charset=UTF-8' );
$payload = array (
'photoIds' => array_values ( $photoIds ),
'matchCount' => ( int ) $matchCount ,
'storedAt' => gmdate ( 'c' )
);
$pendingPayload = array (
'storageKey' => ( string ) $storageKey ,
'raceId' => trim (( string ) $raceId ),
'targetPath' => ( string ) ( parse_url (( string ) $targetUrl , PHP_URL_PATH ) ? : '' ),
'payload' => $payload
);
echo '<!doctype html><html lang="it"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">' ;
echo '<title>FaceAI redirect</title>' ;
echo '<style>body{font-family:Georgia,serif;background:#f7f1e8;color:#2a231b;margin:0;padding:32px}main{max-width:900px;margin:0 auto;background:#fff;border:1px solid #ddcbb5;padding:24px}p{margin:0 0 12px}</style>' ;
echo '</head><body><main><h1>Reindirizzamento FaceAI in corso</h1><p>Sto preparando i risultati e torno alla galleria della gara.</p><noscript><p>JavaScript e richiesto per completare il reindirizzamento FaceAI.</p></noscript>' ;
echo '<script>' ;
echo '(function () {' ;
echo 'var targetUrl = ' . json_encode (( string ) $targetUrl ) . ';' ;
echo 'var storageKey = ' . json_encode (( string ) $storageKey ) . ';' ;
echo 'var payload = ' . json_encode ( $payload ) . ';' ;
echo 'var pendingPayload = ' . json_encode ( $pendingPayload ) . ';' ;
echo 'var entryKey = "faceai-match-state:" + storageKey;' ;
echo 'var pendingEntryKey = "faceai-pending-match-state";' ;
echo 'var stored = false;' ;
echo 'function persist(storageName, key, value) {' ;
echo ' try {' ;
echo ' var storage = window[storageName];' ;
echo ' if (!storage) { return false; }' ;
echo ' storage.setItem(key, JSON.stringify(value));' ;
echo ' return storage.getItem(key) !== null;' ;
echo ' } catch (error) {' ;
echo ' return false;' ;
echo ' }' ;
echo '}' ;
echo 'stored = persist("sessionStorage", entryKey, payload) || persist("localStorage", entryKey, payload);' ;
echo 'stored = persist("sessionStorage", pendingEntryKey, pendingPayload) || persist("localStorage", pendingEntryKey, pendingPayload) || stored;' ;
echo 'try {' ;
echo ' window.name = JSON.stringify({ faceAiStorageKey: storageKey, faceAiMatchState: payload, faceAiPendingMatchState: pendingPayload });' ;
echo '} catch (error) {}' ;
echo 'if (!stored && !window.name) {' ;
echo ' document.body.innerHTML = "<main><h1>FaceAI non disponibile</h1><p>Il browser non permette di trasferire i risultati FaceAI verso la galleria.</p></main>";' ;
echo ' return;' ;
echo '}' ;
echo 'window.location.replace(targetUrl);' ;
echo '}());' ;
echo '</script></main></body></html>' ;
exit ;
}
2026-04-07 19:53:40 +02:00
function faceai_fetch_json ( $url )
{
$context = stream_context_create ( array (
'http' => array (
'ignore_errors' => true ,
'timeout' => 10
)
));
$response = @ file_get_contents ( $url , false , $context );
if ( $response === false ) {
throw new RuntimeException ( 'Unable to fetch remote FaceAI data.' );
}
$statusCode = 0 ;
if ( ! empty ( $http_response_header [ 0 ]) && preg_match ( '/\s(\d{3})\s/' , $http_response_header [ 0 ], $matches )) {
$statusCode = ( int ) $matches [ 1 ];
}
$payload = json_decode ( $response , true );
if ( ! is_array ( $payload )) {
throw new RuntimeException ( 'FaceAI returned invalid JSON.' );
}
if ( $statusCode >= 400 ) {
throw new RuntimeException ( $payload [ 'error' ] ? ? ( 'FaceAI bridge request failed with status ' . $statusCode . '.' ));
}
return $payload ;
}