- 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.
185 lines
5.7 KiB
PHP
185 lines
5.7 KiB
PHP
<?php
|
|
|
|
function faceai_env($key, $default = null)
|
|
{
|
|
$value = getenv($key);
|
|
return $value === false ? $default : $value;
|
|
}
|
|
|
|
function faceai_config()
|
|
{
|
|
static $config = null;
|
|
|
|
if ($config !== null) {
|
|
return $config;
|
|
}
|
|
|
|
$config = array(
|
|
'frontend_url' => rtrim(faceai_env('FACEAI_FRONTEND_URL', 'http://localhost:5173'), '/'),
|
|
'backend_internal_url' => rtrim(faceai_env('FACEAI_BACKEND_INTERNAL_URL', 'http://localhost:3001'), '/'),
|
|
'shared_secret' => (string) faceai_env('FACEAI_SHARED_SECRET', 'change-me'),
|
|
'allow_dev_handoff' => faceai_env('FACEAI_ALLOW_DEV_HANDOFF', '1') === '1',
|
|
'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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|