feat: Docker audit + Telegram bot service + send UI

Docker:
- Harden docker-compose.yml: parameterized DB creds, required AUTH_SECRET,
  health checks, resource limits, network isolation, removed exposed DB port
- Add profiles (telegram/bot/full) so base 'docker compose up' needs only AUTH_SECRET
- Fix docker-entrypoint.sh: AUTH_SECRET startup guard
- Fix Dockerfile: copy prisma.config.ts + dotenv into production image
- Update .env.example with all new variables
- Update .dockerignore

Telegram Bot Service (bot/):
- TDLib-based bot using bot token auth (not HTTP Bot API)
- Commands: /search, /latest, /package, /link, /unlink, /subscribe, /unsubscribe
- pg_notify listener for send requests (bot_send) and new packages (new_package)
- Subscription-based notifications when matching packages arrive
- Dockerfile with multi-stage build (bookworm-slim for glibc/TDLib)

API & Database:
- Prisma: TelegramLink, BotSendRequest, BotSubscription models + migration
- POST /api/telegram/bot/send - queue package delivery to linked TG account
- GET /api/telegram/bot/send/[id] - poll send request status
- Server actions: generateTelegramLinkCode, unlinkTelegram, getBotSendHistory
- Worker: emit pg_notify('new_package') after creating packages

Frontend:
- Settings: TelegramLinkCard for account linking via one-time code
- STL table + drawer: SendToTelegramButton with send dialog and status polling
- Telegram admin: Bot Sends tab with delivery history table
- Shared SendHistoryRow type

README: Updated with bot docs, profiles, config vars, project structure
This commit is contained in:
2026-03-03 21:36:57 +01:00
parent 4d0df6b1a4
commit 575ffdbc31
36 changed files with 4516 additions and 37 deletions

View File

@@ -4,6 +4,7 @@ generator client {
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ───────────────────────────────────────
@@ -34,9 +35,10 @@ model User {
supplies Supply[]
vendors Vendor[]
locations Location[]
usageLogs UsageLog[]
tags Tag[]
settings UserSettings?
usageLogs UsageLog[]
tags Tag[]
settings UserSettings?
telegramLink TelegramLink?
}
model Account {
@@ -469,6 +471,7 @@ model Package {
files PackageFile[]
ingestionRun IngestionRun? @relation(fields: [ingestionRunId], references: [id])
ingestionRunId String?
sendRequests BotSendRequest[]
@@index([sourceChannelId])
@@index([destChannelId])
@@ -566,3 +569,61 @@ model ChannelFetchRequest {
@@index([accountId, status])
@@map("channel_fetch_requests")
}
// ───────────────────────────────────────
// Telegram Bot models
// ───────────────────────────────────────
enum BotSendStatus {
PENDING
SENDING
SENT
FAILED
}
/// Links a NextAuth user to a Telegram user ID.
/// Created when a user sends /link <code> to the bot.
model TelegramLink {
id String @id @default(cuid())
userId String @unique
telegramUserId BigInt @unique
telegramName String? @db.VarChar(128)
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
sendRequests BotSendRequest[]
@@map("telegram_links")
}
/// A queued request to send a package to a Telegram user via the bot.
model BotSendRequest {
id String @id @default(cuid())
packageId String
telegramLinkId String
requestedByUserId String
status BotSendStatus @default(PENDING)
error String?
createdAt DateTime @default(now())
completedAt DateTime?
package Package @relation(fields: [packageId], references: [id])
telegramLink TelegramLink @relation(fields: [telegramLinkId], references: [id], onDelete: Cascade)
@@index([status])
@@index([telegramLinkId])
@@index([createdAt])
@@map("bot_send_requests")
}
/// Subscriptions for new-package notifications via the bot.
model BotSubscription {
id String @id @default(cuid())
telegramUserId BigInt
pattern String @db.VarChar(256)
createdAt DateTime @default(now())
@@unique([telegramUserId, pattern])
@@index([telegramUserId])
@@map("bot_subscriptions")
}