relay: group chat rooms + persistent chat history
Adds named group threads (chat_rooms + chat_room_members), mirroring
the collab-buckets membership pattern. Group messages share the
chat_messages table via a new nullable room_uid column; recipient_id
is left 0 for group rows and fan-out happens via the member-join at
GET time instead of duplicating per recipient.
Chat retention flips from "drop everything after 7 days" to
persistent-by-default: a new `persistent` flag on chat_messages marks
long-term rows (default 1 — all group messages plus non-disposable
1:1). The 7-day cleanup only targets persistent=0 rows so the sender
can still poll final receipt state before they're pruned.
New endpoints:
POST/GET/PATCH/DELETE /api/chat/rooms[/{uid}] — room CRUD
GET /api/chat/backfill — scrollback / cold-start history fetch
POST /api/chat — accepts recipient OR room_uid + disposable flag
GET /api/chat — returns 1:1 to caller AND rooms caller is in
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
618a787b36
commit
044caae447
2 files changed
+294
-21
No files matched your search
+42
-2
@@ -189,6 +189,19 @@ $chatColNames = array_column($chatCols, 'name');
|
||||
if (!in_array('seen', $chatColNames)) {
|
||||
$db->exec('ALTER TABLE chat_messages ADD COLUMN seen INTEGER NOT NULL DEFAULT 0');
|
||||
}
|
||||
// Group chat rooms: room_uid (nullable — set for group messages, NULL for
|
||||
// 1:1). recipient_id stays non-null for 1:1; group rooms resolve recipients
|
||||
// via chat_room_members. `persistent` controls retention: 1 = keep in the
|
||||
// long-term store so multi-device / late-joiners can pull history, 0 = old
|
||||
// "disposable" behaviour (prune after fetch window). 1:1 defaults to 1;
|
||||
// group messages are always 1.
|
||||
if (!in_array('room_uid', $chatColNames)) {
|
||||
$db->exec('ALTER TABLE chat_messages ADD COLUMN room_uid TEXT');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_chat_room ON chat_messages(room_uid, id)');
|
||||
}
|
||||
if (!in_array('persistent', $chatColNames)) {
|
||||
$db->exec('ALTER TABLE chat_messages ADD COLUMN persistent INTEGER NOT NULL DEFAULT 1');
|
||||
}
|
||||
// Sync-notification cleanup: the bot=1 chat messages turned out to clutter
|
||||
// the chat thread. The feature moved to its own `relay_sync_events` queue
|
||||
// (see below). Drop any leftover bot rows so they stop showing up in
|
||||
@@ -199,6 +212,27 @@ if (in_array('bot', $chatColNames)) {
|
||||
}
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_chat_sender_state ON chat_messages(sender_id, id)');
|
||||
|
||||
// Chat rooms: named group threads. Owner + membership mirrors collab
|
||||
// buckets. Group messages live in chat_messages with room_uid set.
|
||||
$db->exec('CREATE TABLE IF NOT EXISTS chat_rooms (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uid TEXT UNIQUE NOT NULL,
|
||||
owner_id INTEGER NOT NULL,
|
||||
owner_username TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (owner_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||
)');
|
||||
$db->exec('CREATE TABLE IF NOT EXISTS chat_room_members (
|
||||
room_uid TEXT NOT NULL,
|
||||
tenant_id INTEGER NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
added_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (room_uid, tenant_id),
|
||||
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||
)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_chat_room_member_tenant ON chat_room_members(tenant_id, room_uid)');
|
||||
|
||||
// Sync-event notifications: replaces the old bot-chat mechanism. When a
|
||||
// client syncs a card they received, they POST here; the sender polls
|
||||
// GET /api/sync-events and surfaces each as a transient toast. Rows are
|
||||
@@ -315,8 +349,14 @@ $db->exec('CREATE TABLE IF NOT EXISTS rate_limits (
|
||||
// Cleanup: 24h ephemeral storage, expired sessions, old rate limits
|
||||
$db->exec("DELETE FROM messages WHERE created_at < datetime('now', '-24 hours')");
|
||||
$db->exec("DELETE FROM attachments WHERE created_at < datetime('now', '-24 hours')");
|
||||
// Chat: 7 days, longer than card sync but still ephemeral
|
||||
$db->exec("DELETE FROM chat_messages WHERE created_at < datetime('now', '-7 days')");
|
||||
// Chat retention:
|
||||
// - persistent = 1 (default, 1:1 non-disposable + all group messages)
|
||||
// → never auto-deleted. Relay is the source of truth so late joiners
|
||||
// and second devices can backfill full history.
|
||||
// - persistent = 0 (disposable 1:1 threads) → drop after a 7-day grace
|
||||
// window so the sender's /api/chat/sent receipt poll still sees the
|
||||
// final state.
|
||||
$db->exec("DELETE FROM chat_messages WHERE persistent = 0 AND created_at < datetime('now', '-7 days')");
|
||||
// Sync events are transient toasts — drop fetched ones after an hour,
|
||||
// everything after 24h regardless (sender was offline too long to care).
|
||||
$db->exec("DELETE FROM relay_sync_events WHERE fetched = 1 AND created_at < datetime('now', '-1 hour')");
|
||||
|
||||
Reference in new issue
Block a user