refactor: break monorepo into 3 standalone services (gateway, backend, frontend)
Build & Deploy / build-and-push (backend) (push) Failing after 35s
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 25s
Build & Deploy / build-and-push (proxy) (push) Failing after 25s

- Remove pnpm workspace, moon repo, and all monorepo tooling
- Delete packages/shared/, embed shared code directly into each service
- Copy packages/shared/src/* -> services/backend/src/shared/ and services/discord-gateway/src/shared/
- Replace all @bete/shared imports with @/shared/ path alias
- Remove @bete/shared workspace dependency from both services
- Update root package.json scripts from --filter to --prefix
- Rewrite Dockerfiles to build each service standalone
- Clean up biome.json, .gitignore, remove root drizzle.config.ts
This commit is contained in:
Developer
2026-07-30 11:50:48 +07:00
parent 8eb7fa49e4
commit dcd13482c2
167 changed files with 1886 additions and 533 deletions
-3
View File
@@ -8,9 +8,6 @@ public/app/
.env.test .env.test
logs/ logs/
.codegraph/ .codegraph/
# moon
.moon/cache
.moon/docker
worktrees/ worktrees/
.worktrees/ .worktrees/
services/frontend/frontend/dist/ services/frontend/frontend/dist/
-34
View File
@@ -1,34 +0,0 @@
$schema: 'https://moonrepo.dev/schema/config/v2.json'
# Global task templates — each project must explicitly inherit via workspace.inheritedTasks
# In moon v2, these define reusable task templates
tasks:
lint:
command: biome
args:
- check
- '--diagnostic-level=error'
- .
options:
runFromWorkspaceRoot: false
inputs:
- 'src/**/*'
- '*.js'
- '*.mjs'
- '*.ts'
- '*.mts'
format:
command: biome
args:
- format
- '--write'
- .
options:
runFromWorkspaceRoot: false
inputs:
- 'src/**/*'
- '*.js'
- '*.mjs'
- '*.ts'
- '*.mts'
-10
View File
@@ -1,10 +0,0 @@
$schema: 'https://moonrepo.dev/schema/config/v2.json'
# Moon Workspace Configuration
projects:
- 'services/*'
- 'packages/*'
vcs:
defaultBranch: main
provider: github
-2
View File
@@ -11,13 +11,11 @@
"tests/**/*.ts", "tests/**/*.ts",
"services/**/*.ts", "services/**/*.ts",
"services/**/*.tsx", "services/**/*.tsx",
"packages/**/*.ts",
"*.json", "*.json",
"*.ts", "*.ts",
"!vendor", "!vendor",
"!.claude", "!.claude",
"!services/**/dist", "!services/**/dist",
"!packages/**/dist",
"!services/**/.next", "!services/**/.next",
"!services/**/out" "!services/**/out"
] ]
-18
View File
@@ -1,18 +0,0 @@
import { defineConfig } from "drizzle-kit";
const databaseUrl = process.env.DATABASE_URL;
export default defineConfig({
schema: "./src/database/schema.ts",
out: "./drizzle/migrations",
dialect: "postgresql",
dbCredentials: databaseUrl
? { url: databaseUrl }
: {
host: process.env.POSTGRES_HOST || "localhost",
port: parseInt(process.env.POSTGRES_PORT || "5432", 10),
user: process.env.POSTGRES_USER || "postgres",
password: process.env.POSTGRES_PASSWORD || "",
database: process.env.POSTGRES_DB || "moderation_bot",
},
});
+6 -21
View File
@@ -2,12 +2,6 @@ FROM node:22-slim
WORKDIR /app WORKDIR /app
# Build args for frontend API URLs
ARG VITE_BE_API_URL
ARG VITE_BE_WS_URL
ENV VITE_BE_API_URL=${VITE_BE_API_URL}
ENV VITE_BE_WS_URL=${VITE_BE_WS_URL}
# Install pnpm # Install pnpm
RUN npm install -g pnpm RUN npm install -g pnpm
@@ -18,30 +12,21 @@ RUN apt-get update -qq && apt-get install -y -qq --no-install-recommends python3
RUN groupadd --system app && useradd --system -g app app RUN groupadd --system app && useradd --system -g app app
# Copy dependency definition files first for better caching # Copy dependency definition files first for better caching
COPY pnpm-workspace.yaml . COPY services/backend/package.json services/backend/pnpm-lock.yaml* ./services/backend/
COPY pnpm-lock.yaml .
COPY package.json .
# Copy patches (pnpm patchedDependencies) # Copy patches (pnpm patchedDependencies)
COPY patches ./patches COPY patches ./patches
# Copy packages (workspace dependencies) # Copy service source (shared code is embedded in src/shared/)
COPY packages/shared ./packages/shared COPY services/backend/ ./services/backend/
# Copy service # Install dependencies
COPY services/backend ./services/backend
# Install dependencies with build cache
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \ RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile cd services/backend && pnpm install --frozen-lockfile
# Build shared workspace dependency first
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm --filter './packages/shared' run build
# Build backend # Build backend
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \ RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm --filter './services/backend' run build cd services/backend && pnpm run build
# Own dist + node_modules by non-root user # Own dist + node_modules by non-root user
RUN chown -R app:app /app RUN chown -R app:app /app
+8 -26
View File
@@ -13,40 +13,23 @@ RUN apt-get update -qq && apt-get install -y -qq --no-install-recommends \
ENV PATH="/root/.cargo/bin:${PATH}" ENV PATH="/root/.cargo/bin:${PATH}"
# Dependency manifests # Dependency manifests
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ COPY services/discord-gateway/package.json services/discord-gateway/pnpm-lock.yaml* ./services/discord-gateway/
COPY patches ./patches COPY patches ./patches
COPY packages/shared/package.json ./packages/shared/package.json
COPY services/discord-gateway/package.json ./services/discord-gateway/package.json
# Install ALL deps (including devDeps needed for build) # Install ALL deps (including devDeps needed for build)
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \ RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile cd services/discord-gateway && pnpm install --frozen-lockfile
# Source code # Source code (shared code is embedded in src/shared/)
COPY packages/shared ./packages/shared
COPY services/discord-gateway ./services/discord-gateway COPY services/discord-gateway ./services/discord-gateway
COPY services/discord-gateway/drizzle ./drizzle COPY services/discord-gateway/drizzle ./drizzle
# Build (combined to reduce layers) # Build
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \ RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm --filter './packages/shared' run build \ cd services/discord-gateway && pnpm run build \
&& pnpm --filter './services/discord-gateway' run build \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Create production-only deployment (no devDeps)
RUN pnpm deploy --legacy --filter '@bete/discord-gateway' /tmp/deploy \
&& rm -rf /tmp/deploy/node_modules/.pnpm/@biomejs+biome@* \
/tmp/deploy/node_modules/.pnpm/@biomejs+cli-linux-x64@* \
/tmp/deploy/node_modules/.pnpm/typescript@* \
/tmp/deploy/node_modules/.pnpm/drizzle-kit@* \
/tmp/deploy/node_modules/.pnpm/vitest@* \
/tmp/deploy/node_modules/.pnpm/esbuild@* \
/tmp/deploy/node_modules/.pnpm/tsx@* \
/tmp/deploy/node_modules/.pnpm/@types* \
/tmp/deploy/node_modules/.pnpm/@rolldown* \
/tmp/deploy/node_modules/.pnpm/@vitest*
# ---- Stage 2: Runtime (minimal) ---- # ---- Stage 2: Runtime (minimal) ----
FROM node:22-slim FROM node:22-slim
@@ -57,11 +40,10 @@ RUN apt-get update -qq && apt-get install -y -qq --no-install-recommends \
ffmpeg ca-certificates \ ffmpeg ca-certificates \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Copy production-only artifacts from deploy # Copy built artifacts
COPY --from=builder /tmp/deploy/node_modules ./node_modules
COPY --from=builder /tmp/deploy/package.json ./package.json
COPY --from=builder /app/packages/shared/dist ./packages/shared/dist
COPY --from=builder /app/services/discord-gateway/dist ./services/discord-gateway/dist COPY --from=builder /app/services/discord-gateway/dist ./services/discord-gateway/dist
COPY --from=builder /app/services/discord-gateway/node_modules ./services/discord-gateway/node_modules
COPY --from=builder /app/services/discord-gateway/package.json ./services/discord-gateway/package.json
COPY --from=builder /app/drizzle ./drizzle COPY --from=builder /app/drizzle ./drizzle
# Create recordings dir and set ownership # Create recordings dir and set ownership
+5 -13
View File
@@ -11,20 +11,14 @@ RUN apt-get update -qq && apt-get install -y -qq --no-install-recommends \
python3 make g++ && rm -rf /var/lib/apt/lists/* python3 make g++ && rm -rf /var/lib/apt/lists/*
# Copy dependency manifests first for layer caching # Copy dependency manifests first for layer caching
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ COPY services/frontend/package.json services/frontend/pnpm-lock.yaml* ./services/frontend/
COPY packages/shared/package.json ./packages/shared/package.json
COPY services/frontend/package.json ./services/frontend/package.json
COPY services/frontend/tsconfig.json ./services/frontend/tsconfig.json COPY services/frontend/tsconfig.json ./services/frontend/tsconfig.json
# Copy patches (pnpm patchedDependencies) # Install frontend dependencies (standalone, no shared workspace dep)
COPY patches/ ./patches/
# Install dependencies (frontend + shared)
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \ RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm install --no-frozen-lockfile --filter './packages/shared' --filter './services/frontend' cd services/frontend && pnpm install --no-frozen-lockfile
# Copy source code # Copy source code
COPY packages/shared/ ./packages/shared/
COPY services/frontend/ ./services/frontend/ COPY services/frontend/ ./services/frontend/
# Pass API/WS URLs as build args # Pass API/WS URLs as build args
@@ -33,11 +27,9 @@ ARG VITE_BE_WS_URL
ENV VITE_BE_API_URL=${VITE_BE_API_URL} ENV VITE_BE_API_URL=${VITE_BE_API_URL}
ENV VITE_BE_WS_URL=${VITE_BE_WS_URL} ENV VITE_BE_WS_URL=${VITE_BE_WS_URL}
# Build shared lib first, then frontend static export # Build frontend static export
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \ RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm --filter './packages/shared' run build cd services/frontend && pnpm run build
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm --filter frontend run build
# ---- Stage 2: Nginx ---- # ---- Stage 2: Nginx ----
FROM nginx:alpine FROM nginx:alpine
+11 -18
View File
@@ -1,31 +1,24 @@
{ {
"name": "discord-voice-recorder", "name": "bete-monorepo",
"version": "1.0.0", "version": "1.0.0",
"description": "Discord bot that joins a voice channel and records audio", "private": true,
"type": "module", "description": "Bete Discord Moderation Services — standalone services in one repo",
"packageManager": "pnpm@11.1.3",
"scripts": { "scripts": {
"dev:backend": "pnpm --filter './services/backend' run dev", "dev:backend": "pnpm --prefix services/backend dev",
"dev:discord-gateway": "pnpm --filter './services/discord-gateway' run dev", "dev:discord-gateway": "pnpm --prefix services/discord-gateway dev",
"dev:web": "pnpm --filter './services/frontend' run dev", "dev:web": "pnpm --prefix services/frontend dev",
"build:web": "pnpm --filter './services/frontend' run build", "build:web": "pnpm --prefix services/frontend build",
"typecheck:web": "pnpm --filter './services/frontend' run build", "build:backend": "pnpm --prefix services/backend build",
"build:backend": "pnpm --filter './services/backend' run build", "build:discord-gateway": "pnpm --prefix services/discord-gateway build",
"build:discord-gateway": "pnpm --filter './services/discord-gateway' run build", "typecheck": "pnpm --prefix services/backend typecheck && pnpm --prefix services/discord-gateway typecheck",
"typecheck": "pnpm -r run typecheck",
"lint": "biome check --diagnostic-level=error .", "lint": "biome check --diagnostic-level=error .",
"format": "biome format --write .", "format": "biome format --write .",
"test": "pnpm -r run test", "test": "pnpm --prefix services/backend test && pnpm --prefix services/discord-gateway test",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio",
"db:truncate": "psql \"$DATABASE_URL\" -f scripts/truncate-all.sql", "db:truncate": "psql \"$DATABASE_URL\" -f scripts/truncate-all.sql",
"install:yt-dlp": "sh scripts/install-yt-dlp.sh" "install:yt-dlp": "sh scripts/install-yt-dlp.sh"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.5.5", "@biomejs/biome": "^2.5.5",
"drizzle-kit": "^0.31.10",
"tsx": "^4.22.2",
"typescript": "^5.9.3" "typescript": "^5.9.3"
} }
} }
-12
View File
@@ -1,12 +0,0 @@
$schema: 'https://moonrepo.dev/schema/config/v2.json'
language: typescript
tasks:
typecheck:
command: tsc --noEmit
options:
runFromWorkspaceRoot: false
inputs:
- src
- tsconfig.json
-36
View File
@@ -1,36 +0,0 @@
{
"name": "@bete/shared",
"version": "1.0.0",
"description": "Shared utilities, types, and errors for Bete microservices",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./config": "./dist/config/index.js",
"./database/pool": "./dist/database/pool.js",
"./database/schema": "./dist/database/schema.js",
"./database/init": "./dist/database/init.js",
"./errors": "./dist/errors/index.js",
"./logger": "./dist/logger/index.js",
"./moderation-types": "./dist/moderation-types.js",
"./redis-channels": "./dist/redis-channels.js",
"./utils": "./dist/utils/index.js"
},
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"drizzle-orm": "^0.45.2",
"pg": "^8.13.0",
"pino": "^9.0.0",
"zod": "^4.4.3"
},
"devDependencies": {
"@types/node": "^25.9.0",
"@types/pg": "^8.11.0",
"pino-pretty": "^13.1.3",
"typescript": "^5.9.3"
}
}
-19
View File
@@ -1,19 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "bundler"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
-44
View File
@@ -1,44 +0,0 @@
packages:
- services/*
- packages/*
allowBuilds:
'@discordjs/opus': true
'@lng2004/node-datachannel': true
esbuild: true
node-av: true
node-crc: true
sharp: true
zeromq: true
minimumReleaseAgeExclude:
- '@types/node@25.9.0'
- pg-cloudflare@1.4.0
- pg-connection-string@2.13.0
- pg-pool@3.14.0
- pg-protocol@1.14.0
- pg@8.21.0
- tsx@4.22.2
- '@next/env@16.2.12'
- '@next/swc-darwin-arm64@16.2.12'
- '@next/swc-darwin-x64@16.2.12'
- '@next/swc-linux-arm64-gnu@16.2.12'
- '@next/swc-linux-arm64-musl@16.2.12'
- '@next/swc-linux-x64-gnu@16.2.12'
- '@next/swc-linux-x64-musl@16.2.12'
- '@next/swc-win32-arm64-msvc@16.2.12'
- '@next/swc-win32-x64-msvc@16.2.12'
- lucide-react@1.27.0
- next@16.2.12
- shadcn@4.15.0
onlyBuiltDependencies:
- '@discordjs/opus'
- '@lng2004/node-datachannel'
- esbuild
- node-av
- sharp
- zeromq
patchedDependencies:
node-crc@4.0.0: patches/node-crc@4.0.0.patch
-48
View File
@@ -1,48 +0,0 @@
# Backend service - Discord moderation monitoring
language: typescript
tasks:
dev:
command: tsx watch src/index.ts
preset: server
build:
command: tsc
options:
runFromWorkspaceRoot: false
inputs:
- src
- tsconfig.json
outputs:
- dist
typecheck:
command: tsc --noEmit
options:
runFromWorkspaceRoot: false
inputs:
- src
- tsconfig.json
lint:
command: biome check --diagnostic-level=error .
options:
runFromWorkspaceRoot: false
inputs:
- src
format:
command: biome format --write .
options:
runFromWorkspaceRoot: false
inputs:
- src
test:
command: vitest run
options:
runFromWorkspaceRoot: false
inputs:
- src
- tests
- tsconfig.json
-1
View File
@@ -14,7 +14,6 @@
"test": "vitest run" "test": "vitest run"
}, },
"dependencies": { "dependencies": {
"@bete/shared": "workspace:*",
"@discordjs/voice": "^0.19.2", "@discordjs/voice": "^0.19.2",
"axios": "^1.16.1", "axios": "^1.16.1",
"dotenv": "^17.4.2", "dotenv": "^17.4.2",
+1 -1
View File
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import express, { import express, {
type Express, type Express,
type NextFunction, type NextFunction,
+1 -1
View File
@@ -1,5 +1,5 @@
import { createServer, type Server } from "node:http"; import { createServer, type Server } from "node:http";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../shared/config/index.js"; import { config } from "../shared/config/index.js";
import { initializeDatabase } from "../shared/database/index.js"; import { initializeDatabase } from "../shared/database/index.js";
import { startRedisBridge } from "../ws/redis-bridge.js"; import { startRedisBridge } from "../ws/redis-bridge.js";
+1 -1
View File
@@ -1,5 +1,5 @@
import type { Server } from "node:http"; import type { Server } from "node:http";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { startHttpServer } from "./http/server.js"; import { startHttpServer } from "./http/server.js";
import { closeDatabase } from "./shared/database/index.js"; import { closeDatabase } from "./shared/database/index.js";
import { stopCommandBridge } from "./shared/redis/index.js"; import { stopCommandBridge } from "./shared/redis/index.js";
@@ -1,5 +1,5 @@
import { pgMessagesTable } from "@bete/shared"; import { pgMessagesTable } from "../../shared/index.js";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "../../shared/logger/index.js";
import { and, desc, eq, ilike, type SQL } from "drizzle-orm"; import { and, desc, eq, ilike, type SQL } from "drizzle-orm";
import { getDatabase } from "../../shared/database/index.js"; import { getDatabase } from "../../shared/database/index.js";
import { import {
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response, Router } from "express"; import type { Request, Response, Router } from "express";
import express from "express"; import express from "express";
import { asyncHandler } from "../../shared/middlewares/index.js"; import { asyncHandler } from "../../shared/middlewares/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/index.js"; import { config } from "../../shared/config/index.js";
import type { AnalysisSearchQuery } from "./analysis.repository.js"; import type { AnalysisSearchQuery } from "./analysis.repository.js";
import { analysisRepository } from "./analysis.repository.js"; import { analysisRepository } from "./analysis.repository.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { asyncHandler } from "../../shared/middlewares/index.js"; import { asyncHandler } from "../../shared/middlewares/index.js";
import { chatbotService } from "./chatbot.service.js"; import { chatbotService } from "./chatbot.service.js";
@@ -1,5 +1,5 @@
import { pgChatbotMessagesTable, pgMessagesTable } from "@bete/shared"; import { pgChatbotMessagesTable, pgMessagesTable } from "../../shared/index.js";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "../../shared/logger/index.js";
import { and, desc, eq, type SQL, sql } from "drizzle-orm"; import { and, desc, eq, type SQL, sql } from "drizzle-orm";
import { getDatabase } from "../../shared/database/index.js"; import { getDatabase } from "../../shared/database/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/index.js"; import { config } from "../../shared/config/index.js";
import type { import type {
ChatbotContext, ChatbotContext,
@@ -4,7 +4,7 @@ import {
pgUserProfilesTable, pgUserProfilesTable,
pgUserReputationsTable, pgUserReputationsTable,
pgVoiceRecordingsTable, pgVoiceRecordingsTable,
} from "@bete/shared"; } from "../../shared/index.js";
import type { SQL } from "drizzle-orm"; import type { SQL } from "drizzle-orm";
import { sql } from "drizzle-orm"; import { sql } from "drizzle-orm";
import { getDatabase } from "../../shared/database/index.js"; import { getDatabase } from "../../shared/database/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response, Router } from "express"; import type { Request, Response, Router } from "express";
import express from "express"; import express from "express";
import { asyncHandler } from "../../shared/middlewares/index.js"; import { asyncHandler } from "../../shared/middlewares/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { dashboardRepository } from "./dashboard.repository.js"; import { dashboardRepository } from "./dashboard.repository.js";
const logger = createChildLogger("dashboard.service"); const logger = createChildLogger("dashboard.service");
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { sql } from "drizzle-orm"; import { sql } from "drizzle-orm";
import { getDatabase } from "../../shared/database/index.js"; import { getDatabase } from "../../shared/database/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response, Router } from "express"; import type { Request, Response, Router } from "express";
import express from "express"; import express from "express";
import { asyncHandler, validateBody } from "../../shared/middlewares/index.js"; import { asyncHandler, validateBody } from "../../shared/middlewares/index.js";
@@ -4,7 +4,7 @@ import {
COMMAND_MEDIA_STOP, COMMAND_MEDIA_STOP,
COMMAND_MEDIA_VOLUME, COMMAND_MEDIA_VOLUME,
MEDIA_STATUS_KEY, MEDIA_STATUS_KEY,
} from "@bete/shared"; } from "../../shared/index.js";
import { import {
createChildLogger, createChildLogger,
tryCommandThenFallback, tryCommandThenFallback,
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { asyncHandler } from "../../shared/middlewares/index.js"; import { asyncHandler } from "../../shared/middlewares/index.js";
import { messageQuerySchema } from "./messages.schema.js"; import { messageQuerySchema } from "./messages.schema.js";
@@ -1,6 +1,6 @@
import type { PageResult } from "@bete/shared"; import type { PageResult } from "../../shared/index.js";
import { pgAttachmentsTable, pgMessagesTable } from "@bete/shared"; import { pgAttachmentsTable, pgMessagesTable } from "../../shared/index.js";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "../../shared/logger/index.js";
import { import {
and, and,
desc, desc,
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response, Router } from "express"; import type { Request, Response, Router } from "express";
import express from "express"; import express from "express";
import { asyncHandler, validateBody } from "../../shared/middlewares/index.js"; import { asyncHandler, validateBody } from "../../shared/middlewares/index.js";
@@ -1,5 +1,5 @@
import { NotFoundError, ValidationError } from "@bete/shared/errors"; import { NotFoundError, ValidationError } from "@/shared/errors/index";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { messagesRepository } from "./messages.repository.js"; import { messagesRepository } from "./messages.repository.js";
import type { MessageQuery } from "./messages.schema.js"; import type { MessageQuery } from "./messages.schema.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response, Router } from "express"; import type { Request, Response, Router } from "express";
import express from "express"; import express from "express";
import { asyncHandler } from "../../shared/middlewares/index.js"; import { asyncHandler } from "../../shared/middlewares/index.js";
@@ -1,5 +1,5 @@
import { pgVoiceRecordingsTable } from "@bete/shared"; import { pgVoiceRecordingsTable } from "../../shared/index.js";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "../../shared/logger/index.js";
import { and, desc, eq, lt, type SQL } from "drizzle-orm"; import { and, desc, eq, lt, type SQL } from "drizzle-orm";
import { getDatabase } from "../../shared/database/index.js"; import { getDatabase } from "../../shared/database/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response, Router } from "express"; import type { Request, Response, Router } from "express";
import express from "express"; import express from "express";
import { asyncHandler } from "../../shared/middlewares/index.js"; import { asyncHandler } from "../../shared/middlewares/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { sql } from "drizzle-orm"; import { sql } from "drizzle-orm";
import { getDatabase } from "../../shared/database/index.js"; import { getDatabase } from "../../shared/database/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { asyncHandler } from "../../shared/middlewares/index.js"; import { asyncHandler } from "../../shared/middlewares/index.js";
import { publishCommandNoReply } from "../../shared/redis/index.js"; import { publishCommandNoReply } from "../../shared/redis/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Request, Response, Router } from "express"; import type { Request, Response, Router } from "express";
import express from "express"; import express from "express";
import { asyncHandler, validateBody } from "../../shared/middlewares/index.js"; import { asyncHandler, validateBody } from "../../shared/middlewares/index.js";
@@ -7,7 +7,7 @@ import {
type CommandReply, type CommandReply,
pgMessagesTable, pgMessagesTable,
VOICE_STATUS_KEY, VOICE_STATUS_KEY,
} from "@bete/shared"; } from "../../shared/index.js";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { import {
createChildLogger, createChildLogger,
+2 -2
View File
@@ -1,5 +1,5 @@
import type { CommandReply } from "@bete/shared"; import type { CommandReply } from "./index.js";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "./logger/index.js";
export { createChildLogger }; export { createChildLogger };
+310 -3
View File
@@ -1,4 +1,311 @@
import "dotenv/config"; /**
import { config as sharedConfig } from "@bete/shared/config"; * Unified configuration schema shared by all services.
*
* This is the single source of truth for all environment variables.
* Individual services re-export from here; they do NOT define their own schemas.
*/
export const config = sharedConfig; import { z } from "zod";
import { ConfigError } from "../errors/index.js";
export const configSchema = z
.object({
// ── Discord ──────────────────────────────────────────────────────────
DISCORD_TOKEN: z
.string()
.min(1, "DISCORD_TOKEN is required")
.transform((value) => value.replace(/^("|')|(?:("|'))$/g, "")),
MONITOR_GUILD_IDS: z
.string()
.default("")
.transform((v) => v.split(",").filter(Boolean)),
MONITOR_GUILD_ID: z.string().min(1).optional(),
TEXT_GUILD_ID: z.string().min(1).optional(),
TEXT_CHANNEL_ID: z.string().min(1).optional(),
EXCLUDED_CHANNEL_IDS: z
.string()
.default("")
.transform((v) => v.split(",").filter(Boolean))
.describe("Channel IDs to exclude from capture"),
EXCLUDED_THREAD_IDS: z
.string()
.default("")
.transform((v) => v.split(",").filter(Boolean))
.describe("Thread IDs to exclude from capture"),
// ── Legacy voice ─────────────────────────────────────────────────────
VOICE_GUILD_ID: z.string().min(1).optional(),
VOICE_CHANNEL_ID: z.string().min(1).optional(),
// ── Recording ────────────────────────────────────────────────────────
RECORDINGS_DIR: z.string().default("./recordings"),
RECORDING_SEGMENT_MS: z.coerce.number().positive().default(5000),
// ── Decoder ──────────────────────────────────────────────────────────
DECODER_ROTATE_MS: z.coerce.number().positive().default(5000),
DECODER_COOLDOWN_MS: z.coerce.number().positive().default(30000),
// ── Audio ────────────────────────────────────────────────────────────
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),
AUDIO_CHANNELS: z.coerce.number().positive().default(2),
AVATAR_SIZE: z.coerce.number().positive().default(64),
// ── Server ───────────────────────────────────────────────────────────
WEBSERVER_PORT: z.coerce.number().positive().default(3001),
NODE_ENV: z
.enum(["development", "production", "test"])
.default("development"),
LOG_LEVEL: z
.enum(["error", "warn", "info", "http", "verbose", "debug", "silly"])
.default("info"),
VERBOSE: z
.string()
.optional()
.transform((v) => v === "true")
.default(false),
ADMIN_PASSWORD: z.string().default("admin123"),
WEBHOOK_URLS: z
.string()
.default("")
.transform((v) => v.split(",").filter(Boolean)),
WEBHOOK_EVENTS: z
.string()
.default("message_flagged,auto_deleted,high_severity")
.transform((v) => v.split(",").filter(Boolean)),
METRICS_PORT: z.coerce.number().positive().default(9090),
// ── Database (PostgreSQL) ────────────────────────────────────────────
DATABASE_URL: z.string().optional(),
POSTGRES_HOST: z.string().default("localhost"),
POSTGRES_PORT: z.coerce.number().int().positive().default(5432),
POSTGRES_USER: z.string().optional(),
POSTGRES_PASSWORD: z.string().optional(),
POSTGRES_DB: z.string().optional(),
POSTGRES_POOL_MIN: z.coerce.number().int().positive().default(2),
POSTGRES_POOL_MAX: z.coerce.number().int().positive().default(10),
// ── Redis ────────────────────────────────────────────────────────────
REDIS_URL: z.string().default("redis://localhost:6379"),
// ── Voice PCM WebSocket (direct gateway→backend, bypasses Redis) ────
VOICE_PCM_WS_ENABLED: z
.string()
.optional()
.transform((v) => v === "true")
.default(true),
BACKEND_WS_URL: z.string().default("ws://backend:3000/ws"),
BACKEND_WS_TOKEN: z.string().optional().default(""),
// ── Connection ───────────────────────────────────────────────────────
VOICE_CONNECTION_TIMEOUT_MS: z.coerce.number().positive().default(15000),
RECONNECT_TIMEOUT_MS: z.coerce.number().positive().default(5000),
// ── Attachments ─────────────────────────────────────────────────────
TELE_UPLOAD_URL: z
.string()
.url()
.default("https://upload.asepharyana.my.id/api/upload"),
ATTACHMENT_UPLOAD_TIMEOUT_MS: z.coerce.number().positive().default(30000),
ATTACHMENT_MAX_SIZE_MB: z.coerce.number().positive().default(100),
ATTACHMENT_RETRY_ATTEMPTS: z.coerce.number().positive().default(3),
BACKLOG_SYNC_HOURS: z.coerce.number().positive().default(24),
BACKLOG_SYNC_BATCH_SIZE: z.coerce
.number()
.int()
.positive()
.max(100)
.default(100),
// ── AI Analysis ─────────────────────────────────────────────────────
AI_ANALYSIS_ENABLED: z
.string()
.optional()
.transform((v) => v === "true")
.default(false),
AI_LLM_API_KEY: z.string().optional(),
AI_LLM_BASE_URL: z
.string()
.url()
.default("https://9router.asepharyana.my.id/v1"),
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_TEXT_BATCH_SIZE: z.coerce.number().int().positive().default(20),
AI_LLM_MEDIA_ANALYSIS_TIMEOUT_MS: z.coerce
.number()
.int()
.positive()
.default(60000),
// ── AI Model (new unified keys) ───────────────────────────────────
AI_MODEL_FAST_CLASSIFIER_ENABLED: z
.string()
.optional()
.transform((v) => v === "true")
.default(true)
.describe("Enable Layer 1 fast heuristic classifier"),
AI_MODEL_LLM_TIMEOUT_MS: z.coerce
.number()
.int()
.positive()
.default(30000)
.describe("Timeout for individual LLM moderation calls"),
// ── 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_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_PROCESSING_TIMEOUT_MS: z.coerce
.number()
.positive()
.default(120000),
AI_ANALYSIS_INDIVIDUAL_MAX_CONCURRENT: z.coerce
.number()
.int()
.positive()
.default(50),
AI_ANALYSIS_INDIVIDUAL_CB_THRESHOLD: z.coerce
.number()
.int()
.positive()
.default(50),
PISCINA_MAX_THREADS: z.coerce.number().int().positive().optional(),
// ── Voice Transcription ────────────────────────────────────────────────
AI_VOICE_TRANSCRIPTION_ENABLED: z
.string()
.optional()
.transform((v) => v === "true")
.default(false),
// ── OpenAI Moderation ───────────────────────────────────────────────
OPENAI_MODERATION_API_KEY: z.string().optional(),
OPENAI_MODERATION_BASE_URL: z
.string()
.url()
.default("https://api.openai.com/v1"),
OPENAI_MODERATION_MODEL: z.string().default("omni-moderation-latest"),
// ── Auto Delete ─────────────────────────────────────────────────────
AUTO_DELETE_FLAGGED_ENABLED: z
.string()
.optional()
.transform((v) => v === "true")
.default(true),
AUTO_DELETE_FLAGGED_DRY_RUN: z
.string()
.optional()
.transform((v) => v === "true")
.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_CATEGORIES: z.string().default(""),
AUTO_DELETE_EXCLUDED_CHANNEL_IDS: z.string().default(""),
AUTO_DELETE_EXCLUDED_USER_IDS: z.string().default(""),
AUTO_DELETE_NOTIFY_USER: z
.string()
.optional()
.transform((v) => v === "true")
.default(false),
AUTO_DELETE_LOG_CHANNEL_ID: z.string().default(""),
// ── Retention ───────────────────────────────────────────────────────
RETENTION_MESSAGES_DAYS: z.coerce.number().int().min(0).default(0),
RETENTION_ATTACHMENTS_DAYS: z.coerce.number().int().min(0).default(0),
RETENTION_VOICE_DAYS: z.coerce.number().int().min(0).default(0),
RETENTION_CLEANUP_INTERVAL_MS: z.coerce
.number()
.positive()
.default(24 * 60 * 60 * 1000),
RETENTION_DRY_RUN: z
.string()
.optional()
.transform((v) => v === "true")
.default(true),
AUTO_MIGRATE_ON_STARTUP: z
.string()
.optional()
.transform((v) => v === "true")
.default(true),
})
.superRefine((value, ctx) => {
if (!value.AI_ANALYSIS_ENABLED) {
// skip: AI analysis not enabled
} else if (!value.AI_LLM_API_KEY) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["AI_LLM_API_KEY"],
message: "AI_LLM_API_KEY is required when AI_ANALYSIS_ENABLED=true",
});
}
// Validate database configuration
if (!value.DATABASE_URL && !value.POSTGRES_HOST) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["DATABASE_URL"],
message: "Either DATABASE_URL or POSTGRES_HOST must be provided",
});
}
});
export type AppConfig = z.infer<typeof configSchema> & {
EFFECTIVE_TEXT_GUILD_ID?: string;
EFFECTIVE_VOICE_GUILD_ID?: string;
EFFECTIVE_MONITOR_GUILD_IDS: string[];
};
export function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig {
try {
const parsed = configSchema.parse(env);
return {
...parsed,
EFFECTIVE_TEXT_GUILD_ID: parsed.TEXT_GUILD_ID ?? parsed.MONITOR_GUILD_ID,
EFFECTIVE_VOICE_GUILD_ID: parsed.VOICE_GUILD_ID,
EFFECTIVE_MONITOR_GUILD_IDS:
parsed.MONITOR_GUILD_IDS.length > 0
? parsed.MONITOR_GUILD_IDS
: parsed.MONITOR_GUILD_ID
? [parsed.MONITOR_GUILD_ID]
: [],
};
} catch (error) {
if (error instanceof z.ZodError) {
const messages = error.issues
.map((e) => `${e.path.join(".")}: ${e.message}`)
.join("\n");
throw new ConfigError(`Configuration validation failed:\n${messages}`);
}
throw error;
}
}
/** Singleton config loaded from process.env at import time. */
export const config = loadConfig();
@@ -3,8 +3,8 @@ import {
getDatabase as sharedGetDb, getDatabase as sharedGetDb,
getPool as sharedGetPool, getPool as sharedGetPool,
initializeDatabase as sharedInit, initializeDatabase as sharedInit,
} from "@bete/shared/database/init"; } from "@/shared/database/init";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../config/index.js"; import { config } from "../config/index.js";
const logger = createChildLogger("database"); const logger = createChildLogger("database");
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "../logger/index.js";
import { drizzle } from "drizzle-orm/node-postgres"; import { drizzle } from "drizzle-orm/node-postgres";
import type { Pool, PoolClient } from "pg"; import type { Pool, PoolClient } from "pg";
import { closePool, createPoolFromConfig } from "./pool.js"; import { closePool, createPoolFromConfig } from "./pool.js";
@@ -1,5 +1,5 @@
import { AppError, ValidationError } from "@bete/shared/errors"; import { AppError, ValidationError } from "@/shared/errors/index";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { NextFunction, Request, Response } from "express"; import type { NextFunction, Request, Response } from "express";
import type { ZodSchema } from "zod"; import type { ZodSchema } from "zod";
+2 -2
View File
@@ -4,8 +4,8 @@ import {
BACKEND_COMMAND_REPLY_PREFIX, BACKEND_COMMAND_REPLY_PREFIX,
type CommandMessage, type CommandMessage,
type CommandReply, type CommandReply,
} from "@bete/shared"; } from "../index.js";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "../logger/index.js";
import Redis from "ioredis"; import Redis from "ioredis";
import { config } from "../config/index.js"; import { config } from "../config/index.js";
+1 -1
View File
@@ -9,7 +9,7 @@
* broadcastEvent("message_created", messageData); * broadcastEvent("message_created", messageData);
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
const logger = createChildLogger("broadcast"); const logger = createChildLogger("broadcast");
+2 -2
View File
@@ -1,5 +1,5 @@
import { DISCORD_CHANNEL_TO_WS_EVENT, DISCORD_VOICE_PCM } from "@bete/shared"; import { DISCORD_CHANNEL_TO_WS_EVENT, DISCORD_VOICE_PCM } from "../shared/index.js";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "../shared/logger/index.js";
import Redis from "ioredis"; import Redis from "ioredis";
import { config } from "../shared/config/index.js"; import { config } from "../shared/config/index.js";
import { broadcastBinary, broadcastEvent } from "./broadcast.js"; import { broadcastBinary, broadcastEvent } from "./broadcast.js";
+2 -2
View File
@@ -1,6 +1,6 @@
import type { Server } from "node:http"; import type { Server } from "node:http";
import { BACKEND_COMMAND, BACKEND_VOICE_TRANSMIT } from "@bete/shared"; import { BACKEND_COMMAND, BACKEND_VOICE_TRANSMIT } from "../shared/index.js";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "../shared/logger/index.js";
import { WebSocket, WebSocketServer } from "ws"; import { WebSocket, WebSocketServer } from "ws";
import { config } from "../shared/config/index.js"; import { config } from "../shared/config/index.js";
import { setBroadcastFunctions } from "./broadcast.js"; import { setBroadcastFunctions } from "./broadcast.js";
-48
View File
@@ -1,48 +0,0 @@
# Discord Gateway service - handles message capture, voice recording, and AI moderation
language: typescript
tasks:
dev:
command: tsx watch src/index.ts
preset: server
build:
command: tsc
options:
runFromWorkspaceRoot: false
inputs:
- src
- tsconfig.json
outputs:
- dist
typecheck:
command: tsc --noEmit
options:
runFromWorkspaceRoot: false
inputs:
- src
- tsconfig.json
lint:
command: biome check --diagnostic-level=error .
options:
runFromWorkspaceRoot: false
inputs:
- src
format:
command: biome format --write .
options:
runFromWorkspaceRoot: false
inputs:
- src
test:
command: vitest run
options:
runFromWorkspaceRoot: false
inputs:
- src
- tests
- tsconfig.json
-1
View File
@@ -15,7 +15,6 @@
"test": "vitest run" "test": "vitest run"
}, },
"dependencies": { "dependencies": {
"@bete/shared": "workspace:*",
"@discordjs/opus": "^0.10.0", "@discordjs/opus": "^0.10.0",
"@discordjs/voice": "^0.19.2", "@discordjs/voice": "^0.19.2",
"@snazzah/davey": "^0.1.11", "@snazzah/davey": "^0.1.11",
@@ -1,5 +1,5 @@
import { ConfigError, DatabaseError } from "@bete/shared/errors"; import { ConfigError, DatabaseError } from "@/shared/errors/index";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { Client } from "discord.js-selfbot-v13"; import { Client } from "discord.js-selfbot-v13";
import { inArray, lt } from "drizzle-orm"; import { inArray, lt } from "drizzle-orm";
import type { NodePgDatabase } from "drizzle-orm/node-postgres"; import type { NodePgDatabase } from "drizzle-orm/node-postgres";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { inArray, lt } from "drizzle-orm"; import { inArray, lt } from "drizzle-orm";
import type { NodePgDatabase } from "drizzle-orm/node-postgres"; import type { NodePgDatabase } from "drizzle-orm/node-postgres";
import { config } from "../shared/config/config.js"; import { config } from "../shared/config/config.js";
+1 -1
View File
@@ -1,4 +1,4 @@
import type { createChildLogger } from "@bete/shared/logger"; import type { createChildLogger } from "@/shared/logger/index";
import type { Client } from "discord.js-selfbot-v13"; import type { Client } from "discord.js-selfbot-v13";
import type { CommandHandler } from "../modules/command-handler/commandHandler.js"; import type { CommandHandler } from "../modules/command-handler/commandHandler.js";
import type { EventBroadcaster } from "../modules/event-broadcaster/index.js"; import type { EventBroadcaster } from "../modules/event-broadcaster/index.js";
+1 -1
View File
@@ -1,7 +1,7 @@
import "libsodium-wrappers"; import "libsodium-wrappers";
import "@snazzah/davey"; import "@snazzah/davey";
import "dotenv/config"; import "dotenv/config";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { initializeDiscordGateway } from "./app/bootstrap.js"; import { initializeDiscordGateway } from "./app/bootstrap.js";
const logger = createChildLogger("discord-gateway"); const logger = createChildLogger("discord-gateway");
@@ -17,7 +17,7 @@
* simpler, unified worker that combines both layers. * simpler, unified worker that combines both layers.
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { initializeDatabase } from "../../shared/database/drizzle.js"; import { initializeDatabase } from "../../shared/database/drizzle.js";
import { messageStore } from "../message-capture/messageStore.js"; import { messageStore } from "../message-capture/messageStore.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Client } from "discord.js-selfbot-v13"; import type { Client } from "discord.js-selfbot-v13";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import type { EventBroadcaster } from "../event-broadcaster/index.js"; import type { EventBroadcaster } from "../event-broadcaster/index.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { initializeDatabase } from "../../shared/database/drizzle.js"; import { initializeDatabase } from "../../shared/database/drizzle.js";
import { extractMessageMediaEvidence } from "../message-capture/messageMetadata.js"; import { extractMessageMediaEvidence } from "../message-capture/messageMetadata.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import type { import type {
AnalysisResult, AnalysisResult,
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Guild } from "discord.js-selfbot-v13"; import type { Guild } from "discord.js-selfbot-v13";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import type { MessageRecord } from "../message-capture/types.js"; import type { MessageRecord } from "../message-capture/types.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Client, PermissionString } from "discord.js-selfbot-v13"; import type { Client, PermissionString } from "discord.js-selfbot-v13";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { messageStore } from "../message-capture/messageStore.js"; import { messageStore } from "../message-capture/messageStore.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Client } from "discord.js-selfbot-v13"; import type { Client } from "discord.js-selfbot-v13";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import type { MessageRecord } from "../message-capture/types.js"; import type { MessageRecord } from "../message-capture/types.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { isAgeRestrictedMetadata } from "../message-capture/messageMetadata.js"; import { isAgeRestrictedMetadata } from "../message-capture/messageMetadata.js";
import { messageStore } from "../message-capture/messageStore.js"; import { messageStore } from "../message-capture/messageStore.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { messageStore } from "../message-capture/messageStore.js"; import { messageStore } from "../message-capture/messageStore.js";
import type { MessageRecord } from "../message-capture/types.js"; import type { MessageRecord } from "../message-capture/types.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { getDatabase } from "../../shared/database/drizzle.js"; import { getDatabase } from "../../shared/database/drizzle.js";
import { import {
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { encoding_for_model as encodingForModel } from "tiktoken"; import { encoding_for_model as encodingForModel } from "tiktoken";
import { formatMediaEvidenceForPrompt } from "../message-capture/messageMetadata.js"; import { formatMediaEvidenceForPrompt } from "../message-capture/messageMetadata.js";
import type { MessageRecord } from "../message-capture/types.js"; import type { MessageRecord } from "../message-capture/types.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { LRUCache } from "lru-cache"; import { LRUCache } from "lru-cache";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { LAST_ERROR } from "./moderationState.js"; import { LAST_ERROR } from "./moderationState.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { and, desc, eq, sql } from "drizzle-orm"; import { and, desc, eq, sql } from "drizzle-orm";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { getDatabase } from "../../shared/database/drizzle.js"; import { getDatabase } from "../../shared/database/drizzle.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { LRUCache } from "lru-cache"; import { LRUCache } from "lru-cache";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { messageStore } from "../message-capture/messageStore.js"; import { messageStore } from "../message-capture/messageStore.js";
@@ -9,8 +9,8 @@
* *
* Both sides now import from this module instead. * Both sides now import from this module instead.
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { delay, retryWithBackoff } from "@bete/shared/utils"; import { delay, retryWithBackoff } from "@/shared/utils/index";
import type { ChatCompletion } from "openai/resources/chat/completions"; import type { ChatCompletion } from "openai/resources/chat/completions";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import type { AnalysisResult } from "../message-capture/types.js"; import type { AnalysisResult } from "../message-capture/types.js";
@@ -6,8 +6,8 @@
* defaults are maintained in one place. * defaults are maintained in one place.
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { retryWithBackoff } from "@bete/shared/utils"; import { retryWithBackoff } from "@/shared/utils/index";
import OpenAI from "openai"; import OpenAI from "openai";
import pLimit from "p-limit"; import pLimit from "p-limit";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
@@ -5,7 +5,7 @@
* analysis, and calls the LLM for a batched moderation response. Extracted from * analysis, and calls the LLM for a batched moderation response. Extracted from
* moderationOrchestrator.ts. * moderationOrchestrator.ts.
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import type { import type {
AnalysisResult, AnalysisResult,
@@ -10,8 +10,8 @@ import { mkdtemp, readFile, rm, unlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import path from "node:path"; import path from "node:path";
import { promisify } from "node:util"; import { promisify } from "node:util";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { createAbortControllerWithTimeout } from "@bete/shared/utils"; import { createAbortControllerWithTimeout } from "@/shared/utils/index";
import { resizeImageForVision } from "../attachment-upload/imageResizer.js"; import { resizeImageForVision } from "../attachment-upload/imageResizer.js";
import type { MessageMediaEvidence } from "../message-capture/messageMetadata.js"; import type { MessageMediaEvidence } from "../message-capture/messageMetadata.js";
import type { AttachmentRecord } from "../message-capture/types.js"; import type { AttachmentRecord } from "../message-capture/types.js";
@@ -4,7 +4,7 @@
* Orchestrates LLM-based moderation analysis manages batch splitting, * Orchestrates LLM-based moderation analysis manages batch splitting,
* parallel text+media analysis, LLM calls with retry, and cache handling. * parallel text+media analysis, LLM calls with retry, and cache handling.
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { extractMessageMediaEvidence } from "../message-capture/messageMetadata.js"; import { extractMessageMediaEvidence } from "../message-capture/messageMetadata.js";
import type { import type {
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { z } from "zod"; import type { z } from "zod";
import type { AnalysisResult } from "../message-capture/types.js"; import type { AnalysisResult } from "../message-capture/types.js";
import type { import type {
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { z } from "zod"; import { z } from "zod";
const log = createChildLogger("moderationSchemas"); const log = createChildLogger("moderationSchemas");
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { Client } from "discord.js-selfbot-v13"; import type { Client } from "discord.js-selfbot-v13";
import { LRUCache } from "lru-cache"; import { LRUCache } from "lru-cache";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
@@ -10,7 +10,7 @@
* Enables full audit trail and debugging of moderation decisions. * Enables full audit trail and debugging of moderation decisions.
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { AnalysisResult } from "../message-capture/types.js"; import type { AnalysisResult } from "../message-capture/types.js";
const logger = createChildLogger("response-logger"); const logger = createChildLogger("response-logger");
@@ -1,5 +1,5 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { createAbortControllerWithTimeout } from "@bete/shared/utils"; import { createAbortControllerWithTimeout } from "@/shared/utils/index";
import Redis from "ioredis"; import Redis from "ioredis";
const log = createChildLogger("searxng-search"); const log = createChildLogger("searxng-search");
@@ -6,7 +6,7 @@
* Step 2: Real analysis text (only if not clean). * Step 2: Real analysis text (only if not clean).
* Extracted from moderationOrchestrator.ts. * Extracted from moderationOrchestrator.ts.
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import type { import type {
AnalysisResult, AnalysisResult,
MessageRecord, MessageRecord,
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { executeAll, executeGet } from "../../shared/database/drizzle.js"; import { executeAll, executeGet } from "../../shared/database/drizzle.js";
import { uploadToTele } from "../../shared/uploader.js"; import { uploadToTele } from "../../shared/uploader.js";
@@ -5,7 +5,7 @@
* searches, deduplicates short messages, splits into sub-batches, and calls * searches, deduplicates short messages, splits into sub-batches, and calls
* the LLM for analysis. Extracted from moderationOrchestrator.ts. * the LLM for analysis. Extracted from moderationOrchestrator.ts.
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import type { import type {
AnalysisResult, AnalysisResult,
@@ -1,5 +1,5 @@
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { executeAll, executeGet } from "../../shared/database/drizzle.js"; import { executeAll, executeGet } from "../../shared/database/drizzle.js";
const logger = createChildLogger("text-cache-store"); const logger = createChildLogger("text-cache-store");
@@ -1,7 +1,7 @@
import { resolve } from "node:dns/promises"; import { resolve } from "node:dns/promises";
import { isIP } from "node:net"; import { isIP } from "node:net";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { createAbortControllerWithTimeout } from "@bete/shared/utils"; import { createAbortControllerWithTimeout } from "@/shared/utils/index";
const _log = createChildLogger("urlFetcher"); const _log = createChildLogger("urlFetcher");
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { and, desc, eq, sql } from "drizzle-orm"; import { and, desc, eq, sql } from "drizzle-orm";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { getDatabase } from "../../shared/database/drizzle.js"; import { getDatabase } from "../../shared/database/drizzle.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { getDatabase } from "../../shared/database/drizzle.js"; import { getDatabase } from "../../shared/database/drizzle.js";
import { import {
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { and, desc, eq } from "drizzle-orm"; import { and, desc, eq } from "drizzle-orm";
import { getDatabase } from "../../shared/database/drizzle.js"; import { getDatabase } from "../../shared/database/drizzle.js";
import { import {
@@ -5,8 +5,8 @@
* moderation pipeline, runs single-image vision LLM analysis with * moderation pipeline, runs single-image vision LLM analysis with
* multi-layer caching, and detects whether a message has media content. * multi-layer caching, and detects whether a message has media content.
*/ */
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { delay } from "@bete/shared/utils"; import { delay } from "@/shared/utils/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { extractMessageMediaEvidence } from "../message-capture/messageMetadata.js"; import { extractMessageMediaEvidence } from "../message-capture/messageMetadata.js";
import type { import type {
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import { config } from "../../shared/config/config.js"; import { config } from "../../shared/config/config.js";
import { uploadToTele } from "../../shared/uploader.js"; import { uploadToTele } from "../../shared/uploader.js";
import { messageStore } from "../message-capture/messageStore.js"; import { messageStore } from "../message-capture/messageStore.js";
@@ -1,4 +1,4 @@
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@/shared/logger/index";
import sharp from "sharp"; import sharp from "sharp";
const log = createChildLogger("imageResizer"); const log = createChildLogger("imageResizer");

Some files were not shown because too many files have changed in this diff Show More