73956fd1e3
- 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>
89 lines
2.6 KiB
TypeScript
89 lines
2.6 KiB
TypeScript
import { ForwardJobData, IndexJobData } from '@tower/types';
|
|
import { createLogger } from '@tower/logger';
|
|
|
|
const logger = createLogger('approve-message');
|
|
|
|
export interface ApproveMessageResult {
|
|
forwardJobs: ForwardJobData[];
|
|
indexDoc: IndexJobData;
|
|
}
|
|
|
|
/**
|
|
* Approve a PENDING message: mark APPROVED in a transaction, create an Approval row,
|
|
* and return the forward jobs + index document.
|
|
* Returns null if the message is not in PENDING state or is already approved.
|
|
*/
|
|
export async function approveMessage(
|
|
messageId: string,
|
|
tenantId: string,
|
|
adminId: string,
|
|
prisma: any,
|
|
targetGroupIds?: string[],
|
|
): Promise<ApproveMessageResult | null> {
|
|
const message = await prisma.message.findUnique({
|
|
where: { id: messageId },
|
|
include: {
|
|
approval: true,
|
|
sourceGroup: {
|
|
include: {
|
|
syncRoutesFrom: {
|
|
where: { isActive: true, targetGroup: { isActive: true } },
|
|
include: { targetGroup: { select: { platformId: true, isActive: true, accountId: true } } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!message) return null;
|
|
if (message.status !== 'PENDING') return null;
|
|
if (message.approval) return null;
|
|
|
|
let approved = false;
|
|
await prisma.$transaction(async (tx: any) => {
|
|
const updated = await tx.message.updateMany({
|
|
where: { id: message.id, status: 'PENDING' },
|
|
data: { status: 'APPROVED' },
|
|
});
|
|
if (updated.count === 0) return;
|
|
approved = true;
|
|
await tx.approval.create({
|
|
data: {
|
|
tenantId: message.tenantId,
|
|
messageId: message.id,
|
|
adminId,
|
|
decision: 'APPROVED',
|
|
},
|
|
});
|
|
});
|
|
|
|
if (!approved) return null;
|
|
|
|
const forwardJobs: ForwardJobData[] = (message.sourceGroup?.syncRoutesFrom ?? [])
|
|
.filter((route: any) => route.targetGroup != null)
|
|
.filter((route: any) => !targetGroupIds || targetGroupIds.includes(route.targetGroupId))
|
|
.map((route: any) => ({
|
|
tenantId: message.tenantId,
|
|
messageId: message.id,
|
|
content: message.content,
|
|
sourceGroupName: message.sourceGroup.name,
|
|
senderName: message.senderName ?? undefined,
|
|
toGroupJid: route.targetGroup.platformId,
|
|
fromAccountId: route.targetGroup.accountId ?? message.sourceGroup.accountId ?? '',
|
|
}));
|
|
|
|
const indexDoc: IndexJobData = {
|
|
tenantId: message.tenantId,
|
|
messageId: message.id,
|
|
content: message.content,
|
|
senderName: message.senderName ?? null,
|
|
sourceGroupId: message.sourceGroupId,
|
|
sourceGroupName: message.sourceGroup.name,
|
|
tags: message.tags ?? [],
|
|
platform: message.platform,
|
|
approvedAt: new Date().toISOString(),
|
|
};
|
|
|
|
return { forwardJobs, indexDoc };
|
|
}
|