refactor: comprehensive codebase cleanup and architecture hardening

- Sprint 1 (Quick Wins): Remove dead analytics modules, fix 4 unresolved
  imports, replace 3 console.warn with logger, remove mock-crc import
- Sprint 2 (Architecture): Create MascotChatRepository, AnalysisRepository,
  3 Zod schemas (mascot-chat, analysis, voice), deduplicate error classes,
  move 3 SQL queries from routes to repository
- Sprint 3 (Complexity): Replace 7 any types with proper interfaces,
  extract 6 helpers from prepareMediaMessage (CC 85 -> ~15)
- Sprint 4 (Config): Remove 22 dead env vars from .env, add 30 missing
  vars to .env.example, standardize naming

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-09 10:16:04 +07:00
co-authored by Claude Opus 4.8
parent d0d9e1669e
commit 4becf0d6f1
89 changed files with 1260 additions and 5499 deletions
+2 -286
View File
@@ -138,7 +138,7 @@ export interface UIState {
selectedTextChannel?: string;
selectedAnalyticsGuild?: string;
selectedAnalyticsChannel?: string;
activeTab?: "live" | "messages" | "analytics";
activeTab?: "live" | "messages";
isListening?: boolean;
isStreaming?: boolean;
}
@@ -147,7 +147,7 @@ export interface AppConfig {
monitorGuildId: string | null;
}
export type DashboardTab = "live" | "messages" | "analytics";
export type DashboardTab = "live" | "messages";
// ─── Messages ────────────────────────────────────────────────────────────────
@@ -277,287 +277,3 @@ export function updateUIState(patch: Partial<UIState>): Promise<UIState> {
body: JSON.stringify(patch),
});
}
// ─── Analytics ───────────────────────────────────────────────────────────────
export interface HourlyBucket {
hour: string;
count: number;
clean: number;
warned: number;
flagged: number;
error: number;
}
export interface TopicTrend {
topic: string;
count: number;
score: number;
}
export interface UserStat {
user_id: string;
username: string;
avatar_url: string | null;
message_count: number;
edited_count: number;
deleted_count: number;
flagged_count: number;
last_active: number;
}
export interface ModerationBreakdown {
total: number;
clean: number;
warned: number;
flagged: number;
error: number;
pending: number;
average_score: number;
}
export interface AnalyticsOverview {
period: { start: number; end: number };
messages: ModerationBreakdown;
hourly: HourlyBucket[];
topics: TopicTrend[];
top_users: UserStat[];
active_users_count: number;
total_channels: number;
}
export interface ViolatorStat {
user_id: string;
username: string;
avatar_url: string | null;
total_messages: number;
flagged_count: number;
warned_count: number;
violation_score: number;
worst_flags: string[];
last_violation: number;
}
export interface TrendBucket {
date: string;
count: number;
clean: number;
warned: number;
flagged: number;
error: number;
}
export interface HeatmapCell {
dayOfWeek: number;
hour: number;
count: number;
clean: number;
warned: number;
flagged: number;
}
export function fetchAnalyticsOverview(params: {
guildId: string;
channelId?: string;
hours?: number;
}): Promise<AnalyticsOverview> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
});
return request<AnalyticsOverview>(`/api/analytics/overview?${sp}`);
}
export function fetchHourlyStats(params: {
guildId: string;
channelId?: string;
hours?: number;
}): Promise<HourlyBucket[]> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
});
return request<HourlyBucket[]>(`/api/analytics/hourly?${sp}`);
}
export function fetchTopicTrends(params: {
guildId: string;
channelId?: string;
hours?: number;
}): Promise<TopicTrend[]> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
});
return request<TopicTrend[]>(`/api/analytics/topics?${sp}`);
}
export function fetchLeaderboard(params: {
guildId: string;
channelId?: string;
hours?: number;
limit?: number;
}): Promise<UserStat[]> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
...(params.limit && { limit: String(params.limit) }),
});
return request<UserStat[]>(`/api/analytics/leaderboard?${sp}`);
}
export function fetchModerationStats(params: {
guildId: string;
channelId?: string;
hours?: number;
}): Promise<ModerationBreakdown> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
});
return request<ModerationBreakdown>(`/api/analytics/stats?${sp}`);
}
export function fetchViolators(params: {
guildId: string;
channelId?: string;
hours?: number;
limit?: number;
}): Promise<ViolatorStat[]> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
...(params.limit && { limit: String(params.limit) }),
});
return request<ViolatorStat[]>(`/api/analytics/violators?${sp}`);
}
export function fetchTrend(params: {
guildId: string;
channelId?: string;
hours?: number;
}): Promise<TrendBucket[]> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
});
return request<TrendBucket[]>(`/api/analytics/trend?${sp}`);
}
export function fetchHeatmap(params: {
guildId: string;
channelId?: string;
hours?: number;
}): Promise<HeatmapCell[]> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
});
return request<HeatmapCell[]>(`/api/analytics/heatmap?${sp}`);
}
// ── New analytics types & endpoints ────────────────────────────────────────
export interface ModerationActionRecord {
id: string;
message_id: string | null;
user_id: string;
guild_id: string;
action_type: string;
reason: string | null;
executed_by: string | null;
status: string;
error: string | null;
created_at: number;
executed_at: number | null;
username: string;
content: string | null;
}
export interface AISeverityBreakdown {
none: number;
low: number;
medium: number;
high: number;
critical: number;
}
export interface AIRecommendedActions {
none: number;
monitor: number;
warn: number;
review: number;
delete: number;
escalate: number;
}
export interface AIStats {
total_analyzed: number;
severity: AISeverityBreakdown;
recommended_actions: AIRecommendedActions;
analysis_errors: number;
analysis_pending: number;
avg_confidence: number;
avg_score: number;
}
export interface AttachmentStats {
total_attachments: number;
uploaded: number;
pending: number;
failed: number;
total_size_bytes: number;
unique_uploaders: number;
top_mime_type: string | null;
}
export function fetchModerationActions(params: {
guildId: string;
channelId?: string;
hours?: number;
limit?: number;
}): Promise<ModerationActionRecord[]> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
...(params.limit && { limit: String(params.limit) }),
});
return request<ModerationActionRecord[]>(
`/api/analytics/moderation-actions?${sp}`,
);
}
export function fetchAIStats(params: {
guildId: string;
channelId?: string;
hours?: number;
}): Promise<AIStats> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
});
return request<AIStats>(`/api/analytics/ai-stats?${sp}`);
}
export function fetchAttachmentStats(params: {
guildId: string;
channelId?: string;
hours?: number;
}): Promise<AttachmentStats> {
const sp = new URLSearchParams({
guildId: params.guildId,
...(params.channelId && { channelId: params.channelId }),
...(params.hours && { hours: String(params.hours) }),
});
return request<AttachmentStats>(`/api/analytics/attachment-stats?${sp}`);
}