feat: fix channel scanning bugs, add package tags, and kickstarters tab

Bug fixes:
- Fix channels not being scanned by paginating TDLib getChats (was only
  loading first batch, additional channels were unknown to TDLib)
- Add per-channel getChat pre-load as safety net before scanning
- Fix preview pictures not loading by checking previewData instead of
  previewMsgId for hasPreview flag
- Prevent previewMsgId from being set when preview download fails

Package Tags:
- Add tags Text[] column to Package with migration backfilling from
  channel categories
- Worker auto-inherits source channel category as initial tag
- Tag filter dropdown and Tags column in STL Files table
- Server actions for individual and bulk tag editing

Kickstarters Tab:
- New KickstarterHost, Kickstarter, and KickstarterPackage models
- Full CRUD with delivery status, payment status, host management
- Package linking (many-to-many with existing packages)
- Sidebar entry with Gift icon
- Table with search, filters, modal forms

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 18:17:44 +01:00
parent e2dd3bb9d0
commit 5fd341dfc4
23 changed files with 1375 additions and 32 deletions

View File

@@ -0,0 +1,10 @@
-- Add tags array column to packages
ALTER TABLE "packages" ADD COLUMN "tags" TEXT[] NOT NULL DEFAULT '{}';
-- Backfill: inherit source channel category as initial tag
UPDATE "packages" p
SET "tags" = ARRAY[c."category"]
FROM "telegram_channels" c
WHERE p."sourceChannelId" = c."id"
AND c."category" IS NOT NULL
AND c."category" != '';

View File

@@ -0,0 +1,50 @@
-- CreateEnum
CREATE TYPE "DeliveryStatus" AS ENUM ('NOT_DELIVERED', 'PARTIAL', 'DELIVERED');
CREATE TYPE "PaymentStatus" AS ENUM ('PAID', 'UNPAID');
-- CreateTable
CREATE TABLE "kickstarter_hosts" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "kickstarter_hosts_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "kickstarters" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"link" TEXT,
"filesUrl" TEXT,
"deliveryStatus" "DeliveryStatus" NOT NULL DEFAULT 'NOT_DELIVERED',
"paymentStatus" "PaymentStatus" NOT NULL DEFAULT 'UNPAID',
"notes" TEXT,
"hostId" TEXT,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "kickstarters_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "kickstarter_packages" (
"kickstarterId" TEXT NOT NULL,
"packageId" TEXT NOT NULL,
CONSTRAINT "kickstarter_packages_pkey" PRIMARY KEY ("kickstarterId","packageId")
);
-- CreateIndex
CREATE UNIQUE INDEX "kickstarter_hosts_name_key" ON "kickstarter_hosts"("name");
CREATE INDEX "kickstarters_hostId_idx" ON "kickstarters"("hostId");
CREATE INDEX "kickstarters_userId_idx" ON "kickstarters"("userId");
CREATE INDEX "kickstarters_deliveryStatus_idx" ON "kickstarters"("deliveryStatus");
CREATE INDEX "kickstarters_paymentStatus_idx" ON "kickstarters"("paymentStatus");
-- AddForeignKey
ALTER TABLE "kickstarters" ADD CONSTRAINT "kickstarters_hostId_fkey" FOREIGN KEY ("hostId") REFERENCES "kickstarter_hosts"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "kickstarters" ADD CONSTRAINT "kickstarters_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "kickstarter_packages" ADD CONSTRAINT "kickstarter_packages_kickstarterId_fkey" FOREIGN KEY ("kickstarterId") REFERENCES "kickstarters"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "kickstarter_packages" ADD CONSTRAINT "kickstarter_packages_packageId_fkey" FOREIGN KEY ("packageId") REFERENCES "packages"("id") ON DELETE CASCADE ON UPDATE CASCADE;