togethere.cloud/public_html/api/admin_chat_messages.php

645 lines
29 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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);