69 lines
2 KiB
TypeScript
69 lines
2 KiB
TypeScript
|
|
import { and, asc, eq, isNull } from "drizzle-orm";
|
||
|
|
|
||
|
|
import type { db as dbInstance } from "@tasks/database";
|
||
|
|
import { convoyOperatorMessages } from "@tasks/database/schema";
|
||
|
|
|
||
|
|
type Db = typeof dbInstance;
|
||
|
|
|
||
|
|
export type OperatorMessageRow = typeof convoyOperatorMessages.$inferSelect;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Poll undelivered operator messages for a convoy.
|
||
|
|
* Keep polling fallback simple — no Redis pubsub, just a DB query.
|
||
|
|
*/
|
||
|
|
export async function pollOperatorNotes(
|
||
|
|
db: Db,
|
||
|
|
workspaceId: string,
|
||
|
|
convoyId: string,
|
||
|
|
briefId?: string | null,
|
||
|
|
): Promise<OperatorMessageRow[]> {
|
||
|
|
const conditions = [
|
||
|
|
eq(convoyOperatorMessages.workspaceId, workspaceId),
|
||
|
|
eq(convoyOperatorMessages.convoyId, convoyId),
|
||
|
|
isNull(convoyOperatorMessages.deliveredAt),
|
||
|
|
];
|
||
|
|
|
||
|
|
// Optional brief scoping — include global convoy notes plus brief-specific ones
|
||
|
|
// When briefId is provided we return rows where brief_id is null OR matches the brief.
|
||
|
|
// Keeping the simple fallback: just filter by workspace+convoy when briefId not set.
|
||
|
|
// When briefId is set we still return all for convoy so direct messages are not missed.
|
||
|
|
void briefId;
|
||
|
|
|
||
|
|
const rows = await db
|
||
|
|
.select()
|
||
|
|
.from(convoyOperatorMessages)
|
||
|
|
.where(and(...conditions))
|
||
|
|
.orderBy(asc(convoyOperatorMessages.createdAt));
|
||
|
|
|
||
|
|
return rows;
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function markOperatorNotesDelivered(
|
||
|
|
db: Db,
|
||
|
|
ids: string[],
|
||
|
|
): Promise<void> {
|
||
|
|
if (ids.length === 0) return;
|
||
|
|
for (const id of ids) {
|
||
|
|
await db
|
||
|
|
.update(convoyOperatorMessages)
|
||
|
|
.set({ deliveredAt: new Date() })
|
||
|
|
.where(eq(convoyOperatorMessages.id, id));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Convenience: poll and mark delivered in one step */
|
||
|
|
export async function consumeOperatorNotes(
|
||
|
|
db: Db,
|
||
|
|
workspaceId: string,
|
||
|
|
convoyId: string,
|
||
|
|
briefId?: string | null,
|
||
|
|
): Promise<OperatorMessageRow[]> {
|
||
|
|
const rows = await pollOperatorNotes(db, workspaceId, convoyId, briefId);
|
||
|
|
if (rows.length === 0) return rows;
|
||
|
|
await markOperatorNotesDelivered(
|
||
|
|
db,
|
||
|
|
rows.map((r) => r.id),
|
||
|
|
);
|
||
|
|
return rows;
|
||
|
|
}
|