Files
GMW/services/backend/src/modules/analysis/analysis.repository.ts
T
MythEclipseandClaude Opus 4.8 07032ab521 refactor: atomic, DRY, and logging improvements across codebase
- Split llmModerationClient.ts (2170 lines) into 5 focused sub-modules
- Split aiAnalyzer.ts (1282 lines) into 4 modular pipelines
- Split messages.db.ts (826 lines) into 5 domain-specific modules
- Moved shared schema to @bete/shared, eliminated backend duplication
- Added createChildLogger to all voice-recording and AI moderation modules
- Extracted tryCommandThenFallback, normalizeMediaState, DEFAULT_VOICE_STATUS
- Created shared pagination.ts utility, eliminated 5+ cursor-pagination duplications
- Created shared messageMapper.ts for row mapping
- Standardized backend error handling with asyncHandler
- Added frontend createLogger utility and useAsyncAction hook
- Added structured logging to frontend hooks, socket, and API client

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 19:46:08 +07:00

66 lines
1.8 KiB
TypeScript

import { createChildLogger } from "@bete/shared/logger";
import { getPool } from "../../shared/database/index.js";
import {
type MappedMessage,
mapMessageRow,
} from "../../shared/utils/messageMapper.js";
const logger = createChildLogger("analysis.repository");
export interface AnalysisSearchQuery {
q?: string;
channelId?: string;
guildId?: string;
limit?: number;
}
// AnalysisSearchResult is identical to MappedMessage — reuse the shared mapper
export type AnalysisSearchResult = MappedMessage;
export class AnalysisRepository {
async search(query: AnalysisSearchQuery): Promise<AnalysisSearchResult[]> {
const pool = getPool();
const { q = "", channelId, guildId, limit = 20 } = query;
logger.debug({ q, channelId, guildId, limit }, "Searching analysis");
const searchPattern = `%${q}%`;
const clauses: string[] = ["content ILIKE $1"];
const params: (string | number)[] = [searchPattern];
let p = 2;
if (guildId) {
clauses.push(`guild_id = $${p}`);
params.push(guildId);
p++;
}
if (channelId) {
clauses.push(`channel_id = $${p}`);
params.push(channelId);
p++;
}
const where = clauses.join(" AND ");
const { rows } = await pool.query(
`SELECT
id, guild_id, channel_id, thread_id,
user_id, username, avatar_url,
content, edited_content, created_at, edited_at, deleted_at,
type, metadata,
ai_status, ai_moderation_flags, ai_moderation_score,
ai_analysis, ai_categories, ai_severity, ai_confidence,
ai_recommended_action, ai_analyzed_at, ai_error
FROM messages
WHERE ${where}
ORDER BY created_at DESC
LIMIT $${p}`,
[...params, limit],
);
return rows.map((r) => mapMessageRow(r as Record<string, unknown>));
}
}
export const analysisRepository = new AnalysisRepository();