API réception alertes chute (SmartEye/YOLO), analyse IA (Gemini 2.5 Flash), gestion alertes avec escalade (watchdog), notifications Firebase, dashboard web, documentation MkDocs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
137 lines
4.5 KiB
PHP
137 lines
4.5 KiB
PHP
<?php
|
|
/**
|
|
* provision.php — Provisioning d'un nouveau site client
|
|
*
|
|
* Crée un nouveau client dans database.json, assigne un site_number,
|
|
* génère un token, crée le dossier client, et retourne les infos
|
|
* de provisioning (dont un QR code encodé en base64).
|
|
*
|
|
* POST /provision.php
|
|
* Content-Type: application/json
|
|
* Authorization: Bearer <ADMIN_PASSWORD>
|
|
*
|
|
* {
|
|
* "client_id": "dupont_marie",
|
|
* "name": "Marie Dupont",
|
|
* "senior_name": "Mamie Marie",
|
|
* "address": "12 rue des Roses",
|
|
* "city": "20270 Aleria",
|
|
* "latitude": "42.1028",
|
|
* "longitude": "9.5147",
|
|
* "emergency_number": "15",
|
|
* "contacts": [{"name": "Jean", "role": "Fils", "phone": "+33698765432"}]
|
|
* }
|
|
*/
|
|
header("Content-Type: application/json");
|
|
header("Access-Control-Allow-Origin: *");
|
|
header("Access-Control-Allow-Methods: POST, OPTIONS");
|
|
header("Access-Control-Allow-Headers: Content-Type, Authorization");
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
http_response_code(405);
|
|
die(json_encode(["success" => false, "message" => "POST uniquement"]));
|
|
}
|
|
|
|
// Authentification admin
|
|
$ADMIN_PASS = "smart123";
|
|
$auth_header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
|
|
$provided_pass = str_replace('Bearer ', '', $auth_header);
|
|
if ($provided_pass !== $ADMIN_PASS) {
|
|
http_response_code(401);
|
|
die(json_encode(["success" => false, "message" => "Authentification admin requise"]));
|
|
}
|
|
|
|
$input = json_decode(file_get_contents("php://input"), true);
|
|
$client_id = preg_replace('/[^a-z0-9_]/', '', strtolower($input['client_id'] ?? ''));
|
|
|
|
if (empty($client_id)) {
|
|
http_response_code(400);
|
|
die(json_encode(["success" => false, "message" => "client_id requis"]));
|
|
}
|
|
|
|
$DB_FILE = "database.json";
|
|
$db = json_decode(file_get_contents($DB_FILE), true);
|
|
|
|
// Vérifier unicité
|
|
foreach ($db['clients'] as $c) {
|
|
if (strcasecmp($c['name'] ?? '', $client_id) === 0 || strcasecmp($c['name'] ?? '', $input['name'] ?? '') === 0) {
|
|
http_response_code(409);
|
|
die(json_encode(["success" => false, "message" => "Client déjà existant"]));
|
|
}
|
|
}
|
|
|
|
// Calculer le prochain site_number
|
|
$max_site = 0;
|
|
foreach ($db['clients'] as $c) {
|
|
$sn = $c['site_number'] ?? 0;
|
|
if ($sn > $max_site) $max_site = $sn;
|
|
}
|
|
$site_number = $max_site + 1;
|
|
|
|
// Générer le token
|
|
$token = bin2hex(random_bytes(4));
|
|
|
|
// Calculer les ports
|
|
$base_port = 8550 + ($site_number - 1) * 10;
|
|
|
|
// Créer l'entrée client
|
|
$db['clients'][$client_id] = [
|
|
"name" => $input['name'] ?? $client_id,
|
|
"token" => $token,
|
|
"created_at" => date("Y-m-d H:i:s"),
|
|
"site_number" => $site_number,
|
|
"site_status" => "provisioned",
|
|
"senior_name" => $input['senior_name'] ?? $input['name'] ?? '',
|
|
"senior_nickname" => $input['senior_nickname'] ?? '',
|
|
"senior_photo" => "",
|
|
"latitude" => $input['latitude'] ?? '',
|
|
"longitude" => $input['longitude'] ?? '',
|
|
"emergency_number" => $input['emergency_number'] ?? '15',
|
|
"fcm_tokens" => [],
|
|
"contacts" => $input['contacts'] ?? [],
|
|
"address" => $input['address'] ?? '',
|
|
"city" => $input['city'] ?? '',
|
|
"phone_mobile" => $input['phone_mobile'] ?? '',
|
|
"phone_fixed" => '',
|
|
"age" => $input['age'] ?? '',
|
|
"sex" => $input['sex'] ?? 'F',
|
|
"alerte" => false,
|
|
];
|
|
|
|
// Créer le dossier client
|
|
$client_dir = "clients/" . ($input['name'] ?? $client_id) . "/";
|
|
if (!is_dir($client_dir)) {
|
|
mkdir($client_dir, 0775, true);
|
|
}
|
|
|
|
// Sauvegarder la DB
|
|
file_put_contents($DB_FILE, json_encode($db, JSON_PRETTY_PRINT));
|
|
|
|
// Données de provisioning pour le QR code
|
|
$provision_data = [
|
|
"client_id" => $input['name'] ?? $client_id,
|
|
"token" => $token,
|
|
"site_number" => $site_number,
|
|
"server" => "57.128.74.87",
|
|
];
|
|
|
|
// Écrire la demande de firewall dans une file d'attente
|
|
$fw_queue = "/var/www/lucas/firewall_queue.txt";
|
|
$fw_cmd = "ufw allow " . $base_port . ":" . ($base_port + 9) . "/tcp comment 'Site $site_number - $client_id'";
|
|
file_put_contents($fw_queue, $fw_cmd . "\n", FILE_APPEND);
|
|
|
|
// Réponse
|
|
echo json_encode([
|
|
"success" => true,
|
|
"status" => "provisioned",
|
|
"client_id" => $input['name'] ?? $client_id,
|
|
"token" => $token,
|
|
"site_number" => $site_number,
|
|
"base_port" => $base_port,
|
|
"port_range" => $base_port . "-" . ($base_port + 9),
|
|
"provision_qr_data" => json_encode($provision_data),
|
|
"firewall_queued" => true,
|
|
"message" => "Client provisionné. Ports $base_port-" . ($base_port + 9) . " en attente d'ouverture firewall.",
|
|
], JSON_PRETTY_PRINT);
|
|
?>
|