Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2cc3d73
feat(conversations): add flagged_messages migration
BillChirico Feb 28, 2026
85d97b2
feat(conversations): add API endpoints for list, detail, search, flag…
BillChirico Feb 28, 2026
62dbfcf
feat(conversations): mount router in API index
BillChirico Feb 28, 2026
2258dbb
feat(conversations): add conversation list and replay dashboard pages
BillChirico Feb 28, 2026
6ee2922
feat(conversations): add Next.js API proxy routes
BillChirico Feb 28, 2026
e81dd0d
test(conversations): add API and grouping tests
BillChirico Feb 28, 2026
01361ae
fix(conversations): escape ILIKE wildcards to prevent wildcard injection
BillChirico Feb 28, 2026
7088ce3
fix(conversations): remove unused _totalChars variable
BillChirico Feb 28, 2026
736debe
fix(conversations): cap list query to 5000 rows to prevent memory exh…
BillChirico Feb 28, 2026
c44382c
fix(conversations): replace in-memory stats grouping with SQL aggregates
BillChirico Feb 28, 2026
6729349
fix(conversations): bound conversation detail query by time window in…
BillChirico Feb 28, 2026
8e78939
style: alphabetize imports and format authorizeGuildAdmin calls
BillChirico Feb 28, 2026
2a74ad7
test(conversations): fix stats mock to match SQL conversation counting
BillChirico Feb 28, 2026
79dd188
test(conversations): add POST flag endpoint to guild validation test
BillChirico Feb 28, 2026
a412911
fix(conversations): parameterize SQL interval and fix flag button a11y
BillChirico Feb 28, 2026
926985f
test: add ILIKE wildcard escape coverage for % and _ characters
BillChirico Feb 28, 2026
f88edc7
fix: deterministic flag status for duplicate flagged_messages rows
BillChirico Feb 28, 2026
cf7583d
refactor: extract escapeIlike utility from logQuery inline impl
BillChirico Feb 28, 2026
02b9999
fix(conversations): use escapeIlike(), fix non-deterministic flag sta…
BillChirico Feb 28, 2026
1c5e8d2
test(conversations): add ILIKE backslash escape test and fix flag moc…
BillChirico Feb 28, 2026
b52e79a
refactor(web): add LOG_PREFIX constant to all 5 conversation proxy ro…
BillChirico Feb 28, 2026
0ec2ca0
fix(ui): use MessagesSquare icon for Conversations sidebar entry (#8)
BillChirico Feb 28, 2026
0f057c3
fix(ui): show last 4 digits of channel snowflake instead of raw ID (#9)
BillChirico Feb 28, 2026
4377722
refactor(ui): extract PAGE_SIZE constant in conversations page (#5)
BillChirico Feb 28, 2026
a7a6689
docs(migration): explain missing FK on conversation_first_id (#10)
BillChirico Feb 28, 2026
6b6dc1e
Merge branch 'main' into feat/ai-conversations
BillChirico Feb 28, 2026
0af8759
fix(lint): suppress pre-existing biome a11y errors in label component
BillChirico Feb 28, 2026
b63d639
fix(conversations): stray $ in JSX channel display, increase query li…
BillChirico Feb 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions migrations/012_flagged_messages.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Migration 012 — Flagged Messages
* Creates the flagged_messages table for tracking problematic AI responses
* within conversation threads.
*
* @see https://github.com/VolvoxLLC/volvox-bot/issues/34
*/

'use strict';

/**
* @param {import('pg').Pool} pool
*/
async function up(pool) {
await pool.query(`
CREATE TABLE IF NOT EXISTS flagged_messages (
id SERIAL PRIMARY KEY,
guild_id TEXT NOT NULL,
conversation_first_id INTEGER NOT NULL,
message_id INTEGER NOT NULL REFERENCES conversations(id),
flagged_by TEXT NOT NULL,
reason TEXT NOT NULL,
notes TEXT,
status TEXT DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'dismissed')),
resolved_by TEXT,
resolved_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
`);

await pool.query(`
CREATE INDEX IF NOT EXISTS idx_flagged_messages_guild
ON flagged_messages(guild_id);
`);

await pool.query(`
CREATE INDEX IF NOT EXISTS idx_flagged_messages_status
ON flagged_messages(guild_id, status);
`);
}

module.exports = { up };
5 changes: 5 additions & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import healthRouter from './routes/health.js';
import membersRouter from './routes/members.js';
import moderationRouter from './routes/moderation.js';
import conversationsRouter from './routes/conversations.js';
import webhooksRouter from './routes/webhooks.js';

const router = Router();
Expand All @@ -28,6 +29,10 @@
// (mounted before guilds to handle /:id/members/* before the basic guilds endpoint)
router.use('/guilds', requireAuth(), membersRouter);

// Conversation routes — require API secret or OAuth2 JWT
// (mounted before guilds to handle /:id/conversations/* before the catch-all guild endpoint)
router.use('/guilds/:id/conversations', requireAuth(), conversationsRouter);

// Guild routes — require API secret or OAuth2 JWT
router.use('/guilds', requireAuth(), guildsRouter);

Expand Down
Loading
Loading