'error', 'message' => 'action_invalide'); switch ($strAction) { case 'list_events': if (!fxChronotrackApiSettingsConfigured()) { $tabRetour = array('state' => 'error', 'message' => fxChronotrackApiSettingsErrorMessage()); break; } $strSearch = trim((string)($_REQUEST['search'] ?? '')); $blnShowPast = (intval($_REQUEST['show_past'] ?? 0) === 1); $tabRetour = fxChronotrackApiFetchEventsFiltered($strSearch, $blnShowPast); break; case 'save_link': if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } $intCtEventId = intval($_REQUEST['ct_event_id'] ?? 0); if ($intCtEventId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'ct_event_id manquant'); break; } $arrEvents = fxChronotrackApiFetchEventsFiltered('', true); $strName = ''; $strDate = ''; if ($arrEvents['state'] === 'ok') { foreach ($arrEvents['events'] as $arrEv) { if (intval($arrEv['ct_event_id']) === $intCtEventId) { $strName = $arrEv['name']; $strDate = $arrEv['start_label']; break; } } } if ($strName === '') { $strName = 'Event CT ' . $intCtEventId; } $tabRetour = fxChronotrackApiConfigSaveLink($intEveId, $intCtEventId, $strName, $strDate, $intUsaId); break; case 'set_status': $strStatus = trim((string)($_REQUEST['sync_status'] ?? '')); $tabRetour = fxChronotrackApiConfigSetStatus($intEveId, $strStatus, $intUsaId); break; case 'unlink': // MSIN-4568 — délier pour pouvoir relier un autre event CT if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } $tabRetour = fxChronotrackApiConfigUnlink($intEveId, $intUsaId); break; case 'list_races': $intCtEventId = intval($_REQUEST['ct_event_id'] ?? 0); if ($intCtEventId <= 0 && $intEveId > 0) { $arrCfg = fxChronotrackApiConfigGet($intEveId); $intCtEventId = intval($arrCfg['ct_event_id'] ?? 0); } $tabRetour = fxChronotrackApiFetchRacesForEvent($intCtEventId); break; case 'save_race_map': $tabMap = $_REQUEST['map'] ?? array(); if (is_string($tabMap)) { $tabDecoded = json_decode($tabMap, true); if (is_array($tabDecoded)) { $tabMap = $tabDecoded; } } $tabRetour = fxChronotrackApiRaceMapSave($intEveId, $tabMap); break; case 'diagnostic': $intCtEventId = intval($_REQUEST['ct_event_id'] ?? 0); if ($intCtEventId <= 0 && $intEveId > 0) { $arrCfg = fxChronotrackApiConfigGet($intEveId); $intCtEventId = intval($arrCfg['ct_event_id'] ?? 0); } $tabRetour = fxChronotrackApiRunEventDiagnostic($intCtEventId); break; case 'sync_preview': if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } set_time_limit(120); $tabRetour = fxChronotrackApiSyncBuildPreview($intEveId); break; case 'sync_ct_count': if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } $arrCfg = fxChronotrackApiConfigGet($intEveId); $intCtEventId = intval($arrCfg['ct_event_id'] ?? 0); if ($intCtEventId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'Événement non lié à ChronoTrack'); break; } set_time_limit(180); $tabRetour = fxChronotrackApiSyncFetchCtEntryCount($intCtEventId, $intEveId); break; case 'sync_mark_current': // MSIN-4328 — reclôt last_push_at sans renvoyer (ex. faux différentiel après backfill entry_id) if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } if (fxChronotrackApiConfigGet($intEveId) === null) { $tabRetour = array('state' => 'error', 'message' => 'Événement non lié à ChronoTrack'); break; } $intOkCount = max(0, intval($_REQUEST['ok_count'] ?? 0)); if (fxChronotrackApiConfigSetLastPush($intEveId, $intOkCount)) { $tabRetour = array( 'state' => 'ok', 'message' => 'Différentiel remis à zéro (last_push_at actualisé, rien envoyé à ChronoTrack).', ); } else { $tabRetour = array('state' => 'error', 'message' => 'Impossible de mettre à jour last_push_at'); } break; case 'sync_auto_save': // MSIN-4328 — phase A : config only (cron phase B) if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } $blnEnabled = (intval($_REQUEST['enabled'] ?? 0) === 1); $intInterval = intval($_REQUEST['interval_min'] ?? 15); $strUntil = trim((string)($_REQUEST['until'] ?? '')); $tabRetour = fxChronotrackApiConfigSaveAutoSync($intEveId, $blnEnabled, $intInterval, $strUntil); break; case 'sync_logs': if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } $intLimit = max(1, min(200, intval($_REQUEST['limit'] ?? 50))); $tabRetour = fxChronotrackApiSyncLogList($intEveId, $intLimit); break; case 'sync_auto_diag': // MSIN-4328 — pourquoi le cron pousse / ne pousse pas (sans essais curl) if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } $tabRetour = fxChronotrackApiSyncAutoDiag($intEveId); break; case 'sync_probe_push': if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } set_time_limit(120); $tabRetour = fxChronotrackApiSyncProbePush($intEveId); break; case 'sync_probe_custom': // MSIN-4461 — sonde entry_note_test (notes custom CT) if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } set_time_limit(120); $tabRetour = fxChronotrackApiSyncProbeCustomField($intEveId); break; case 'sync_push_prepare': if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } set_time_limit(180); $blnForceFull = (intval($_REQUEST['force_full'] ?? 0) === 1); $blnVerboseLogs = (intval($_REQUEST['verbose_logs'] ?? 0) === 1); $tabRetour = fxChronotrackApiSyncPushPrepare($intEveId, array( 'force_full' => $blnForceFull, 'verbose_logs' => $blnVerboseLogs, )); break; case 'sync_push': if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } set_time_limit(180); $intOffset = max(0, intval($_REQUEST['offset'] ?? 0)); $intLimit = max(0, intval($_REQUEST['limit'] ?? 0)); if ($intLimit <= 0) { $intLimit = MSIN_API_CHRONOTRACK_SYNC_CHUNK_SIZE; } try { $tabRetour = fxChronotrackApiSyncPushEvent($intEveId, array( 'offset' => $intOffset, 'limit' => $intLimit, )); } catch (Throwable $e) { $tabRetour = array( 'state' => 'error', 'message' => 'Exception push : ' . $e->getMessage(), ); } break; case 'sync_push_bibs': // MSIN-4574 — passe dossards découpée (évite 503 hébergeur en fin de resync) if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } set_time_limit(180); $intLimit = max(0, intval($_REQUEST['limit'] ?? 0)); if ($intLimit <= 0) { $intLimit = MSIN_API_CHRONOTRACK_SYNC_BIB_CHUNK_SIZE; } try { $tabRetour = fxChronotrackApiSyncPushBibsPhase($intEveId, array( 'limit' => $intLimit, )); } catch (Throwable $e) { $tabRetour = array( 'state' => 'error', 'message' => 'Exception dossards : ' . $e->getMessage(), ); } break; case 'sync_orphans': if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } set_time_limit(240); $tabRetour = fxChronotrackApiSyncListCtOrphans($intEveId); break; case 'sync_reconcile_withdraw': // MSIN-4574 — WITHDRAWN + clear bib sur orphelins CT (hors éligibles / doublons) if ($intEveId <= 0) { $tabRetour = array('state' => 'error', 'message' => 'eve_id manquant'); break; } set_time_limit(300); $tabRetour = fxChronotrackApiSyncWithdrawCtOrphans($intEveId); break; default: break; } if (!headers_sent()) { header('Content-Type: application/json; charset=utf-8'); } $strJsonOut = json_encode($tabRetour, JSON_UNESCAPED_UNICODE); if ($strJsonOut === false) { $strJsonOut = '{"state":"error","message":"json_encode failed"}'; } echo $strJsonOut;