645 lines
29 KiB
PHP
645 lines
29 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
|
||
require_once __DIR__ . '/admin_bootstrap.php';
|
||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/file_api_client.php';
|
||
|
||
$pdo = admin_get_pdo();
|
||
$auth = admin_require_auth($pdo);
|
||
|
||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||
|
||
function admin_chat_select_sql(): string
|
||
{
|
||
return 'SELECT '
|
||
. 'm.id, m.user_id, m.username, m.message, m.created_at, m.updated_at, UNIX_TIMESTAMP(m.updated_at) AS updated_at_ts, '
|
||
. 'm.reply_to_id, '
|
||
. 'm.is_hearted, m.hearted_by_user_id, m.hearted_by_username, m.hearted_at, '
|
||
. '(m.file_name IS NOT NULL AND m.file_name <> \'\') AS has_file, m.file_name, m.file_mime, m.file_size, '
|
||
. 'r.username AS reply_username, r.message AS reply_message, r.created_at AS reply_created_at '
|
||
. 'FROM admin_chat_messages m '
|
||
. 'LEFT JOIN admin_chat_messages r ON r.id = m.reply_to_id ';
|
||
}
|
||
|
||
function admin_chat_normalize_row(array $row): array
|
||
{
|
||
$row['id'] = isset($row['id']) ? (int)$row['id'] : 0;
|
||
$row['user_id'] = isset($row['user_id']) ? (int)$row['user_id'] : 0;
|
||
$row['has_file'] = (bool)((int)($row['has_file'] ?? 0));
|
||
$row['reply_to_id'] = isset($row['reply_to_id']) ? (int)$row['reply_to_id'] : null;
|
||
$row['file_size'] = isset($row['file_size']) && $row['file_size'] !== null ? (int)$row['file_size'] : null;
|
||
$row['updated_at_ts'] = isset($row['updated_at_ts']) && $row['updated_at_ts'] !== null ? (int)$row['updated_at_ts'] : null;
|
||
$row['is_hearted'] = (bool)((int)($row['is_hearted'] ?? 0));
|
||
$row['hearted_by_user_id'] = isset($row['hearted_by_user_id']) && $row['hearted_by_user_id'] !== null ? (int)$row['hearted_by_user_id'] : null;
|
||
$row['hearted_by_username'] = isset($row['hearted_by_username']) && $row['hearted_by_username'] !== null ? (string)$row['hearted_by_username'] : null;
|
||
|
||
return $row;
|
||
}
|
||
|
||
function admin_chat_normalize_rows(array $rows): array
|
||
{
|
||
foreach ($rows as $k => $r) {
|
||
if (is_array($r)) {
|
||
$rows[$k] = admin_chat_normalize_row($r);
|
||
}
|
||
}
|
||
return $rows;
|
||
}
|
||
|
||
if ($method === 'GET') {
|
||
$beforeId = isset($_GET['before_id']) ? (int)$_GET['before_id'] : 0;
|
||
$afterId = isset($_GET['after_id']) ? (int)$_GET['after_id'] : 0;
|
||
$updatedAfter = isset($_GET['updated_after']) ? (int)$_GET['updated_after'] : 0;
|
||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 100;
|
||
$limit = max(1, min(200, $limit));
|
||
|
||
try {
|
||
if ($afterId > 0) {
|
||
// Nowe wiadomości (do dopisywania na dole)
|
||
$stmt = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.id > :after_id '
|
||
. 'ORDER BY m.id ASC '
|
||
. 'LIMIT :limit'
|
||
);
|
||
$stmt->bindValue(':after_id', $afterId, PDO::PARAM_INT);
|
||
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
||
$stmt->execute();
|
||
$rows = admin_chat_normalize_rows($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||
|
||
admin_json_response([
|
||
'success' => true,
|
||
'data' => $rows,
|
||
'count' => count($rows),
|
||
]);
|
||
}
|
||
|
||
if ($updatedAfter > 0) {
|
||
// Zmienione (edytowane) wiadomości do odświeżenia w UI
|
||
$stmt = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.updated_at IS NOT NULL AND m.updated_at > FROM_UNIXTIME(:updated_after) '
|
||
. 'ORDER BY m.id ASC '
|
||
. 'LIMIT :limit'
|
||
);
|
||
$stmt->bindValue(':updated_after', $updatedAfter, PDO::PARAM_INT);
|
||
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
||
$stmt->execute();
|
||
$rows = admin_chat_normalize_rows($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||
|
||
admin_json_response([
|
||
'success' => true,
|
||
'data' => $rows,
|
||
'count' => count($rows),
|
||
]);
|
||
}
|
||
|
||
if ($beforeId > 0) {
|
||
// Starsze wiadomości (do wczytywania przy scrollu w górę)
|
||
$stmt = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.id < :before_id '
|
||
. 'ORDER BY m.id DESC '
|
||
. 'LIMIT :limit'
|
||
);
|
||
$stmt->bindValue(':before_id', $beforeId, PDO::PARAM_INT);
|
||
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
||
$stmt->execute();
|
||
$rowsDesc = admin_chat_normalize_rows($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||
|
||
admin_json_response([
|
||
'success' => true,
|
||
'data' => $rowsDesc,
|
||
'count' => count($rowsDesc),
|
||
'hasMore' => count($rowsDesc) === $limit,
|
||
]);
|
||
}
|
||
|
||
// Początkowe wczytanie: 100 najnowszych
|
||
$stmt = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'ORDER BY m.id DESC '
|
||
. 'LIMIT :limit'
|
||
);
|
||
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
||
$stmt->execute();
|
||
$rowsDesc = admin_chat_normalize_rows($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||
|
||
admin_json_response([
|
||
'success' => true,
|
||
'data' => $rowsDesc,
|
||
'count' => count($rowsDesc),
|
||
'hasMore' => count($rowsDesc) === $limit,
|
||
]);
|
||
} catch (Throwable $e) {
|
||
$msg = (string)$e->getMessage();
|
||
if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
|
||
admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
|
||
}
|
||
admin_json_error('Błąd pobierania wiadomości', 500);
|
||
}
|
||
}
|
||
|
||
if ($method === 'POST') {
|
||
$contentType = (string)($_SERVER['CONTENT_TYPE'] ?? '');
|
||
$isJson = stripos($contentType, 'application/json') !== false;
|
||
|
||
$RECALLED_TEXT = 'Wiadomość cofnięta';
|
||
|
||
$message = '';
|
||
$replyToId = null;
|
||
$fileName = null;
|
||
$fileMime = null;
|
||
$fileSize = null;
|
||
$fileData = null; // fallback BLOB (do usunięcia po pełnej migracji)
|
||
$filePath = null; // nowa ścieżka dyskowa
|
||
|
||
if ($isJson) {
|
||
$payload = admin_read_json_body();
|
||
$action = isset($payload['action']) ? (string)$payload['action'] : '';
|
||
|
||
// Recall existing message (revoke own)
|
||
if ($action === 'recall') {
|
||
$id = isset($payload['id']) ? (int)$payload['id'] : 0;
|
||
if ($id <= 0) {
|
||
admin_json_error('Nieprawidłowe id', 422);
|
||
}
|
||
|
||
try {
|
||
$stmt = $pdo->prepare('SELECT id, user_id FROM admin_chat_messages WHERE id = :id');
|
||
$stmt->execute([':id' => $id]);
|
||
$cur = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
if (!$cur) {
|
||
admin_json_error('Nie znaleziono wiadomości', 404);
|
||
}
|
||
if ((int)$cur['user_id'] !== (int)$auth['user_id']) {
|
||
admin_json_error('Nie możesz cofnąć cudzej wiadomości', 403);
|
||
}
|
||
|
||
$up = $pdo->prepare(
|
||
'UPDATE admin_chat_messages '
|
||
. 'SET message = :message, file_name = NULL, file_mime = NULL, file_size = NULL, file_path = NULL, updated_at = NOW() '
|
||
. 'WHERE id = :id'
|
||
);
|
||
$up->execute([':message' => $RECALLED_TEXT, ':id' => $id]);
|
||
|
||
$select = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.id = :id LIMIT 1'
|
||
);
|
||
$select->execute([':id' => $id]);
|
||
$row = $select->fetch(PDO::FETCH_ASSOC);
|
||
if (is_array($row)) {
|
||
$row = admin_chat_normalize_row($row);
|
||
}
|
||
|
||
admin_json_response(['success' => true, 'data' => $row]);
|
||
} catch (Throwable $e) {
|
||
$msg = (string)$e->getMessage();
|
||
if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
|
||
admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
|
||
}
|
||
admin_json_error('Błąd cofania wiadomości', 500);
|
||
}
|
||
}
|
||
|
||
// Update existing message (edit own)
|
||
if ($action === 'update') {
|
||
$id = isset($payload['id']) ? (int)$payload['id'] : 0;
|
||
$newMessage = isset($payload['message']) ? trim((string)$payload['message']) : '';
|
||
|
||
if ($id <= 0) {
|
||
admin_json_error('Nieprawidłowe id', 422);
|
||
}
|
||
if ($newMessage === '') {
|
||
admin_json_error('Wiadomość nie może być pusta', 422);
|
||
}
|
||
if (mb_strlen($newMessage) > 1500) {
|
||
admin_json_error('Wiadomość jest zbyt długa (max 1500 znaków)', 422);
|
||
}
|
||
|
||
try {
|
||
$stmt = $pdo->prepare('SELECT id, user_id, message FROM admin_chat_messages WHERE id = :id');
|
||
$stmt->execute([':id' => $id]);
|
||
$cur = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
if (!$cur) {
|
||
admin_json_error('Nie znaleziono wiadomości', 404);
|
||
}
|
||
if ((int)$cur['user_id'] !== (int)$auth['user_id']) {
|
||
admin_json_error('Nie możesz edytować cudzej wiadomości', 403);
|
||
}
|
||
if (((string)($cur['message'] ?? '')) === $RECALLED_TEXT) {
|
||
admin_json_error('Nie możesz edytować cofniętej wiadomości', 422);
|
||
}
|
||
|
||
$up = $pdo->prepare('UPDATE admin_chat_messages SET message = :message, updated_at = NOW() WHERE id = :id');
|
||
$up->execute([':message' => $newMessage, ':id' => $id]);
|
||
|
||
$select = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.id = :id LIMIT 1'
|
||
);
|
||
$select->execute([':id' => $id]);
|
||
$row = $select->fetch(PDO::FETCH_ASSOC);
|
||
if (is_array($row)) {
|
||
$row = admin_chat_normalize_row($row);
|
||
}
|
||
|
||
admin_json_response(['success' => true, 'data' => $row]);
|
||
} catch (Throwable $e) {
|
||
$msg = (string)$e->getMessage();
|
||
if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
|
||
admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
|
||
}
|
||
admin_json_error('Błąd edycji wiadomości', 500);
|
||
}
|
||
}
|
||
|
||
if ($action === 'toggle_heart') {
|
||
$id = isset($payload['id']) ? (int)$payload['id'] : 0;
|
||
if ($id <= 0) {
|
||
admin_json_error('Nieprawidłowe id', 422);
|
||
}
|
||
|
||
try {
|
||
$stmt = $pdo->prepare('SELECT id, is_hearted FROM admin_chat_messages WHERE id = :id');
|
||
$stmt->execute([':id' => $id]);
|
||
$cur = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
if (!$cur) {
|
||
admin_json_error('Nie znaleziono wiadomości', 404);
|
||
}
|
||
|
||
$isHearted = (int)($cur['is_hearted'] ?? 0) === 1;
|
||
if ($isHearted) {
|
||
$up = $pdo->prepare(
|
||
'UPDATE admin_chat_messages '
|
||
. 'SET is_hearted = 0, hearted_by_user_id = NULL, hearted_by_username = NULL, hearted_at = NULL, updated_at = NOW() '
|
||
. 'WHERE id = :id'
|
||
);
|
||
$up->execute([':id' => $id]);
|
||
} else {
|
||
$up = $pdo->prepare(
|
||
'UPDATE admin_chat_messages '
|
||
. 'SET is_hearted = 1, hearted_by_user_id = :hearted_by_user_id, hearted_by_username = :hearted_by_username, hearted_at = NOW(), updated_at = NOW() '
|
||
. 'WHERE id = :id'
|
||
);
|
||
$up->execute([
|
||
':id' => $id,
|
||
':hearted_by_user_id' => (int)$auth['user_id'],
|
||
':hearted_by_username' => (string)$auth['username'],
|
||
]);
|
||
}
|
||
|
||
$select = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.id = :id LIMIT 1'
|
||
);
|
||
$select->execute([':id' => $id]);
|
||
$row = $select->fetch(PDO::FETCH_ASSOC);
|
||
if (is_array($row)) {
|
||
$row = admin_chat_normalize_row($row);
|
||
}
|
||
|
||
admin_json_response(['success' => true, 'data' => $row]);
|
||
} catch (Throwable $e) {
|
||
$msg = (string)$e->getMessage();
|
||
if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
|
||
admin_json_error('Brak wymaganych kolumn/tabel (np. is_hearted). Uruchom /administration/install_notes_chat.php', 500);
|
||
}
|
||
admin_json_error('Błąd zmiany serduszka', 500);
|
||
}
|
||
}
|
||
|
||
$message = isset($payload['message']) ? trim((string)$payload['message']) : '';
|
||
$replyToId = isset($payload['reply_to_id']) ? (int)$payload['reply_to_id'] : null;
|
||
} else {
|
||
$action = isset($_POST['action']) ? (string)$_POST['action'] : '';
|
||
$message = isset($_POST['message']) ? trim((string)$_POST['message']) : '';
|
||
$replyToId = isset($_POST['reply_to_id']) && $_POST['reply_to_id'] !== '' ? (int)$_POST['reply_to_id'] : null;
|
||
$clearFile = isset($_POST['clear_file']) ? (bool)((int)$_POST['clear_file']) : false;
|
||
|
||
// Recall existing message (revoke own)
|
||
if ($action === 'recall') {
|
||
$id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
|
||
if ($id <= 0) {
|
||
admin_json_error('Nieprawidłowe id', 422);
|
||
}
|
||
|
||
try {
|
||
$stmt = $pdo->prepare('SELECT id, user_id FROM admin_chat_messages WHERE id = :id');
|
||
$stmt->execute([':id' => $id]);
|
||
$cur = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
if (!$cur) {
|
||
admin_json_error('Nie znaleziono wiadomości', 404);
|
||
}
|
||
if ((int)$cur['user_id'] !== (int)$auth['user_id']) {
|
||
admin_json_error('Nie możesz cofnąć cudzej wiadomości', 403);
|
||
}
|
||
|
||
// Usuń plik z dysku jeśli istnieje
|
||
try {
|
||
$recallFile = $pdo->prepare('SELECT file_path FROM admin_chat_messages WHERE id = :id LIMIT 1');
|
||
$recallFile->execute([':id' => $id]);
|
||
$recallRow = $recallFile->fetch(PDO::FETCH_ASSOC);
|
||
if (!empty($recallRow['file_path'])) {
|
||
$rSub = dirname((string)$recallRow['file_path']);
|
||
$rName = basename((string)$recallRow['file_path']);
|
||
get_file_api_client()->deleteFile($rSub, $rName);
|
||
}
|
||
} catch (Throwable $ignored) { /* nie blokuj cofania przy błędzie serwisu */ }
|
||
|
||
$up = $pdo->prepare(
|
||
'UPDATE admin_chat_messages '
|
||
. 'SET message = :message, file_name = NULL, file_mime = NULL, file_size = NULL, file_path = NULL, updated_at = NOW() '
|
||
. 'WHERE id = :id'
|
||
);
|
||
$up->execute([':message' => $RECALLED_TEXT, ':id' => $id]);
|
||
|
||
$select = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.id = :id LIMIT 1'
|
||
);
|
||
$select->execute([':id' => $id]);
|
||
$row = $select->fetch(PDO::FETCH_ASSOC);
|
||
if (is_array($row)) {
|
||
$row = admin_chat_normalize_row($row);
|
||
}
|
||
|
||
admin_json_response(['success' => true, 'data' => $row]);
|
||
} catch (Throwable $e) {
|
||
$msg = (string)$e->getMessage();
|
||
if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
|
||
admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
|
||
}
|
||
admin_json_error('Błąd cofania wiadomości', 500);
|
||
}
|
||
}
|
||
|
||
if (!empty($_FILES['file']) && is_array($_FILES['file'])) {
|
||
$upload = $_FILES['file'];
|
||
if (($upload['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE) {
|
||
if (($upload['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) {
|
||
admin_json_error('Błąd uploadu pliku (kod: ' . (int)$upload['error'] . ')', 422);
|
||
}
|
||
if (empty($upload['tmp_name']) || !is_uploaded_file($upload['tmp_name'])) {
|
||
admin_json_error('Nieprawidłowy upload pliku', 422);
|
||
}
|
||
|
||
$fileSize = isset($upload['size']) ? (int)$upload['size'] : null;
|
||
if ($fileSize !== null && $fileSize > 5 * 1024 * 1024) {
|
||
admin_json_error('Plik jest za duży (max 5MB)', 422);
|
||
}
|
||
|
||
$fileName = (string)($upload['name'] ?? 'plik');
|
||
|
||
// Rozpoznaj MIME bezpieczniej niż "type" z przeglądarki
|
||
$detectedMime = null;
|
||
if (function_exists('finfo_open')) {
|
||
$fi = finfo_open(FILEINFO_MIME_TYPE);
|
||
if ($fi) {
|
||
$detectedMime = finfo_file($fi, $upload['tmp_name']);
|
||
finfo_close($fi);
|
||
}
|
||
}
|
||
$fileMime = (string)($detectedMime ?: ($upload['type'] ?? 'application/octet-stream'));
|
||
|
||
$allowed = false;
|
||
if (stripos($fileMime, 'image/') === 0) {
|
||
$allowed = true;
|
||
}
|
||
if (in_array($fileMime, ['application/pdf', 'text/plain'], true)) {
|
||
$allowed = true;
|
||
}
|
||
if (!$allowed) {
|
||
admin_json_error('Niedozwolony typ pliku: ' . $fileMime, 422);
|
||
}
|
||
|
||
// Wyślij plik do serwisu plików – zapisze na dysku
|
||
try {
|
||
$fileApiResult = get_file_api_client()->upload(
|
||
'admin_chat',
|
||
$upload['tmp_name'],
|
||
$fileName,
|
||
$fileMime
|
||
);
|
||
$filePath = (string)($fileApiResult['path'] ?? '');
|
||
if ($filePath === '') {
|
||
admin_json_error('Serwis plików nie zwrócił ścieżki', 500);
|
||
}
|
||
} catch (RuntimeException $e) {
|
||
$status = (int)$e->getCode();
|
||
if ($status < 400 || $status > 599) {
|
||
$status = 500;
|
||
}
|
||
admin_json_error('Błąd zapisu pliku: ' . $e->getMessage(), $status);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Update existing message (edit own) with optional file changes
|
||
if ($action === 'update') {
|
||
$id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
|
||
$newMessage = $message;
|
||
|
||
if ($id <= 0) {
|
||
admin_json_error('Nieprawidłowe id', 422);
|
||
}
|
||
if ($newMessage === '') {
|
||
admin_json_error('Wiadomość nie może być pusta', 422);
|
||
}
|
||
if (mb_strlen($newMessage) > 1500) {
|
||
admin_json_error('Wiadomość jest zbyt długa (max 1500 znaków)', 422);
|
||
}
|
||
|
||
try {
|
||
$stmt = $pdo->prepare('SELECT id, user_id, message FROM admin_chat_messages WHERE id = :id');
|
||
$stmt->execute([':id' => $id]);
|
||
$cur = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
if (!$cur) {
|
||
admin_json_error('Nie znaleziono wiadomości', 404);
|
||
}
|
||
if ((int)$cur['user_id'] !== (int)$auth['user_id']) {
|
||
admin_json_error('Nie możesz edytować cudzej wiadomości', 403);
|
||
}
|
||
if (((string)($cur['message'] ?? '')) === $RECALLED_TEXT) {
|
||
admin_json_error('Nie możesz edytować cofniętej wiadomości', 422);
|
||
}
|
||
|
||
$fields = ['message = :message', 'updated_at = NOW()'];
|
||
$params = [':message' => $newMessage, ':id' => $id];
|
||
|
||
if ($clearFile) {
|
||
// Usuń stary plik z dysku jeśli istnieje
|
||
try {
|
||
$clearFileRow = $pdo->prepare('SELECT file_path FROM admin_chat_messages WHERE id = :id LIMIT 1');
|
||
$clearFileRow->execute([':id' => $id]);
|
||
$cfr = $clearFileRow->fetch(PDO::FETCH_ASSOC);
|
||
if (!empty($cfr['file_path'])) {
|
||
get_file_api_client()->deleteFile(
|
||
dirname((string)$cfr['file_path']),
|
||
basename((string)$cfr['file_path'])
|
||
);
|
||
}
|
||
} catch (Throwable $ignored) { /* nie blokuj aktualizacji */ }
|
||
|
||
$fields[] = 'file_name = NULL';
|
||
$fields[] = 'file_mime = NULL';
|
||
$fields[] = 'file_size = NULL';
|
||
$fields[] = 'file_path = NULL';
|
||
} elseif ($filePath !== null) {
|
||
$fields[] = 'file_name = :file_name';
|
||
$fields[] = 'file_mime = :file_mime';
|
||
$fields[] = 'file_size = :file_size';
|
||
$fields[] = 'file_path = :file_path';
|
||
$params[':file_name'] = $fileName;
|
||
$params[':file_mime'] = $fileMime;
|
||
$params[':file_size'] = $fileSize;
|
||
$params[':file_path'] = $filePath;
|
||
}
|
||
|
||
$sql = 'UPDATE admin_chat_messages SET ' . implode(', ', $fields) . ' WHERE id = :id';
|
||
$up = $pdo->prepare($sql);
|
||
$up->bindValue(':message', $params[':message'], PDO::PARAM_STR);
|
||
$up->bindValue(':id', (int)$params[':id'], PDO::PARAM_INT);
|
||
|
||
if (array_key_exists(':file_name', $params)) {
|
||
$up->bindValue(':file_name', (string)$params[':file_name'], PDO::PARAM_STR);
|
||
$up->bindValue(':file_mime', (string)$params[':file_mime'], PDO::PARAM_STR);
|
||
$up->bindValue(':file_size', $params[':file_size'] !== null ? (int)$params[':file_size'] : null, $params[':file_size'] !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
|
||
$up->bindValue(':file_path', (string)$params[':file_path'], PDO::PARAM_STR);
|
||
}
|
||
|
||
$up->execute();
|
||
|
||
$select = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.id = :id LIMIT 1'
|
||
);
|
||
$select->execute([':id' => $id]);
|
||
$row = $select->fetch(PDO::FETCH_ASSOC);
|
||
if (is_array($row)) {
|
||
$row = admin_chat_normalize_row($row);
|
||
}
|
||
|
||
admin_json_response(['success' => true, 'data' => $row]);
|
||
} catch (Throwable $e) {
|
||
$msg = (string)$e->getMessage();
|
||
if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
|
||
admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
|
||
}
|
||
admin_json_error('Błąd edycji wiadomości', 500);
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($replyToId !== null && $replyToId <= 0) {
|
||
$replyToId = null;
|
||
}
|
||
|
||
if ($message === '' && $filePath === null) {
|
||
admin_json_error('Wiadomość lub plik są wymagane', 422);
|
||
}
|
||
|
||
if ($message !== '' && mb_strlen($message) > 1500) {
|
||
admin_json_error('Wiadomość jest zbyt długa (max 1500 znaków)', 422);
|
||
}
|
||
|
||
try {
|
||
// Server-side dedupe: jeśli user kliknie "Wyślij" kilka razy, nie duplikuj tego samego wpisu
|
||
$dedupeStmt = $pdo->prepare(
|
||
'SELECT id FROM admin_chat_messages '
|
||
. 'WHERE user_id = :uid '
|
||
. 'AND (message <=> :message) '
|
||
. 'AND (reply_to_id <=> :reply_to_id) '
|
||
. 'AND (file_name <=> :file_name) '
|
||
. 'AND (file_size <=> :file_size) '
|
||
. 'AND created_at >= (NOW() - INTERVAL 3 SECOND) '
|
||
. 'ORDER BY id DESC LIMIT 1'
|
||
);
|
||
$dedupeStmt->bindValue(':uid', (int)$auth['user_id'], PDO::PARAM_INT);
|
||
// Keep compatibility with schemas where message is NOT NULL: use empty string instead of NULL.
|
||
$dedupeStmt->bindValue(':message', $message, PDO::PARAM_STR);
|
||
$dedupeStmt->bindValue(':reply_to_id', $replyToId, $replyToId !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
|
||
$dedupeStmt->bindValue(':file_name', $fileName, $fileName !== null ? PDO::PARAM_STR : PDO::PARAM_NULL);
|
||
$dedupeStmt->bindValue(':file_size', $fileSize, $fileSize !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
|
||
$dedupeStmt->execute();
|
||
$existingId = (int)($dedupeStmt->fetchColumn() ?: 0);
|
||
|
||
if ($existingId > 0) {
|
||
$select = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.id = :id LIMIT 1'
|
||
);
|
||
$select->execute([':id' => $existingId]);
|
||
$row = $select->fetch(PDO::FETCH_ASSOC);
|
||
if (is_array($row)) {
|
||
$row = admin_chat_normalize_row($row);
|
||
}
|
||
|
||
admin_json_response([
|
||
'success' => true,
|
||
'deduped' => true,
|
||
'data' => $row,
|
||
]);
|
||
}
|
||
|
||
$stmt = $pdo->prepare(
|
||
'INSERT INTO admin_chat_messages (user_id, username, message, reply_to_id, file_name, file_mime, file_size, file_path) '
|
||
. 'VALUES (:user_id, :username, :message, :reply_to_id, :file_name, :file_mime, :file_size, :file_path)'
|
||
);
|
||
|
||
$stmt->bindValue(':user_id', (int)$auth['user_id'], PDO::PARAM_INT);
|
||
$stmt->bindValue(':username', (string)$auth['username'], PDO::PARAM_STR);
|
||
// Keep compatibility with schemas where message is NOT NULL: use empty string instead of NULL.
|
||
$stmt->bindValue(':message', $message, PDO::PARAM_STR);
|
||
$stmt->bindValue(':reply_to_id', $replyToId, $replyToId !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
|
||
$stmt->bindValue(':file_name', $fileName, $fileName !== null ? PDO::PARAM_STR : PDO::PARAM_NULL);
|
||
$stmt->bindValue(':file_mime', $fileMime, $fileMime !== null ? PDO::PARAM_STR : PDO::PARAM_NULL);
|
||
$stmt->bindValue(':file_size', $fileSize, $fileSize !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
|
||
$stmt->bindValue(':file_path', $filePath, $filePath !== null ? PDO::PARAM_STR : PDO::PARAM_NULL);
|
||
|
||
$stmt->execute();
|
||
|
||
$id = (int)$pdo->lastInsertId();
|
||
$select = $pdo->prepare(
|
||
admin_chat_select_sql()
|
||
. 'WHERE m.id = :id LIMIT 1'
|
||
);
|
||
$select->execute([':id' => $id]);
|
||
$row = $select->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if (is_array($row)) {
|
||
$row = admin_chat_normalize_row($row);
|
||
}
|
||
|
||
admin_json_response([
|
||
'success' => true,
|
||
'data' => $row ?: [
|
||
'id' => $id,
|
||
'user_id' => (int)$auth['user_id'],
|
||
'username' => (string)$auth['username'],
|
||
'message' => $message,
|
||
'created_at' => date('Y-m-d H:i:s'),
|
||
],
|
||
], 201);
|
||
} catch (PDOException $e) {
|
||
$msg = $e->getMessage();
|
||
if (stripos($msg, 'Unknown column') !== false || stripos($msg, 'doesn\'t exist') !== false) {
|
||
admin_json_error('Brak wymaganych kolumn/tabel. Uruchom /administration/install_notes_chat.php', 500);
|
||
}
|
||
if (stripos($msg, 'max_allowed_packet') !== false || stripos($msg, 'packet') !== false || stripos($msg, 'server has gone away') !== false) {
|
||
admin_json_error('Błąd zapisu wiadomości: prawdopodobnie limit max_allowed_packet w MySQL. Zmniejsz plik lub zwiększ max_allowed_packet na serwerze.', 500);
|
||
}
|
||
$compact = preg_replace('/\s+/', ' ', (string)$msg);
|
||
$compact = mb_substr($compact, 0, 240);
|
||
if ($fileData !== null) {
|
||
admin_json_error('Błąd zapisu wiadomości (DB): ' . $compact, 500);
|
||
}
|
||
admin_json_error('Błąd zapisu wiadomości', 500);
|
||
} catch (Throwable $e) {
|
||
admin_json_error('Błąd zapisu wiadomości', 500);
|
||
}
|
||
}
|
||
|
||
admin_json_error('Metoda niedozwolona', 405);
|