diff --git a/services/backend/src/modules/analysis/analysis.schema.ts b/services/backend/src/modules/analysis/analysis.schema.ts deleted file mode 100644 index 339e8ef0..00000000 --- a/services/backend/src/modules/analysis/analysis.schema.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { z } from "zod"; - -export const searchQuerySchema = z.object({ - q: z.string().default(""), - channelId: z.string().optional(), - guildId: z.string().optional(), - limit: z.coerce.number().int().positive().max(100).default(20), -}); - -export type SearchQuery = z.infer; diff --git a/services/backend/src/modules/health/health.schema.ts b/services/backend/src/modules/health/health.schema.ts deleted file mode 100644 index e91142e0..00000000 --- a/services/backend/src/modules/health/health.schema.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { z } from "zod"; - -export const healthCheckSchema = z.object({ - verbose: z.coerce.boolean().optional().default(false), -}); diff --git a/services/backend/src/modules/health/moderationMetrics.ts b/services/backend/src/modules/health/moderationMetrics.ts deleted file mode 100644 index 619e6214..00000000 --- a/services/backend/src/modules/health/moderationMetrics.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * moderationMetrics.ts - * - * Prometheus metrics for AI moderation pipeline. - * Defined in backend (where prom-client is installed + /api/metrics endpoint). - */ -import { Counter, Histogram } from "prom-client"; - -// ── LLM Call Metrics ── -export const llmCallsTotal = new Counter({ - name: "moderation_llm_calls_total", - help: "Total LLM moderation calls", - labelNames: ["path", "model"] as const, -}); - -export const llmCallDuration = new Histogram({ - name: "moderation_llm_call_duration_ms", - help: "LLM moderation call duration (ms)", - labelNames: ["path", "status"] as const, - buckets: [500, 1000, 2000, 5000, 10000, 20000, 30000, 60000, 120000], -}); - -export const llmTokensTotal = new Counter({ - name: "moderation_llm_tokens_total", - help: "Total tokens consumed by LLM moderation", - labelNames: ["type"] as const, -}); - -// ── Cache Metrics ── -export const moderationCacheHits = new Counter({ - name: "moderation_cache_hits_total", - help: "Moderation cache hits", - labelNames: ["layer"] as const, -}); - -export const moderationCacheMisses = new Counter({ - name: "moderation_cache_misses_total", - help: "Moderation cache misses", - labelNames: ["layer"] as const, -}); - -// ── Media Analysis Metrics ── -export const mediaAnalysesTotal = new Counter({ - name: "moderation_media_analyses_total", - help: "Media analyses performed", - labelNames: ["type"] as const, -}); - -export const mediaDownloadDuration = new Histogram({ - name: "moderation_media_download_duration_ms", - help: "Media download duration (ms)", - labelNames: ["source"] as const, - buckets: [100, 500, 1000, 2000, 5000, 10000, 30000], -}); - -// ── Batch & Error Metrics ── -export const moderationBatchSize = new Histogram({ - name: "moderation_batch_size", - help: "Messages per batch", - labelNames: ["path"] as const, - buckets: [1, 5, 10, 20, 50, 100], -}); - -export const moderationErrors = new Counter({ - name: "moderation_errors_total", - help: "Moderation errors", - labelNames: ["type"] as const, -}); - -export const webSearchCalls = new Counter({ - name: "moderation_websearch_calls_total", - help: "Wikipedia web-search calls", - labelNames: ["status"] as const, -}); - -export const autoDeleteActions = new Counter({ - name: "moderation_auto_delete_total", - help: "Auto-delete actions", - labelNames: ["action"] as const, -}); diff --git a/services/backend/src/shared/commandHelper.ts b/services/backend/src/shared/commandHelper.ts deleted file mode 100644 index 79d091b4..00000000 --- a/services/backend/src/shared/commandHelper.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { CommandReply } from "./index.js"; -import { createChildLogger } from "./logger/index.js"; - -export { createChildLogger }; - -/** - * Attempt a Redis command first; if it fails or times out, fall back. - * - * @param commandFn - Function that issues the publishCommand and returns the reply. - * @param fallbackFn - Async fallback, typically reads from Redis status key. - * @param commandLabel - Label used for logging (e.g. "voice:connect"). - */ -export async function tryCommandThenFallback( - commandFn: () => Promise | null>, - fallbackFn: () => Promise, - commandLabel: string, -): Promise { - const logger = createChildLogger(`command-helper:${commandLabel}`); - const reply = await commandFn(); - if (reply?.success && reply.data !== undefined && reply.data !== null) { - return reply.data; - } - logger.warn("discord-gateway unreachable, falling back"); - return fallbackFn(); -} diff --git a/services/backend/src/ws/index.ts b/services/backend/src/ws/index.ts deleted file mode 100644 index 44ba0d92..00000000 --- a/services/backend/src/ws/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { - broadcastBinary, - broadcastEvent, - clearBroadcastFunctions, - setBroadcastFunctions, -} from "./broadcast.js"; -export { startRedisBridge, stopRedisBridge } from "./redis-bridge.js"; -export { closeWebSocketServer, createWebSocketServer } from "./server.js"; diff --git a/services/discord-gateway/src/app/bootstrap.ts b/services/discord-gateway/src/app/bootstrap.ts index 3fd33b09..e8d55d13 100644 --- a/services/discord-gateway/src/app/bootstrap.ts +++ b/services/discord-gateway/src/app/bootstrap.ts @@ -31,7 +31,7 @@ import { startDigestScheduler } from "../modules/monitor/digestScheduler.js"; import { registerReactionCapture } from "../modules/reaction-tracking/index.js"; import { registerThreadCapture } from "../modules/thread-tracking/index.js"; import { registerPresenceCapture } from "../modules/user-presence/index.js"; -import { config } from "../shared/config/config.js"; +import { config } from "../shared/config/index.js"; import { closeDatabase, initializeDatabase, diff --git a/services/discord-gateway/src/app/retention.ts b/services/discord-gateway/src/app/retention.ts index d8a6104b..d7b83e1b 100644 --- a/services/discord-gateway/src/app/retention.ts +++ b/services/discord-gateway/src/app/retention.ts @@ -4,7 +4,7 @@ import type { NodePgQueryResultHKT, } from "drizzle-orm/node-postgres"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../shared/config/config.js"; +import { config } from "../shared/config/index.js"; import { getDatabase } from "../shared/database/drizzle.js"; import type * as schema from "../shared/database/schema.js"; import { attachmentsTable, messagesTable } from "../shared/database/schema.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/ai-analysis-worker.ts b/services/discord-gateway/src/modules/ai-moderation/ai-analysis-worker.ts index 6cbbd57c..39734791 100644 --- a/services/discord-gateway/src/modules/ai-moderation/ai-analysis-worker.ts +++ b/services/discord-gateway/src/modules/ai-moderation/ai-analysis-worker.ts @@ -17,7 +17,7 @@ */ import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { initializeDatabase } from "../../shared/database/drizzle.js"; import { messageStore } from "../message-capture/messageStore.js"; import type { MessageRecord } from "../message-capture/types.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/aiAnalyzer.ts b/services/discord-gateway/src/modules/ai-moderation/aiAnalyzer.ts index 5eb27830..a64d69e0 100644 --- a/services/discord-gateway/src/modules/ai-moderation/aiAnalyzer.ts +++ b/services/discord-gateway/src/modules/ai-moderation/aiAnalyzer.ts @@ -1,6 +1,6 @@ import type { Client } from "discord.js-selfbot-v13"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { EventBroadcaster } from "../event-broadcaster/index.js"; import { messageStore } from "../message-capture/messageStore.js"; import type { AnalysisQueueStatus } from "../message-capture/types.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/autoDeleteEligibility.ts b/services/discord-gateway/src/modules/ai-moderation/autoDeleteEligibility.ts index d3b8e8cd..d99ae7f1 100644 --- a/services/discord-gateway/src/modules/ai-moderation/autoDeleteEligibility.ts +++ b/services/discord-gateway/src/modules/ai-moderation/autoDeleteEligibility.ts @@ -1,5 +1,5 @@ import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { AnalysisResult, MessageRecord, diff --git a/services/discord-gateway/src/modules/ai-moderation/autoDeleteLogger.ts b/services/discord-gateway/src/modules/ai-moderation/autoDeleteLogger.ts index d619e2a9..b56637c9 100644 --- a/services/discord-gateway/src/modules/ai-moderation/autoDeleteLogger.ts +++ b/services/discord-gateway/src/modules/ai-moderation/autoDeleteLogger.ts @@ -1,6 +1,6 @@ import type { Guild } from "discord.js-selfbot-v13"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { MessageRecord } from "../message-capture/types.js"; interface ChannelWithSend { diff --git a/services/discord-gateway/src/modules/ai-moderation/autoDeleteManager.ts b/services/discord-gateway/src/modules/ai-moderation/autoDeleteManager.ts index 01f62b59..4718e01c 100644 --- a/services/discord-gateway/src/modules/ai-moderation/autoDeleteManager.ts +++ b/services/discord-gateway/src/modules/ai-moderation/autoDeleteManager.ts @@ -1,7 +1,7 @@ import type { Client, PermissionString } from "discord.js-selfbot-v13"; import { LRUCache } from "lru-cache"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { parseRichMessageMetadata } from "../message-capture/messageMetadata.js"; import { messageStore } from "../message-capture/messageStore.js"; import type { MessageRecord } from "../message-capture/types.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/autoDeleteNotify.ts b/services/discord-gateway/src/modules/ai-moderation/autoDeleteNotify.ts index 67132873..18e4c729 100644 --- a/services/discord-gateway/src/modules/ai-moderation/autoDeleteNotify.ts +++ b/services/discord-gateway/src/modules/ai-moderation/autoDeleteNotify.ts @@ -1,6 +1,6 @@ import type { Client } from "discord.js-selfbot-v13"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { MessageRecord } from "../message-capture/types.js"; const logger = createChildLogger("auto-delete-notify"); diff --git a/services/discord-gateway/src/modules/ai-moderation/batchProcessor.ts b/services/discord-gateway/src/modules/ai-moderation/batchProcessor.ts index 33078574..cb8a3e3b 100644 --- a/services/discord-gateway/src/modules/ai-moderation/batchProcessor.ts +++ b/services/discord-gateway/src/modules/ai-moderation/batchProcessor.ts @@ -1,5 +1,5 @@ import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { isAgeRestrictedMetadata } from "../message-capture/messageMetadata.js"; import { messageStore } from "../message-capture/messageStore.js"; import type { MessageRecord } from "../message-capture/types.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/batchScheduler.ts b/services/discord-gateway/src/modules/ai-moderation/batchScheduler.ts index fab9216f..07d26b45 100644 --- a/services/discord-gateway/src/modules/ai-moderation/batchScheduler.ts +++ b/services/discord-gateway/src/modules/ai-moderation/batchScheduler.ts @@ -1,5 +1,5 @@ import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { messageStore } from "../message-capture/messageStore.js"; import type { MessageRecord } from "../message-capture/types.js"; import { diff --git a/services/discord-gateway/src/modules/ai-moderation/circuitBreaker.ts b/services/discord-gateway/src/modules/ai-moderation/circuitBreaker.ts index 4a2dc504..d04a40ab 100644 --- a/services/discord-gateway/src/modules/ai-moderation/circuitBreaker.ts +++ b/services/discord-gateway/src/modules/ai-moderation/circuitBreaker.ts @@ -2,7 +2,7 @@ import { existsSync } from "node:fs"; import { availableParallelism } from "node:os"; import { fileURLToPath } from "node:url"; import { Piscina } from "piscina"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { MessageRecord } from "../message-capture/types.js"; // --------------------------------------------------------------------------- diff --git a/services/discord-gateway/src/modules/ai-moderation/conversationState.ts b/services/discord-gateway/src/modules/ai-moderation/conversationState.ts index 47bdf769..c820eb0c 100644 --- a/services/discord-gateway/src/modules/ai-moderation/conversationState.ts +++ b/services/discord-gateway/src/modules/ai-moderation/conversationState.ts @@ -1,6 +1,6 @@ import { LRUCache } from "lru-cache"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { LAST_ERROR } from "./moderationState.js"; /** diff --git a/services/discord-gateway/src/modules/ai-moderation/cultureLearner.ts b/services/discord-gateway/src/modules/ai-moderation/cultureLearner.ts index caf1cd82..1e0387b6 100644 --- a/services/discord-gateway/src/modules/ai-moderation/cultureLearner.ts +++ b/services/discord-gateway/src/modules/ai-moderation/cultureLearner.ts @@ -1,6 +1,6 @@ import { and, desc, eq, sql } from "drizzle-orm"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { getDatabase } from "../../shared/database/drizzle.js"; import { messagesTable } from "../../shared/database/schema.js"; import { updateChannelCulture } from "./channelCultureStore.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/embeddingClient.ts b/services/discord-gateway/src/modules/ai-moderation/embeddingClient.ts index 314eeef4..31635e1a 100644 --- a/services/discord-gateway/src/modules/ai-moderation/embeddingClient.ts +++ b/services/discord-gateway/src/modules/ai-moderation/embeddingClient.ts @@ -14,7 +14,7 @@ import OpenAI from "openai"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { cleanContent } from "./textSignals.js"; const log = createChildLogger("embedding-client"); diff --git a/services/discord-gateway/src/modules/ai-moderation/index.ts b/services/discord-gateway/src/modules/ai-moderation/index.ts deleted file mode 100644 index 35e62865..00000000 --- a/services/discord-gateway/src/modules/ai-moderation/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// ── Single-pass LLM pipeline exports ───────────────────────────────────── -export type { - AnalysisInput, - AnalysisResult, - MessageBatch, - WorkerConfig, -} from "./ai-analysis-worker.js"; -export { startPendingAIAnalysisWorker } from "./aiAnalyzer.js"; -export { sanitizeDiscordTokens } from "./discordTokens.js"; -export { runModerationAnalysis } from "./moderationOrchestrator.js"; -export { buildSystemPrompt } from "./moderationPrompt.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/individualFallbackProcessor.ts b/services/discord-gateway/src/modules/ai-moderation/individualFallbackProcessor.ts index 4e6bc671..9760ecaf 100644 --- a/services/discord-gateway/src/modules/ai-moderation/individualFallbackProcessor.ts +++ b/services/discord-gateway/src/modules/ai-moderation/individualFallbackProcessor.ts @@ -1,6 +1,6 @@ import { LRUCache } from "lru-cache"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { messageStore } from "../message-capture/messageStore.js"; import type { AnalysisResult, diff --git a/services/discord-gateway/src/modules/ai-moderation/llmCaller.ts b/services/discord-gateway/src/modules/ai-moderation/llmCaller.ts index 7715c723..003f6223 100644 --- a/services/discord-gateway/src/modules/ai-moderation/llmCaller.ts +++ b/services/discord-gateway/src/modules/ai-moderation/llmCaller.ts @@ -13,7 +13,7 @@ import type { ChatCompletion } from "openai/resources/chat/completions"; import { createChildLogger } from "@/shared/logger/index"; import { delay, retryWithBackoff } from "@/shared/utils/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { incrementCounterBy } from "../gateway-metrics/index.js"; import type { AnalysisResult } from "../message-capture/types.js"; import { llmChat } from "./llmClient.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/llmClient.ts b/services/discord-gateway/src/modules/ai-moderation/llmClient.ts index c87c6d9c..351edc6c 100644 --- a/services/discord-gateway/src/modules/ai-moderation/llmClient.ts +++ b/services/discord-gateway/src/modules/ai-moderation/llmClient.ts @@ -10,7 +10,7 @@ import OpenAI from "openai"; import pLimit from "p-limit"; import { createChildLogger } from "@/shared/logger/index"; import { retryWithBackoff } from "@/shared/utils/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; const log = createChildLogger("llm-client"); diff --git a/services/discord-gateway/src/modules/ai-moderation/mediaBatchProcessor.ts b/services/discord-gateway/src/modules/ai-moderation/mediaBatchProcessor.ts index d7be5050..33783d4d 100644 --- a/services/discord-gateway/src/modules/ai-moderation/mediaBatchProcessor.ts +++ b/services/discord-gateway/src/modules/ai-moderation/mediaBatchProcessor.ts @@ -6,7 +6,7 @@ * moderationOrchestrator.ts. */ import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { AnalysisResult, AttachmentRecord, diff --git a/services/discord-gateway/src/modules/ai-moderation/moderationOrchestrator.ts b/services/discord-gateway/src/modules/ai-moderation/moderationOrchestrator.ts index 46bdf4ab..0a0a1304 100644 --- a/services/discord-gateway/src/modules/ai-moderation/moderationOrchestrator.ts +++ b/services/discord-gateway/src/modules/ai-moderation/moderationOrchestrator.ts @@ -7,7 +7,7 @@ import { LRUCache } from "lru-cache"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { incrementCounterBy } from "../gateway-metrics/index.js"; import { extractMessageMediaEvidence } from "../message-capture/messageMetadata.js"; import type { diff --git a/services/discord-gateway/src/modules/ai-moderation/moderationState.ts b/services/discord-gateway/src/modules/ai-moderation/moderationState.ts index a2db848b..c3c16c9c 100644 --- a/services/discord-gateway/src/modules/ai-moderation/moderationState.ts +++ b/services/discord-gateway/src/modules/ai-moderation/moderationState.ts @@ -1,7 +1,7 @@ import type { Client } from "discord.js-selfbot-v13"; import { LRUCache } from "lru-cache"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { EventBroadcaster } from "../event-broadcaster/index.js"; import type { MessageRecord } from "../message-capture/types.js"; import { attemptAutoDeleteFlaggedMessage } from "./autoDeleteManager.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/qdrantClient.ts b/services/discord-gateway/src/modules/ai-moderation/qdrantClient.ts index 4c82c05f..1361f55b 100644 --- a/services/discord-gateway/src/modules/ai-moderation/qdrantClient.ts +++ b/services/discord-gateway/src/modules/ai-moderation/qdrantClient.ts @@ -13,7 +13,7 @@ import { createHash } from "node:crypto"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; const log = createChildLogger("qdrant"); diff --git a/services/discord-gateway/src/modules/ai-moderation/stickerCache.ts b/services/discord-gateway/src/modules/ai-moderation/stickerCache.ts index 487d2eba..f6067a23 100644 --- a/services/discord-gateway/src/modules/ai-moderation/stickerCache.ts +++ b/services/discord-gateway/src/modules/ai-moderation/stickerCache.ts @@ -1,5 +1,5 @@ import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { executeAll, executeGet } from "../../shared/database/drizzle.js"; import { uploadToTele } from "../../shared/uploader.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/termGlossary.ts b/services/discord-gateway/src/modules/ai-moderation/termGlossary.ts index e5b8bec5..c0993fbc 100644 --- a/services/discord-gateway/src/modules/ai-moderation/termGlossary.ts +++ b/services/discord-gateway/src/modules/ai-moderation/termGlossary.ts @@ -34,7 +34,7 @@ import { LRUCache } from "lru-cache"; import pLimit from "p-limit"; import { createChildLogger } from "@/shared/logger/index"; import { delay } from "@/shared/utils/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { cacheGet, cacheSet, makeCacheKey } from "./cacheStore.js"; import { escapeXml } from "./moderationBuilders.js"; import { diff --git a/services/discord-gateway/src/modules/ai-moderation/textBatchProcessor.ts b/services/discord-gateway/src/modules/ai-moderation/textBatchProcessor.ts index cbd7cc24..93552126 100644 --- a/services/discord-gateway/src/modules/ai-moderation/textBatchProcessor.ts +++ b/services/discord-gateway/src/modules/ai-moderation/textBatchProcessor.ts @@ -7,7 +7,7 @@ */ import { createChildLogger } from "@/shared/logger/index"; import { delay } from "@/shared/utils/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { resizeImageForVision } from "../attachment-upload/imageResizer.js"; import type { AnalysisResult, diff --git a/services/discord-gateway/src/modules/ai-moderation/textCacheStore.ts b/services/discord-gateway/src/modules/ai-moderation/textCacheStore.ts index f9b6632e..da0004d9 100644 --- a/services/discord-gateway/src/modules/ai-moderation/textCacheStore.ts +++ b/services/discord-gateway/src/modules/ai-moderation/textCacheStore.ts @@ -1,6 +1,6 @@ import { createHash } from "node:crypto"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { executeAll, executeGet } from "../../shared/database/drizzle.js"; import { findBestEmbeddingMatch } from "./embeddingClient.js"; import { diff --git a/services/discord-gateway/src/modules/ai-moderation/tinyFishSearch.ts b/services/discord-gateway/src/modules/ai-moderation/tinyFishSearch.ts index d927bc2b..f4559e5b 100644 --- a/services/discord-gateway/src/modules/ai-moderation/tinyFishSearch.ts +++ b/services/discord-gateway/src/modules/ai-moderation/tinyFishSearch.ts @@ -1,6 +1,6 @@ import { createChildLogger } from "@/shared/logger/index"; import { createAbortControllerWithTimeout } from "@/shared/utils/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { SearchResult } from "./wikipediaClient.js"; const log = createChildLogger("tinyfish-search"); diff --git a/services/discord-gateway/src/modules/ai-moderation/userProfileLearner.ts b/services/discord-gateway/src/modules/ai-moderation/userProfileLearner.ts index 110ddf98..3317b282 100644 --- a/services/discord-gateway/src/modules/ai-moderation/userProfileLearner.ts +++ b/services/discord-gateway/src/modules/ai-moderation/userProfileLearner.ts @@ -1,6 +1,6 @@ import { and, desc, eq, sql } from "drizzle-orm"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { getDatabase } from "../../shared/database/drizzle.js"; import { messagesTable } from "../../shared/database/schema.js"; import { llmChat } from "./llmClient.js"; diff --git a/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts b/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts index 3ef27fcc..f573fa90 100644 --- a/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts +++ b/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts @@ -7,7 +7,7 @@ */ import { createChildLogger } from "@/shared/logger/index"; import { delay } from "@/shared/utils/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { extractMessageMediaEvidence } from "../message-capture/messageMetadata.js"; import type { AttachmentRecord, diff --git a/services/discord-gateway/src/modules/ai-moderation/wikipediaClient.ts b/services/discord-gateway/src/modules/ai-moderation/wikipediaClient.ts index 1d2a15e5..ef61298d 100644 --- a/services/discord-gateway/src/modules/ai-moderation/wikipediaClient.ts +++ b/services/discord-gateway/src/modules/ai-moderation/wikipediaClient.ts @@ -20,7 +20,7 @@ import { createChildLogger } from "@/shared/logger/index"; import { createAbortControllerWithTimeout } from "@/shared/utils/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { cacheGet, cacheSet, makeCacheKey } from "./cacheStore.js"; import { cleanContent, diff --git a/services/discord-gateway/src/modules/attachment-upload/attachmentUploader.ts b/services/discord-gateway/src/modules/attachment-upload/attachmentUploader.ts index 6a151a77..61a594c7 100644 --- a/services/discord-gateway/src/modules/attachment-upload/attachmentUploader.ts +++ b/services/discord-gateway/src/modules/attachment-upload/attachmentUploader.ts @@ -1,6 +1,6 @@ import { createChildLogger } from "@/shared/logger/index"; import { retryWithBackoff } from "@/shared/utils/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { uploadToTele } from "../../shared/uploader.js"; import { messageStore } from "../message-capture/messageStore.js"; diff --git a/services/discord-gateway/src/modules/attachment-upload/index.ts b/services/discord-gateway/src/modules/attachment-upload/index.ts deleted file mode 100644 index b343778c..00000000 --- a/services/discord-gateway/src/modules/attachment-upload/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { processAttachmentUpload } from "./attachmentUploader.js"; -export { resizeImageForVision as resizeImage } from "./imageResizer.js"; diff --git a/services/discord-gateway/src/modules/channel-topic/channelTopicCapture.ts b/services/discord-gateway/src/modules/channel-topic/channelTopicCapture.ts index 25008b18..9e872e65 100644 --- a/services/discord-gateway/src/modules/channel-topic/channelTopicCapture.ts +++ b/services/discord-gateway/src/modules/channel-topic/channelTopicCapture.ts @@ -1,6 +1,6 @@ import type { Client, TextChannel } from "discord.js-selfbot-v13"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { EventBroadcaster } from "../event-broadcaster/eventBroadcaster.js"; const logger = createChildLogger("channel-topic"); diff --git a/services/discord-gateway/src/modules/command-handler/commandHandler.ts b/services/discord-gateway/src/modules/command-handler/commandHandler.ts index d9750219..ea7e377f 100644 --- a/services/discord-gateway/src/modules/command-handler/commandHandler.ts +++ b/services/discord-gateway/src/modules/command-handler/commandHandler.ts @@ -1,6 +1,6 @@ import type { Client } from "discord.js-selfbot-v13"; import Redis from "ioredis"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { BACKEND_COMMAND, type CommandMessage, diff --git a/services/discord-gateway/src/modules/gateway-metrics/metrics.ts b/services/discord-gateway/src/modules/gateway-metrics/metrics.ts index 518fab65..387fce21 100644 --- a/services/discord-gateway/src/modules/gateway-metrics/metrics.ts +++ b/services/discord-gateway/src/modules/gateway-metrics/metrics.ts @@ -1,6 +1,6 @@ import http from "node:http"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; const logger = createChildLogger("gateway-metrics"); diff --git a/services/discord-gateway/src/modules/guild-member-events/memberEvents.ts b/services/discord-gateway/src/modules/guild-member-events/memberEvents.ts index c06f8127..6f0cd902 100644 --- a/services/discord-gateway/src/modules/guild-member-events/memberEvents.ts +++ b/services/discord-gateway/src/modules/guild-member-events/memberEvents.ts @@ -4,7 +4,7 @@ import type { PartialGuildMember, } from "discord.js-selfbot-v13"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { EventBroadcaster } from "../event-broadcaster/eventBroadcaster.js"; const logger = createChildLogger("guild-member-events"); diff --git a/services/discord-gateway/src/modules/message-capture/archiveEmbedder.ts b/services/discord-gateway/src/modules/message-capture/archiveEmbedder.ts index d22807cb..bca2304a 100644 --- a/services/discord-gateway/src/modules/message-capture/archiveEmbedder.ts +++ b/services/discord-gateway/src/modules/message-capture/archiveEmbedder.ts @@ -8,7 +8,7 @@ import { upsertQdrantPointV2, } from "@/modules/ai-moderation/qdrantClient"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; const log = createChildLogger("archive-embedder"); diff --git a/services/discord-gateway/src/modules/message-capture/index.ts b/services/discord-gateway/src/modules/message-capture/index.ts deleted file mode 100644 index a29e2312..00000000 --- a/services/discord-gateway/src/modules/message-capture/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -export { - getDisplayContent, - getMessageLocation, - getMessageMetadata, -} from "../message-capture/messageMetadata.js"; -export type { - AIRecommendedAction, - AISeverity, - AIStatus, - AttachmentRecord, - MessageRecord, -} from "../message-capture/types.js"; -export type { TextCaptureTarget } from "./messageCapture.js"; -export { - captureMessage, - registerMessageCapture, - setEventBroadcaster, -} from "./messageCapture.js"; -export { messageStore } from "./messageStore.js"; diff --git a/services/discord-gateway/src/modules/message-capture/messageCapture.ts b/services/discord-gateway/src/modules/message-capture/messageCapture.ts index 239b5a77..0523758d 100644 --- a/services/discord-gateway/src/modules/message-capture/messageCapture.ts +++ b/services/discord-gateway/src/modules/message-capture/messageCapture.ts @@ -1,6 +1,6 @@ import type { Client, Message } from "discord.js-selfbot-v13"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { queueMessageAnalysis } from "../ai-moderation/aiAnalyzer.js"; import { processAttachmentUpload } from "../attachment-upload/attachmentUploader.js"; import type { EventBroadcaster } from "../event-broadcaster/eventBroadcaster.js"; diff --git a/services/discord-gateway/src/modules/reaction-tracking/reactionCapture.ts b/services/discord-gateway/src/modules/reaction-tracking/reactionCapture.ts index f74eaa3b..e74536d1 100644 --- a/services/discord-gateway/src/modules/reaction-tracking/reactionCapture.ts +++ b/services/discord-gateway/src/modules/reaction-tracking/reactionCapture.ts @@ -6,7 +6,7 @@ import type { User, } from "discord.js-selfbot-v13"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import { getDatabase } from "../../shared/database/drizzle.js"; import { reactionsTable } from "../../shared/database/schema.js"; import type { EventBroadcaster } from "../event-broadcaster/eventBroadcaster.js"; diff --git a/services/discord-gateway/src/modules/thread-tracking/threadCapture.ts b/services/discord-gateway/src/modules/thread-tracking/threadCapture.ts index 1ce78a38..d5786bec 100644 --- a/services/discord-gateway/src/modules/thread-tracking/threadCapture.ts +++ b/services/discord-gateway/src/modules/thread-tracking/threadCapture.ts @@ -1,6 +1,6 @@ import type { Client, ThreadChannel } from "discord.js-selfbot-v13"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { EventBroadcaster } from "../event-broadcaster/eventBroadcaster.js"; const logger = createChildLogger("thread-tracking"); diff --git a/services/discord-gateway/src/modules/user-presence/presenceCapture.ts b/services/discord-gateway/src/modules/user-presence/presenceCapture.ts index 74caee18..d424ad35 100644 --- a/services/discord-gateway/src/modules/user-presence/presenceCapture.ts +++ b/services/discord-gateway/src/modules/user-presence/presenceCapture.ts @@ -1,6 +1,6 @@ import type { Client, Presence } from "discord.js-selfbot-v13"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import type { EventBroadcaster } from "../event-broadcaster/eventBroadcaster.js"; const logger = createChildLogger("presence-tracking"); diff --git a/services/discord-gateway/src/shared/config/config.ts b/services/discord-gateway/src/shared/config/config.ts deleted file mode 100644 index 065b9f9f..00000000 --- a/services/discord-gateway/src/shared/config/config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import "dotenv/config"; -import type { AppConfig } from "@/shared/config/index"; -import { loadConfig as sharedLoadConfig } from "@/shared/config/index"; - -// Re-export the unified config — all EFFECTIVE_* fields are already -// computed by the shared loadConfig(). -export type { AppConfig }; - -export function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig { - return sharedLoadConfig(env); -} - -export const config = loadConfig(); diff --git a/services/discord-gateway/src/shared/database/drizzle.ts b/services/discord-gateway/src/shared/database/drizzle.ts index 24a6d738..2997fd1c 100644 --- a/services/discord-gateway/src/shared/database/drizzle.ts +++ b/services/discord-gateway/src/shared/database/drizzle.ts @@ -7,7 +7,7 @@ import { withDatabaseClient as sharedWithClient, } from "@/shared/database/init"; import { createChildLogger } from "@/shared/logger/index"; -import { config } from "../../shared/config/config.js"; +import { config } from "../../shared/config/index.js"; import * as schema from "./schema.js"; const logger = createChildLogger("drizzle"); diff --git a/services/discord-gateway/src/shared/database/schema/analytics.ts b/services/discord-gateway/src/shared/database/schema/analytics.ts deleted file mode 100644 index 26491532..00000000 --- a/services/discord-gateway/src/shared/database/schema/analytics.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { - pgAIAnalysisRunsTable, - pgChannelCulturesTable, - pgUserProfilesTable, -} from "../../../shared/index.js"; - -// Re-export shared tables -export { pgAIAnalysisRunsTable, pgChannelCulturesTable, pgUserProfilesTable }; -export const aiAnalysisRunsTable = pgAIAnalysisRunsTable; -export const channelCulturesTable = pgChannelCulturesTable; -export const userProfilesTable = pgUserProfilesTable; - -// Types -export type AIAnalysisRun = typeof aiAnalysisRunsTable.$inferSelect; -export type AIAnalysisRunInsert = typeof aiAnalysisRunsTable.$inferInsert; -export type ChannelCulture = typeof channelCulturesTable.$inferSelect; -export type ChannelCultureInsert = typeof channelCulturesTable.$inferInsert; -export type UserProfile = typeof userProfilesTable.$inferSelect; -export type UserProfileInsert = typeof userProfilesTable.$inferInsert; diff --git a/services/discord-gateway/src/shared/database/schema/cache.ts b/services/discord-gateway/src/shared/database/schema/cache.ts deleted file mode 100644 index 81de7858..00000000 --- a/services/discord-gateway/src/shared/database/schema/cache.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - pgCorrectedModerationsTable, - pgStickerCacheTable, - pgTextAnalysisCacheTable, -} from "../../../shared/index.js"; - -// Re-export shared tables -export { - pgCorrectedModerationsTable, - pgStickerCacheTable, - pgTextAnalysisCacheTable, -}; -export const correctedModerationsTable = pgCorrectedModerationsTable; -export const stickerCacheTable = pgStickerCacheTable; -export const textAnalysisCacheTable = pgTextAnalysisCacheTable; - -// Types -export type StickerCacheRecord = typeof stickerCacheTable.$inferSelect; -export type StickerCacheInsert = typeof stickerCacheTable.$inferInsert; -export type CorrectedModeration = typeof correctedModerationsTable.$inferSelect; -export type CorrectedModerationInsert = - typeof correctedModerationsTable.$inferInsert; diff --git a/services/discord-gateway/src/shared/database/schema/messages.ts b/services/discord-gateway/src/shared/database/schema/messages.ts deleted file mode 100644 index aaaaf57f..00000000 --- a/services/discord-gateway/src/shared/database/schema/messages.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { - bigint as pgBigint, - boolean as pgBoolean, - index as pgIndex, - real as pgReal, - pgTable, - text as pgText, - uuid as pgUuid, -} from "drizzle-orm/pg-core"; -import { - pgAttachmentsTable, - pgMessageReviewsTable, - pgMessagesTable, -} from "../../../shared/index.js"; - -// Re-export shared message/attachment/review tables -export { pgAttachmentsTable, pgMessageReviewsTable, pgMessagesTable }; -export const messagesTable = pgMessagesTable; -export const attachmentsTable = pgAttachmentsTable; -export const messageReviewsTable = pgMessageReviewsTable; - -/** - * Moderation Actions Table (PostgreSQL) - * Tracks actions taken on messages (delete, mute, etc.) - */ -export const pgModerationActionsTable = pgTable( - "moderation_actions", - { - id: pgText("id").primaryKey(), - message_id: pgText("message_id"), - user_id: pgText("user_id"), - guild_id: pgText("guild_id").notNull(), - action_type: pgText("action_type", { - enum: [ - "delete_message", - "mute_user", - "warn_user", - "kick_user", - "ban_user", - ], - }).notNull(), - reason: pgText("reason"), - executed_by: pgText("executed_by"), - status: pgText("status", { - enum: ["pending", "executed", "failed"], - }) - .notNull() - .default("pending"), - error: pgText("error"), - created_at: pgBigint("created_at", { mode: "number" }).notNull(), - executed_at: pgBigint("executed_at", { mode: "number" }), - // ── Explainability (structured verdict; surfaced read-only to public web) ── - flags: pgText("flags"), // JSON array of string flags, e.g. ["sara_agama","vulgar"] - categories: pgText("categories"), // JSON array of category strings - severity: pgText("severity", { - enum: ["none", "low", "medium", "high", "critical"], - }), - confidence: pgReal("confidence"), // 0..1 - score: pgReal("score"), // 0..1 raw model score - evidence: pgText("evidence"), // JSON array of short quoted snippets - policy_version: pgText("policy_version"), // rules.ts policy version string - username: pgText("username"), // denormalized from messages table for retention safety - server_nick: pgText("server_nick"), // member's server-specific display name (nickname), from metadata.member.displayName - }, - (table) => ({ - messageIdIdx: pgIndex("idx_moderation_actions_message_id").on( - table.message_id, - ), - userIdIdx: pgIndex("idx_moderation_actions_user_id").on(table.user_id), - statusIdx: pgIndex("idx_moderation_actions_status").on(table.status), - guildStatusIdx: pgIndex("idx_moderation_actions_guild_status").on( - table.guild_id, - table.status, - table.created_at, - ), - }), -); - -export const moderationActionsTable = pgModerationActionsTable; - -/** - * Message Edits Table (PostgreSQL) - */ -export const pgMessageEditsTable = pgTable( - "message_edits", - { - id: pgUuid("id").defaultRandom().primaryKey(), - message_id: pgText("message_id").notNull(), - old_content: pgText("old_content").notNull(), - edited_at: pgBigint("edited_at", { mode: "number" }).notNull(), - }, - (table) => ({ - messageIdIdx: pgIndex("idx_message_edits_message_id").on(table.message_id), - editedAtIdx: pgIndex("idx_message_edits_edited_at").on(table.edited_at), - }), -); - -export const messageEditsTable = pgMessageEditsTable; - -/** - * Reactions Table (PostgreSQL) - */ -export const pgReactionsTable = pgTable( - "message_reactions", - { - id: pgText("id").primaryKey(), - message_id: pgText("message_id").notNull(), - channel_id: pgText("channel_id").notNull(), - guild_id: pgText("guild_id").notNull(), - user_id: pgText("user_id").notNull(), - username: pgText("username").notNull(), - emoji: pgText("emoji").notNull(), - emoji_id: pgText("emoji_id"), - animated: pgBoolean("animated").notNull().default(false), - reaction_type: pgText("reaction_type", { - enum: ["add", "remove"], - }).notNull(), - created_at: pgBigint("created_at", { mode: "number" }).notNull(), - }, - (table) => ({ - messageIdIdx: pgIndex("idx_reactions_message_id").on(table.message_id), - userIdIdx: pgIndex("idx_reactions_user_id").on(table.user_id), - guildCreatedIdx: pgIndex("idx_reactions_guild_created").on( - table.guild_id, - table.created_at, - ), - }), -); - -export const reactionsTable = pgReactionsTable; - -// Types -export type Message = typeof messagesTable.$inferSelect; -export type MessageInsert = typeof messagesTable.$inferInsert; -export type Attachment = typeof attachmentsTable.$inferSelect; -export type AttachmentInsert = typeof attachmentsTable.$inferInsert; -export type MessageReview = typeof messageReviewsTable.$inferSelect; -export type MessageReviewInsert = typeof messageReviewsTable.$inferInsert; -export type ModerationAction = typeof moderationActionsTable.$inferSelect; -export type ModerationActionInsert = typeof moderationActionsTable.$inferInsert; diff --git a/services/discord-gateway/src/shared/database/schema/meta.ts b/services/discord-gateway/src/shared/database/schema/meta.ts deleted file mode 100644 index d45ecad6..00000000 --- a/services/discord-gateway/src/shared/database/schema/meta.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - pgChatbotMessagesTable, - pgMuxerJobsTable, - pgRetentionPoliciesTable, - pgUIStateTable, -} from "../../../shared/index.js"; - -// Re-export shared tables -export { - pgChatbotMessagesTable, - pgMuxerJobsTable, - pgRetentionPoliciesTable, - pgUIStateTable, -}; -export const muxerJobsTable = pgMuxerJobsTable; -export const uiStateTable = pgUIStateTable; -export const retentionPoliciesTable = pgRetentionPoliciesTable; -export const chatbotMessagesTable = pgChatbotMessagesTable; - -// Types -export type MuxerJob = typeof muxerJobsTable.$inferSelect; -export type MuxerJobInsert = typeof muxerJobsTable.$inferInsert; -export type UIState = typeof uiStateTable.$inferSelect; -export type UIStateInsert = typeof uiStateTable.$inferInsert; -export type RetentionPolicy = typeof retentionPoliciesTable.$inferSelect; -export type RetentionPolicyInsert = typeof retentionPoliciesTable.$inferInsert; -export type ChatbotMessage = typeof chatbotMessagesTable.$inferSelect; -export type ChatbotMessageInsert = typeof chatbotMessagesTable.$inferInsert; diff --git a/services/discord-gateway/tests/tinyFishSearch.test.ts b/services/discord-gateway/tests/tinyFishSearch.test.ts index 2cf0db3b..844a82dc 100644 --- a/services/discord-gateway/tests/tinyFishSearch.test.ts +++ b/services/discord-gateway/tests/tinyFishSearch.test.ts @@ -45,7 +45,7 @@ describe("tinyFishSearch fallback", () => { // Pin the live config object to a known-disabled state: the shell may // export a real TINYFISH_API_KEY (dev box), which would flip // isTinyFishEnabled() and let tests hit the network. - const { config } = await import("../../src/shared/config/config.js"); + const { config } = await import("../../src/shared/config/index.js"); prevKey = config.TINYFISH_API_KEY; prevEnabled = config.TINYFISH_SEARCH_ENABLED; (config as Record).TINYFISH_API_KEY = ""; @@ -54,7 +54,7 @@ describe("tinyFishSearch fallback", () => { afterEach(async () => { vi.restoreAllMocks(); - const { config } = await import("../../src/shared/config/config.js"); + const { config } = await import("../../src/shared/config/index.js"); (config as Record).TINYFISH_API_KEY = prevKey; (config as Record).TINYFISH_SEARCH_ENABLED = prevEnabled; }); @@ -63,7 +63,7 @@ describe("tinyFishSearch fallback", () => { config: Record; prev: string; }> { - const { config } = await import("../../src/shared/config/config.js"); + const { config } = await import("../../src/shared/config/index.js"); const prev = config.TINYFISH_API_KEY; (config as Record).TINYFISH_API_KEY = "sk-test-key"; return { config: config as unknown as Record, prev }; diff --git a/services/frontend/src/components/CategoryDrilldown.tsx b/services/frontend/src/components/CategoryDrilldown.tsx deleted file mode 100644 index 6242d1ac..00000000 --- a/services/frontend/src/components/CategoryDrilldown.tsx +++ /dev/null @@ -1,131 +0,0 @@ -"use client"; - -import { ChevronRight } from "lucide-react"; -import { Badge, GlassPanel } from "@/components/primitives"; -import { SectionHeader } from "@/components/shared"; -import { formatNumber, formatRelativeTime } from "@/lib/format"; -import type { CategoryAction, ModerationTrends } from "@/lib/types"; - -const SEVERITY_TONE: Record< - string, - "signal" | "amber" | "vermilion" | "neutral" -> = { - critical: "vermilion", - high: "vermilion", - medium: "amber", - low: "signal", - none: "neutral", -}; - -interface CategoryDrilldownProps { - trends: ModerationTrends; - selected?: string | null; - actions?: CategoryAction[]; - loading?: boolean; - onSelect: (category: string | null) => void; -} - -export function CategoryDrilldown({ - trends, - selected, - actions, - loading, - onSelect, -}: CategoryDrilldownProps) { - const maxCat = trends.categories.reduce((m, c) => Math.max(m, c.count), 0); - - return ( - - - {selected ? ( -
- - - / {selected} ( - {loading ? "loading…" : formatNumber(actions?.length ?? 0)} actions) - -
- ) : ( -

- Click a category to list the underlying moderation actions. -

- )} - - {!selected ? ( -
- {trends.categories.map((c) => { - const pct = maxCat > 0 ? Math.max(2, (c.count / maxCat) * 100) : 0; - return ( - - ); - })} -
- ) : ( -
- {loading &&

Loading…

} - {!loading && actions && actions.length === 0 && ( -

- No actions in this category. -

- )} - {actions?.slice(0, 12).map((a) => ( -
- - {a.severity ?? "none"} - -
-
- {a.action_type} - {a.username && ( - @{a.username} - )} - - {a.created_at ? formatRelativeTime(a.created_at) : ""} - -
- {a.content && ( -

- {a.content} -

- )} - {a.reason && ( -

- Reason: {a.reason} -

- )} - -
-
- ))} -
- )} -
- ); -} diff --git a/services/frontend/src/components/CoverageTiles.tsx b/services/frontend/src/components/CoverageTiles.tsx deleted file mode 100644 index c375c3d4..00000000 --- a/services/frontend/src/components/CoverageTiles.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client"; - -import { AlertCircle, CheckCircle2, XCircle } from "lucide-react"; -import { GlassPanel } from "@/components/primitives"; -import { MetricTile, SectionHeader } from "@/components/shared"; -import { formatNumber } from "@/lib/format"; -import type { ModerationCoverage } from "@/lib/types"; - -export function CoverageTiles({ coverage }: { coverage: ModerationCoverage }) { - const pct = (n: number) => `${n.toFixed(1)}%`; - return ( - - -
- 90 ? "signal" : "amber"} - icon={} - /> - } - /> - 0 ? "vermilion" : "neutral"} - icon={} - /> - 0 ? "amber" : "neutral"} - icon={} - /> -
-

