feat: route-centric rules, approve popup, emoji picker, 5-column sync table
- Schema: SyncRouteRuleMap junction table, ruleIntent on TenantRule, ForwardFormat/Frequency/ApprovalMode columns on SyncRoute, INTEREST match type - Worker: remove global AUTO_APPROVE forwarding; per-route rule evaluation in ingest processor — POSITIVE rules auto-forward that route, NEGATIVE rules block it, fallback to approvalMode - API: PATCH /routes/:id, rule assign/unassign endpoints, approve now accepts targetGroupIds, new GET /messages/:id/routes for popup data - Web: 5-column route table with settings + rules dialogs, ForwardTarget popup with rule-based pre-selection, ruleIntent toggle + emoji dropdown (👍/👎 only) + INTEREST type in rules manager Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "RuleIntent" AS ENUM ('POSITIVE', 'NEGATIVE');
|
||||
CREATE TYPE "ForwardFormat" AS ENUM ('THREADED', 'DIGEST', 'SUMMARY');
|
||||
CREATE TYPE "ForwardFrequency" AS ENUM ('INSTANT', 'FIVE_MIN', 'FIFTEEN_MIN', 'ONE_HOUR', 'ONE_DAY', 'SEVEN_DAYS');
|
||||
CREATE TYPE "ApprovalMode" AS ENUM ('MANUAL', 'AUTO');
|
||||
|
||||
-- AlterEnum: Add INTEREST to RuleMatchType
|
||||
ALTER TYPE "RuleMatchType" ADD VALUE IF NOT EXISTS 'INTEREST';
|
||||
|
||||
-- AlterTable: TenantRule - add ruleIntent
|
||||
ALTER TABLE "TenantRule" ADD COLUMN IF NOT EXISTS "ruleIntent" "RuleIntent" NOT NULL DEFAULT 'POSITIVE';
|
||||
|
||||
-- AlterTable: SyncRoute - add new columns (updatedAt with default NOW() for existing rows)
|
||||
ALTER TABLE "SyncRoute"
|
||||
ADD COLUMN IF NOT EXISTS "forwardFormat" "ForwardFormat" NOT NULL DEFAULT 'THREADED',
|
||||
ADD COLUMN IF NOT EXISTS "withAttachment" BOOLEAN NOT NULL DEFAULT true,
|
||||
ADD COLUMN IF NOT EXISTS "frequency" "ForwardFrequency" NOT NULL DEFAULT 'INSTANT',
|
||||
ADD COLUMN IF NOT EXISTS "approvalMode" "ApprovalMode" NOT NULL DEFAULT 'MANUAL',
|
||||
ADD COLUMN IF NOT EXISTS "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT NOW();
|
||||
|
||||
-- CreateTable: SyncRouteRuleMap
|
||||
CREATE TABLE IF NOT EXISTS "SyncRouteRuleMap" (
|
||||
"id" TEXT NOT NULL,
|
||||
"syncRouteId" TEXT NOT NULL,
|
||||
"tenantRuleId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "SyncRouteRuleMap_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "SyncRouteRuleMap_syncRouteId_tenantRuleId_key"
|
||||
ON "SyncRouteRuleMap"("syncRouteId", "tenantRuleId");
|
||||
CREATE INDEX IF NOT EXISTS "SyncRouteRuleMap_syncRouteId_idx"
|
||||
ON "SyncRouteRuleMap"("syncRouteId");
|
||||
CREATE INDEX IF NOT EXISTS "SyncRouteRuleMap_tenantRuleId_idx"
|
||||
ON "SyncRouteRuleMap"("tenantRuleId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "SyncRouteRuleMap"
|
||||
ADD CONSTRAINT "SyncRouteRuleMap_syncRouteId_fkey"
|
||||
FOREIGN KEY ("syncRouteId") REFERENCES "SyncRoute"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "SyncRouteRuleMap"
|
||||
ADD CONSTRAINT "SyncRouteRuleMap_tenantRuleId_fkey"
|
||||
FOREIGN KEY ("tenantRuleId") REFERENCES "TenantRule"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -337,20 +337,40 @@ enum ApprovalDecision {
|
||||
}
|
||||
|
||||
model SyncRoute {
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||
sourceGroupId String
|
||||
sourceGroup Group @relation("sourceGroup", fields: [sourceGroupId], references: [id])
|
||||
targetGroupId String
|
||||
targetGroup Group @relation("targetGroup", fields: [targetGroupId], references: [id])
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||
sourceGroupId String
|
||||
sourceGroup Group @relation("sourceGroup", fields: [sourceGroupId], references: [id])
|
||||
targetGroupId String
|
||||
targetGroup Group @relation("targetGroup", fields: [targetGroupId], references: [id])
|
||||
isActive Boolean @default(true)
|
||||
forwardFormat ForwardFormat @default(THREADED)
|
||||
withAttachment Boolean @default(true)
|
||||
frequency ForwardFrequency @default(INSTANT)
|
||||
approvalMode ApprovalMode @default(MANUAL)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
assignedRules SyncRouteRuleMap[]
|
||||
|
||||
@@unique([sourceGroupId, targetGroupId])
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
model SyncRouteRuleMap {
|
||||
id String @id @default(cuid())
|
||||
syncRouteId String
|
||||
syncRoute SyncRoute @relation(fields: [syncRouteId], references: [id], onDelete: Cascade)
|
||||
tenantRuleId String
|
||||
tenantRule TenantRule @relation(fields: [tenantRuleId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([syncRouteId, tenantRuleId])
|
||||
@@index([syncRouteId])
|
||||
@@index([tenantRuleId])
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Group claiming + sharing
|
||||
// ============================================================================
|
||||
@@ -515,6 +535,7 @@ enum RuleMatchType {
|
||||
HASHTAG
|
||||
PREFIX
|
||||
REACTION_EMOJI
|
||||
INTEREST
|
||||
}
|
||||
|
||||
enum RuleAction {
|
||||
@@ -524,17 +545,45 @@ enum RuleAction {
|
||||
REJECT
|
||||
}
|
||||
|
||||
enum RuleIntent {
|
||||
POSITIVE
|
||||
NEGATIVE
|
||||
}
|
||||
|
||||
enum ForwardFormat {
|
||||
THREADED
|
||||
DIGEST
|
||||
SUMMARY
|
||||
}
|
||||
|
||||
enum ForwardFrequency {
|
||||
INSTANT
|
||||
FIVE_MIN
|
||||
FIFTEEN_MIN
|
||||
ONE_HOUR
|
||||
ONE_DAY
|
||||
SEVEN_DAYS
|
||||
}
|
||||
|
||||
enum ApprovalMode {
|
||||
MANUAL
|
||||
AUTO
|
||||
}
|
||||
|
||||
model TenantRule {
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||
matchType RuleMatchType
|
||||
matchValue String
|
||||
action RuleAction
|
||||
priority Int @default(0)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||
matchType RuleMatchType
|
||||
matchValue String
|
||||
action RuleAction
|
||||
ruleIntent RuleIntent @default(POSITIVE)
|
||||
priority Int @default(0)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
syncRouteRuleMaps SyncRouteRuleMap[]
|
||||
|
||||
@@unique([tenantId, matchType, matchValue])
|
||||
@@index([tenantId, isActive])
|
||||
|
||||
Reference in New Issue
Block a user