payfrit-api/api/hub/messages/list.php
Mike 1dacefcf70 Add Hub Messages, Files, Users, Reactions, and Pins APIs
Complete backend for SprintChat Hub migration:
- Messages: send, edit, delete, list (paginated cursor), thread, search
- Files: upload (multipart), download, thumbnail, info, list
- Users: get, getByIds, search, status (online detection)
- Reactions: add, remove, list (grouped by emoji)
- Pins: pin, unpin, list (with message content)
- Channel stats: member/message/pinned/unread counts

4 new DB tables: Hub_Messages, Hub_Files, Hub_Reactions, Hub_PinnedPosts
21 new endpoints added to PUBLIC_ROUTES

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:03:14 +00:00

87 lines
2.6 KiB
PHP

<?php
/**
* GET /api/hub/messages/list.php
*
* Get messages for a channel, paginated, newest first.
*
* Query params:
* ChannelID int REQUIRED
* Limit int optional default 50, max 200
* Before int optional message ID — get messages before this ID (cursor pagination)
* After int optional message ID — get messages after this ID
*
* Response: { OK: true, Messages: [...], HasMore: bool }
*/
require_once __DIR__ . '/../../helpers.php';
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
jsonResponse(['OK' => false, 'ERROR' => 'method_not_allowed'], 405);
}
$channelId = (int) ($_GET['ChannelID'] ?? 0);
$limit = min(max((int) ($_GET['Limit'] ?? 50), 1), 200);
$before = isset($_GET['Before']) ? (int) $_GET['Before'] : null;
$after = isset($_GET['After']) ? (int) $_GET['After'] : null;
if ($channelId <= 0) jsonResponse(['OK' => false, 'ERROR' => 'channel_id_required']);
// Verify channel exists
$channel = queryOne("SELECT ID FROM Hub_Channels WHERE ID = ?", [$channelId]);
if (!$channel) jsonResponse(['OK' => false, 'ERROR' => 'channel_not_found']);
// Build query
$sql = "SELECT * FROM Hub_Messages WHERE ChannelID = ? AND IsDeleted = 0 AND ParentID IS NULL";
$params = [$channelId];
if ($before !== null) {
$sql .= " AND ID < ?";
$params[] = $before;
}
if ($after !== null) {
$sql .= " AND ID > ?";
$params[] = $after;
}
if ($after !== null) {
$sql .= " ORDER BY ID ASC LIMIT ?";
} else {
$sql .= " ORDER BY ID DESC LIMIT ?";
}
$params[] = $limit + 1; // fetch one extra to determine HasMore
$rows = queryTimed($sql, $params);
$hasMore = count($rows) > $limit;
if ($hasMore) array_pop($rows);
// If we used ASC (After), reverse for consistent newest-first output
if ($after !== null) {
$rows = array_reverse($rows);
}
$messages = [];
foreach ($rows as $row) {
// Get reply count for each root message
$replyCount = queryOne(
"SELECT COUNT(*) as cnt FROM Hub_Messages WHERE ParentID = ? AND IsDeleted = 0",
[(int) $row['ID']]
);
$messages[] = [
'ID' => (int) $row['ID'],
'ChannelID' => (int) $row['ChannelID'],
'SenderAddress' => $row['SenderAddress'],
'Content' => $row['Content'],
'ParentID' => null,
'IsEdited' => (bool) $row['IsEdited'],
'ReplyCount' => (int) ($replyCount['cnt'] ?? 0),
'CreatedAt' => toISO8601($row['CreatedAt']),
'UpdatedAt' => toISO8601($row['UpdatedAt']),
];
}
jsonResponse([
'OK' => true,
'Messages' => $messages,
'HasMore' => $hasMore,
]);