- {pct(coverage.failed_rate)} of analysis runs failed. Total runs in - window: {formatNumber(coverage.total)}. -

-
- ); -} diff --git a/services/frontend/src/components/TopicTrends.tsx b/services/frontend/src/components/TopicTrends.tsx deleted file mode 100644 index 6f90dc0c..00000000 --- a/services/frontend/src/components/TopicTrends.tsx +++ /dev/null @@ -1,146 +0,0 @@ -"use client"; - -import { Donut } from "@/components/charts/donut"; -import { GlassPanel } from "@/components/primitives"; -import { SectionHeader } from "@/components/shared"; -import { formatNumber } from "@/lib/format"; -import type { ModerationTrends } from "@/lib/types"; - -const SEVERITY_COLOR: Record = { - critical: "var(--color-vermilion)", - high: "var(--color-vermilion)", - medium: "var(--color-amber)", - low: "var(--color-signal)", - none: "var(--color-ink-faint)", -}; - -function BarRow({ - label, - count, - max, - color = "var(--color-signal)", -}: { - label: string; - count: number; - max: number; - color?: string; -}) { - const pct = max > 0 ? Math.max(2, (count / max) * 100) : 0; - return ( -
- {label} -
-
-
- - {formatNumber(count)} - -
- ); -} - -export function TopicTrends({ trends }: { trends: ModerationTrends }) { - const maxCat = trends.categories.reduce((m, c) => Math.max(m, c.count), 0); - const maxAct = trends.actions.reduce((m, a) => Math.max(m, a.count), 0); - const totalSev = trends.severities.reduce((s, x) => s + x.count, 0); - - const severitySegments = trends.severities.map((s) => ({ - value: s.count, - color: SEVERITY_COLOR[s.level] ?? "var(--color-ink-faint)", - label: s.level, - })); - - return ( - - - {trends.categories.length === 0 && trends.severities.length === 0 ? ( -

- No categorized actions in the last 30 days. -

- ) : ( -
-
-

- Top flagged categories -

-
- {trends.categories.slice(0, 10).map((c) => ( - - ))} - {trends.categories.length === 0 && ( -

- No categories recorded. -

- )} -
-
- -
-
-

- Severity -

- {totalSev > 0 ? ( -
- -
- {trends.severities.map((s) => ( -
- - - {s.level} - - - {formatNumber(s.count)} - -
- ))} -
-
- ) : ( -

No severity data.

- )} -
- -
-

- Action types -

-
- {trends.actions.slice(0, 6).map((a) => ( - - ))} -
-
-
-
- )} -
- ); -} diff --git a/services/frontend/src/components/charts/donut.tsx b/services/frontend/src/components/charts/donut.tsx deleted file mode 100644 index c70018ef..00000000 --- a/services/frontend/src/components/charts/donut.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/** Stacked donut for moderation overview / composition. */ -export function Donut({ - segments, - size = 132, - thickness = 14, - centerLabel, - centerSub, -}: { - segments: { value: number; color: string; label: string }[]; - size?: number; - thickness?: number; - centerLabel?: string; - centerSub?: string; -}) { - const total = segments.reduce((s, x) => s + x.value, 0) || 1; - const r = size / 2 - thickness / 2; - const c = 2 * Math.PI * r; - let offset = 0; - return ( -
- - - {segments.map((s) => { - const len = (s.value / total) * c; - const el = ( - - ); - offset += len; - return el; - })} - - {(centerLabel || centerSub) && ( -
- {centerLabel && ( - {centerLabel} - )} - {centerSub && ( - - {centerSub} - - )} -
- )} -
- ); -} diff --git a/services/frontend/src/components/charts/index.ts b/services/frontend/src/components/charts/index.ts index c121662e..1a052037 100644 --- a/services/frontend/src/components/charts/index.ts +++ b/services/frontend/src/components/charts/index.ts @@ -1,4 +1,2 @@ export { AreaActivity } from "./area-activity"; -export { Donut } from "./donut"; export { RadialGauge } from "./radial-gauge"; -export { Sparkline } from "./sparkline"; diff --git a/services/frontend/src/components/charts/sparkline.tsx b/services/frontend/src/components/charts/sparkline.tsx deleted file mode 100644 index b4564ab2..00000000 --- a/services/frontend/src/components/charts/sparkline.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { cn } from "@/lib/utils"; - -/** Minimal sparkline. Pure SVG, scales to container width. */ -export function Sparkline({ - values, - className, - stroke = "var(--color-signal)", - fill = true, - height = 40, -}: { - values: number[]; - className?: string; - stroke?: string; - fill?: boolean; - height?: number; -}) { - if (values.length === 0) return null; - const w = 100; - const max = Math.max(...values, 1); - const min = Math.min(...values, 0); - const span = max - min || 1; - const pts = values.map((v, i) => { - const x = (i / (values.length - 1)) * w; - const y = height - ((v - min) / span) * (height - 4) - 2; - return [x, y] as const; - }); - const line = pts - .map( - (p, i) => `${i === 0 ? "M" : "L"}${p[0].toFixed(2)},${p[1].toFixed(2)}`, - ) - .join(" "); - const area = `${line} L${w},${height} L0,${height} Z`; - const id = `spark-${stroke.replace(/[^a-z0-9]/gi, "")}`; - return ( - - - - - - - - {fill && } - - - ); -} diff --git a/services/frontend/src/components/primitives/slot.tsx b/services/frontend/src/components/primitives/slot.tsx deleted file mode 100644 index a55dfd60..00000000 --- a/services/frontend/src/components/primitives/slot.tsx +++ /dev/null @@ -1,31 +0,0 @@ -"use client"; - -import * as React from "react"; - -/** - * Minimal Slot — merges its props onto its single child element (Radix-style - * `asChild`). Enough for wrapping / in a Button. - */ -export const Slot = React.forwardRef< - HTMLElement, - React.HTMLAttributes & { children?: React.ReactNode } ->(({ children, ...slotProps }, ref) => { - if (!React.isValidElement(children)) return null; - const childProps = children.props as Record; - const merged: Record = { ...childProps, ...slotProps, ref }; - // Merge className - if (slotProps.className || childProps.className) { - merged.className = [childProps.className, slotProps.className] - .filter(Boolean) - .join(" "); - } - // Merge style - if (slotProps.style || childProps.style) { - merged.style = { - ...(childProps.style as object), - ...(slotProps.style as object), - }; - } - return React.cloneElement(children, merged); -}); -Slot.displayName = "Slot"; diff --git a/services/frontend/src/hooks/use-mobile.ts b/services/frontend/src/hooks/use-mobile.ts deleted file mode 100644 index a93d5839..00000000 --- a/services/frontend/src/hooks/use-mobile.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as React from "react"; - -const MOBILE_BREAKPOINT = 768; - -export function useIsMobile() { - const [isMobile, setIsMobile] = React.useState( - undefined, - ); - - React.useEffect(() => { - const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); - const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - }; - mql.addEventListener("change", onChange); - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - return () => mql.removeEventListener("change", onChange); - }, []); - - return !!isMobile; -} diff --git a/services/frontend/src/lib/hooks/use-mounted.ts b/services/frontend/src/lib/hooks/use-mounted.ts deleted file mode 100644 index 80d43730..00000000 --- a/services/frontend/src/lib/hooks/use-mounted.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useEffect, useState } from "react"; - -/** - * Returns true once the component has mounted on the client. Charts wrapped - * in ResponsiveContainer measure their parent during the first layout — if - * the container has no size yet (hydration/flex/grid), Recharts logs - * "width(-1) and height(-1)" warnings. Deferring the chart render until after - * mount guarantees the container has real dimensions. - */ -export function useMounted(): boolean { - const [mounted, setMounted] = useState(false); - useEffect(() => { - setMounted(true); - }, []); - return mounted; -}