fix: resolve architecture disconnects and codebase weaknesses

- Standardize MessageRecord types — single source of truth from @bete/shared
- Clean up config: remove unused GUILD_ID/TEXT_GUILD_ID/TEXT_CHANNEL_ID, fix WEBSERVER_PORT default (3001), remove default admin password
- Move mascot_chat_messages table to Drizzle schema with proper migration
- Remove runtime DDL (CREATE TABLE IF NOT EXISTS) from mascot-chat repository
- Remove phantom analytics/ module from documentation
- Add better-sqlite3 dependency to root devDependencies
- Replace 'as any' casts with proper type assertions across AI moderation
- Add error logging to silent catch blocks in LLM client
- Apply Biome formatting and import organization

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-09 13:07:01 +07:00
co-authored by Claude Opus 4.8
parent 67d66bb5dd
commit 3614d32701
21 changed files with 206 additions and 150 deletions
+26 -17
View File
@@ -5,8 +5,8 @@
* Individual services re-export from here; they do NOT define their own schemas.
*/
import { ConfigError } from "../errors/index.js";
import { z } from "zod";
import { ConfigError } from "../errors/index.js";
export const configSchema = z
.object({
@@ -18,14 +18,9 @@ export const configSchema = z
MONITOR_GUILD_ID: z.string().min(1).optional(),
// ── Legacy voice ─────────────────────────────────────────────────────
GUILD_ID: z.string().min(1).optional(),
VOICE_GUILD_ID: z.string().min(1).optional(),
VOICE_CHANNEL_ID: z.string().min(1).optional(),
// ── Text capture legacy ──────────────────────────────────────────────
TEXT_GUILD_ID: z.string().min(1).optional(),
TEXT_CHANNEL_ID: z.string().min(1).optional(),
// ── Recording ────────────────────────────────────────────────────────
RECORDINGS_DIR: z.string().default("./recordings"),
RECORDING_SEGMENT_MS: z.coerce.number().positive().default(5000),
@@ -35,7 +30,10 @@ export const configSchema = z
DECODER_COOLDOWN_MS: z.coerce.number().positive().default(30000),
// ── Audio ────────────────────────────────────────────────────────────
AUDIO_STREAM_SILENCE_DURATION_MS: z.coerce.number().positive().default(3000),
AUDIO_STREAM_SILENCE_DURATION_MS: z.coerce
.number()
.positive()
.default(3000),
PACKET_FILTER_MIN_SIZE: z.coerce.number().positive().default(8),
OPUS_FRAME_SIZE: z.coerce.number().positive().default(960),
AUDIO_SAMPLE_RATE: z.coerce.number().positive().default(48000),
@@ -43,7 +41,7 @@ export const configSchema = z
AVATAR_SIZE: z.coerce.number().positive().default(64),
// ── Server ───────────────────────────────────────────────────────────
WEBSERVER_PORT: z.coerce.number().positive().default(3000),
WEBSERVER_PORT: z.coerce.number().positive().default(3001),
NODE_ENV: z
.enum(["development", "production", "test"])
.default("development"),
@@ -55,7 +53,7 @@ export const configSchema = z
.optional()
.transform((v) => v === "true")
.default(false),
ADMIN_PASSWORD: z.string().default("admin123"),
ADMIN_PASSWORD: z.string(),
// ── Database (PostgreSQL) ────────────────────────────────────────────
DATABASE_URL: z.string().optional(),
@@ -104,7 +102,11 @@ export const configSchema = z
AI_LLM_MODEL: z.string().default("text"),
AI_LLM_VISION_MODEL: z.string().optional(),
AI_LLM_MAX_CONCURRENT: z.coerce.number().int().positive().default(5),
AI_LLM_IMAGE_MAX_DIMENSION: z.coerce.number().int().positive().default(1024),
AI_LLM_IMAGE_MAX_DIMENSION: z.coerce
.number()
.int()
.positive()
.default(1024),
AI_LLM_TEXT_BATCH_SIZE: z.coerce.number().int().positive().default(20),
AI_LLM_MEDIA_ANALYSIS_TIMEOUT_MS: z.coerce
.number()
@@ -114,14 +116,21 @@ export const configSchema = z
// ── AI Analysis Timing ──────────────────────────────────────────────
AI_ANALYSIS_DEBOUNCE_MS: z.coerce.number().positive().default(500),
AI_ANALYSIS_RECOVERY_INTERVAL_MS: z.coerce.number().positive().default(15000),
AI_ANALYSIS_RECOVERY_INTERVAL_MS: z.coerce
.number()
.positive()
.default(15000),
AI_ANALYSIS_ERROR_COOLDOWN_MS: z.coerce.number().positive().default(30000),
// ── AI Analysis Batch ───────────────────────────────────────────────
AI_ANALYSIS_MAX_BATCH_SIZE: z.coerce.number().int().positive().default(200),
AI_ANALYSIS_MAX_CONTEXT_TOKENS: z.coerce.number().positive().default(8000),
AI_ANALYSIS_MAX_TARGET_TOKENS: z.coerce.number().positive().default(4000),
AI_ANALYSIS_CONTEXT_MESSAGE_LIMIT: z.coerce.number().int().positive().default(20),
AI_ANALYSIS_CONTEXT_MESSAGE_LIMIT: z.coerce
.number()
.int()
.positive()
.default(20),
AI_ANALYSIS_PROCESSING_TIMEOUT_MS: z.coerce
.number()
.positive()
@@ -159,7 +168,9 @@ export const configSchema = z
.default(false),
AUTO_DELETE_FLAGGED_DELAY_MS: z.coerce.number().min(0).default(0),
AUTO_DELETE_MIN_CONFIDENCE: z.coerce.number().min(0).max(1).default(0.5),
AUTO_DELETE_ALLOWED_SEVERITIES: z.string().default("critical,high,medium,low"),
AUTO_DELETE_ALLOWED_SEVERITIES: z
.string()
.default("critical,high,medium,low"),
AUTO_DELETE_ALLOWED_CATEGORIES: z.string().default(""),
AUTO_DELETE_EXCLUDED_CHANNEL_IDS: z.string().default(""),
AUTO_DELETE_EXCLUDED_USER_IDS: z.string().default(""),
@@ -215,15 +226,13 @@ export type AppConfig = z.infer<typeof configSchema> & {
EFFECTIVE_VOICE_GUILD_ID?: string;
};
export function loadConfig(
env: NodeJS.ProcessEnv = process.env,
): AppConfig {
export function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig {
try {
const parsed = configSchema.parse(env);
return {
...parsed,
EFFECTIVE_TEXT_GUILD_ID: parsed.MONITOR_GUILD_ID,
EFFECTIVE_VOICE_GUILD_ID: parsed.VOICE_GUILD_ID ?? parsed.GUILD_ID,
EFFECTIVE_VOICE_GUILD_ID: parsed.VOICE_GUILD_ID,
};
} catch (error) {
if (error instanceof z.ZodError) {
+2 -2
View File
@@ -1,5 +1,5 @@
export * from "./config/index.js";
export * from "./errors/index.js";
export * from "./logger/index.js";
export * from "./utils/index.js";
export * from "./moderation-types.js";
export * from "./config/index.js";
export * from "./utils/index.js";