feat: add preview management, channel controls, invite polish, and recovery

- Auto-extract preview images from ZIP/RAR/7z archives during ingestion
- Upload custom preview images via package drawer
- Select preview from archive contents with on-demand extraction UI
- Manually add Telegram channels by t.me link, username, or invite link
- Invite code UX: bulk create, copy link, usage tracking, delete confirm
- Incomplete upload recovery: verify dest messages on worker startup
- Rebuild package DB by scanning destination channel with live progress

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
admin
2026-03-22 00:09:59 +01:00
parent bf093cdfca
commit ab558e00f5
26 changed files with 3028 additions and 98 deletions

View File

@@ -38,7 +38,9 @@ model User {
tags Tag[]
settings UserSettings?
telegramLink TelegramLink?
inviteCodes InviteCode[]
inviteCodes InviteCode[] @relation("InviteCreator")
usedInvite InviteCode? @relation("InviteUser", fields: [usedInviteId], references: [id], onDelete: SetNull)
usedInviteId String?
}
model Account {
@@ -471,11 +473,12 @@ model Package {
indexedAt DateTime @default(now())
createdAt DateTime @default(now())
sourceChannel TelegramChannel @relation(fields: [sourceChannelId], references: [id])
files PackageFile[]
ingestionRun IngestionRun? @relation(fields: [ingestionRunId], references: [id])
ingestionRunId String?
sendRequests BotSendRequest[]
sourceChannel TelegramChannel @relation(fields: [sourceChannelId], references: [id])
files PackageFile[]
ingestionRun IngestionRun? @relation(fields: [ingestionRunId], references: [id])
ingestionRunId String?
sendRequests BotSendRequest[]
extractRequests ArchiveExtractRequest[]
@@index([sourceChannelId])
@@index([destChannelId])
@@ -568,7 +571,8 @@ model InviteCode {
createdBy String
createdAt DateTime @default(now())
creator User @relation(fields: [createdBy], references: [id], onDelete: Cascade)
creator User @relation("InviteCreator", fields: [createdBy], references: [id], onDelete: Cascade)
usedBy User[] @relation("InviteUser")
@@index([code])
@@map("invite_codes")
@@ -646,3 +650,35 @@ model BotSubscription {
@@index([telegramUserId])
@@map("bot_subscriptions")
}
// ───────────────────────────────────────
// Archive image extraction (worker-mediated)
// ───────────────────────────────────────
enum ExtractStatus {
PENDING
IN_PROGRESS
COMPLETED
FAILED
}
/// A request for the worker to extract an image from an archive.
/// The web app creates this, sends a pg_notify, and the worker
/// downloads the archive, extracts the file, and writes the result.
model ArchiveExtractRequest {
id String @id @default(cuid())
packageId String
filePath String @db.VarChar(1024) // path within archive to extract
status ExtractStatus @default(PENDING)
imageData Bytes? // extracted image bytes (JPEG/PNG/WebP)
contentType String? @db.VarChar(64) // MIME type of extracted image
error String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
package Package @relation(fields: [packageId], references: [id], onDelete: Cascade)
@@index([packageId, filePath])
@@index([status])
@@map("archive_extract_requests")
}