false, 'error' => 'Method not allowed'], 405); } $people = migrateAllPeople(normalizePeopleList(readJsonFile('people.json'))); $actor = requireActivePerson($people); if (($actor['role'] ?? '') !== ROLE_HEAD || !isHohVerified()) { sendJson(['success' => false, 'error' => 'Only a verified Head of household can manage NFC'], 403); } $body = readJsonBody(); $action = isset($body['action']) ? trim((string) $body['action']) : ''; if (!in_array($action, ['disable_all_chore_nfc', 'rotate_all_person_tokens'], true)) { sendJson(['success' => false, 'error' => 'Invalid action'], 400); } if ($action === 'disable_all_chore_nfc') { $rawChores = normalizeChoresList(readJsonFile('chores.json')); $chores = migrateAllChores($rawChores, $people); foreach ($chores as $i => $chore) { $meta = normalizeChoreNfcMeta($chore['nfc'] ?? null); $meta['enabled'] = false; $chore['nfc'] = $meta; $chores[$i] = migrateLegacyChoreRow($chore, $people); } if (!writeJsonFile('chores.json', $chores)) { sendJson(['success' => false, 'error' => 'Failed to save chores'], 500); } sendJson(['success' => true, 'action' => $action]); } $tokens = []; foreach ($people as $i => $person) { $personId = (string) ($person['id'] ?? ''); if ($personId === '') { continue; } $token = generateOpaqueToken(); $people[$i]['nfc_submit_token_hash'] = hashOpaqueToken($token); $people[$i]['nfc_submit_token_updated_at'] = gmdate('c'); $tokens[] = [ 'person_id' => $personId, 'person_name' => (string) ($person['name'] ?? $personId), 'person_token' => $token, ]; } if (!writeJsonFile('people.json', $people)) { sendJson(['success' => false, 'error' => 'Failed to save people'], 500); } sendJson(['success' => true, 'action' => $action, 'tokens' => $tokens]);