Init Git Prod Mars 2026
This commit is contained in:
commit
d81efe93b1
362 files changed
+86627
No files matched your search
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../_bootstrap.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$role = $GLOBALS['MESSAGES_CTX']['role']; // 'praticien' ou 'client'
|
||||
$meId = $GLOBALS['MESSAGES_CTX']['me_id']; // id praticien ou client
|
||||
|
||||
if (!hash_equals($_SESSION['_csrf'] ?? '', $_POST['_csrf'] ?? '')) {
|
||||
json_response(['error' => 'csrf'], 419);
|
||||
}
|
||||
|
||||
$convId = (int)($_POST['conversation_id'] ?? 0);
|
||||
$body = isset($_POST['body']) ? trim((string)$_POST['body']) : '';
|
||||
|
||||
if ($convId <= 0) {
|
||||
json_response(['error' => 'bad_params'], 400);
|
||||
}
|
||||
|
||||
// Autorisation + statut
|
||||
$conv = assert_conversation_access($pdo, $convId, $role, $meId);
|
||||
if (($conv['status'] ?? 'open') !== 'open') {
|
||||
json_response(['error' => 'closed'], 403);
|
||||
}
|
||||
|
||||
// Upload (optionnel)
|
||||
$attPath = null;
|
||||
$attName = null;
|
||||
|
||||
if (!empty($_FILES['file']['name'] ?? '')) {
|
||||
$okExt = ['jpg','jpeg','png','pdf','doc','docx'];
|
||||
$name = (string)$_FILES['file']['name'];
|
||||
$size = (int)$_FILES['file']['size'];
|
||||
$tmp = (string)$_FILES['file']['tmp_name'];
|
||||
|
||||
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
|
||||
if (!in_array($ext, $okExt, true)) {
|
||||
json_response(['error' => 'invalid_file_type'], 400);
|
||||
}
|
||||
if ($size > 10 * 1024 * 1024) { // 10 Mo
|
||||
json_response(['error' => 'file_too_large'], 400);
|
||||
}
|
||||
|
||||
$dir = realpath(__DIR__ . '/..') . '/uploads/';
|
||||
if (!is_dir($dir)) { @mkdir($dir, 0775, true); }
|
||||
$safe = $meId . '_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
|
||||
$dest = $dir . $safe;
|
||||
|
||||
if (!move_uploaded_file($tmp, $dest)) {
|
||||
json_response(['error' => 'upload_failed'], 500);
|
||||
}
|
||||
|
||||
// Chemin web relatif (sans leading slash pour constituer une URL /messages/uploads/..)
|
||||
$attPath = 'messages/uploads/' . $safe;
|
||||
$attName = $name;
|
||||
}
|
||||
|
||||
// Flag : doit-on envoyer une notification mail ?
|
||||
$shouldNotify = false;
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Insérer le message
|
||||
$ins = $pdo->prepare("
|
||||
INSERT INTO messages
|
||||
(conversation_id, sender_role, sender_id, body, attachment_path, attachment_name, created_at)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, NOW())
|
||||
");
|
||||
$ins->execute([
|
||||
$convId,
|
||||
$role,
|
||||
$meId,
|
||||
$body !== '' ? $body : null,
|
||||
$attPath,
|
||||
$attName
|
||||
]);
|
||||
$msgId = (int)$pdo->lastInsertId();
|
||||
|
||||
// Mettre à jour les compteurs de non-lus (chez l'autre)
|
||||
if ($role === 'praticien') {
|
||||
// Le praticien écrit → c'est le client qui a des messages non lus (c_unread)
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT c_unread
|
||||
FROM conversation_counters
|
||||
WHERE conversation_id = ?
|
||||
FOR UPDATE
|
||||
");
|
||||
$stmt->execute([$convId]);
|
||||
$prev = $stmt->fetchColumn();
|
||||
$prevUnread = ($prev === false) ? 0 : (int)$prev;
|
||||
|
||||
$pdo->prepare("
|
||||
UPDATE conversation_counters
|
||||
SET c_unread = c_unread + 1
|
||||
WHERE conversation_id = ?
|
||||
")->execute([$convId]);
|
||||
|
||||
if ($prevUnread === 0) {
|
||||
$shouldNotify = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Le client écrit → c'est le praticien qui a des messages non lus (p_unread)
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT p_unread
|
||||
FROM conversation_counters
|
||||
WHERE conversation_id = ?
|
||||
FOR UPDATE
|
||||
");
|
||||
$stmt->execute([$convId]);
|
||||
$prev = $stmt->fetchColumn();
|
||||
$prevUnread = ($prev === false) ? 0 : (int)$prev;
|
||||
|
||||
$pdo->prepare("
|
||||
UPDATE conversation_counters
|
||||
SET p_unread = p_unread + 1
|
||||
WHERE conversation_id = ?
|
||||
")->execute([$convId]);
|
||||
|
||||
if ($prevUnread === 0) {
|
||||
$shouldNotify = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Bump updated_at
|
||||
$pdo->prepare("
|
||||
UPDATE conversations
|
||||
SET updated_at = NOW()
|
||||
WHERE id = ?
|
||||
")->execute([$convId]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) { $pdo->rollBack(); }
|
||||
error_log('send_message error: '.$e->getMessage());
|
||||
json_response(['error' => 'server_error'], 500);
|
||||
}
|
||||
|
||||
// 🔔 Notification par e-mail (ne doit jamais casser la réponse JSON)
|
||||
if ($shouldNotify) {
|
||||
try {
|
||||
require_once __DIR__ . '/../../public/message_par_mail.php';
|
||||
notifier_nouveau_message_par_email($pdo, $convId, $role);
|
||||
} catch (Throwable $e) {
|
||||
error_log('Notif mail error: '.$e->getMessage());
|
||||
}
|
||||
try {
|
||||
// Déterminer le destinataire (l'autre)
|
||||
$targetRole = ($role === 'praticien') ? 'client' : 'praticien';
|
||||
|
||||
// Normalement assert_conversation_access() te renvoie ces champs,
|
||||
// sinon fallback query (au cas où)
|
||||
$clientId = (int)($conv['client_id'] ?? 0);
|
||||
$praticienId = (int)($conv['praticien_id'] ?? 0);
|
||||
|
||||
if ($clientId <= 0 || $praticienId <= 0) {
|
||||
$st = $pdo->prepare("SELECT client_id, praticien_id FROM conversations WHERE id=? LIMIT 1");
|
||||
$st->execute([$convId]);
|
||||
$row = $st->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
$clientId = (int)($row['client_id'] ?? 0);
|
||||
$praticienId = (int)($row['praticien_id'] ?? 0);
|
||||
}
|
||||
|
||||
$targetRef = ($targetRole === 'client') ? $clientId : $praticienId;
|
||||
|
||||
// ✅ Trouver le compte_id du destinataire (obligatoire pour worker + badge global)
|
||||
$stC = $pdo->prepare("
|
||||
SELECT id
|
||||
FROM compte
|
||||
WHERE role = :role AND reference_id = :ref
|
||||
LIMIT 1
|
||||
");
|
||||
$stC->execute([
|
||||
':role' => $targetRole,
|
||||
':ref' => $targetRef,
|
||||
]);
|
||||
$targetCompteId = (int)($stC->fetchColumn() ?: 0);
|
||||
|
||||
|
||||
if ($targetRef > 0) {
|
||||
// Texte push (simple)
|
||||
$title = 'Новое сообщение c uslugi.fr';
|
||||
$text = 'У вас новое сообщение с uslugi.fr.';
|
||||
if ($body !== '') {
|
||||
$snippet = mb_substr($body, 0, 120);
|
||||
$text = $snippet;
|
||||
}
|
||||
|
||||
$url = ($targetRole === 'client')
|
||||
? '/messages/?conv=' . $convId
|
||||
: '/messages/?conv=' . $convId;
|
||||
|
||||
$data = json_encode([
|
||||
'message_id' => $msgId,
|
||||
'sender_role' => $role,
|
||||
'conversation_id' => $convId,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
if ($targetCompteId > 0) {
|
||||
$pdo->prepare("
|
||||
INSERT INTO push_badge_counters (compte_id, msg_unread, rdv_unread)
|
||||
VALUES (:cid, 1, 0)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
msg_unread = LEAST(msg_unread + 1, 99),
|
||||
updated_at = NOW()
|
||||
")->execute([':cid' => $targetCompteId]);
|
||||
}
|
||||
|
||||
$insPush = $pdo->prepare("
|
||||
INSERT INTO push_notifications
|
||||
(target_role, target_reference_id, target_compte_id, type, title, body, url, data_json, status, attempts, created_at)
|
||||
VALUES
|
||||
(:role, :ref, :cid, 'message_new', :title, :body, :url, :data, 'pending', 0, NOW())
|
||||
");
|
||||
$insPush->execute([
|
||||
':role' => $targetRole,
|
||||
':ref' => $targetRef,
|
||||
':cid' => ($targetCompteId > 0 ? $targetCompteId : null),
|
||||
':title' => $title,
|
||||
':body' => $text,
|
||||
':url' => $url,
|
||||
':data' => $data,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
} catch (Throwable $e) {
|
||||
error_log('Notif push error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
json_response(['ok' => true, 'message_id' => $msgId]);
|
||||
Reference in new issue
Block a user