237 lines
7.5 KiB
PHP
237 lines
7.5 KiB
PHP
<?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]);
|