Reshape the account lifecycle to match how operators actually want to work the system: - Add Account → creates a row with status='unpaired'. No QR yet; the operator lands on the detail page. - Pair / Re-pair → transitions an unpaired account to status='pending' and opens the live QR flow. Works for first-time pair AND for re-pair of an account that was previously unpaired. - Unpair → asks the bot to stop the live Baileys session and clean session files; sets status='unpaired' but KEEPS the row (and its reminders) so the operator can re-pair without retyping anything. - Delete → permanently removes the account and cascades to its groups, reminders, run history. Schema: - whatsapp_groups.account_id and reminders.account_id now have ON DELETE CASCADE so deleting an account fans out cleanly. UI: - /accounts list shows everything except the transient 'pending' state. - /accounts/[id] shows state-aware buttons: Pair (when unpaired/banned/ disconnected), Sync + Unpair (when connected), Delete (always). - /accounts/new is now an "Add Account" form (label only). Other fixes: - next.config.ts: allowedDevOrigins includes 192.168.0.253 + test/rexwa subdomains so Server Actions work across the LAN. - packages/shared/src/rrule.ts: rrule@2.8.1 has no exports field and ships ESM that some bundlers can't resolve via default OR named import. Use createRequire to bridge — works under both NodeNext (bot runtime) and Turbopack (web SSR).
33 lines
1.2 KiB
TypeScript
33 lines
1.2 KiB
TypeScript
import { rm } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { db } from "../db.js";
|
|
import { env } from "../env.js";
|
|
import { sessionManager } from "../whatsapp/session-manager.js";
|
|
import { writeAuditLog } from "../audit.js";
|
|
import { pgNotifyWeb } from "./notify.js";
|
|
import { logger } from "../logger.js";
|
|
|
|
/**
|
|
* Unpair handler: stop the live Baileys session and remove session files.
|
|
* The web's unpair action deletes the account row before notifying us,
|
|
* so we expect the row to be gone by the time we run. Audit log uses a
|
|
* null operator since the row is no longer queryable.
|
|
*/
|
|
export async function handleUnpair(accountId: string): Promise<void> {
|
|
await sessionManager.stop(accountId);
|
|
await rm(join(env.SESSIONS_DIR, accountId), { recursive: true, force: true });
|
|
try {
|
|
await writeAuditLog(db, {
|
|
operatorId: null,
|
|
source: "web",
|
|
action: "account.unpaired",
|
|
targetType: "whatsapp_account",
|
|
targetId: accountId,
|
|
payload: {},
|
|
});
|
|
} catch (err) {
|
|
logger.warn({ err, accountId }, "unpair: audit log failed (non-fatal)");
|
|
}
|
|
await pgNotifyWeb({ type: "session.disconnected", accountId });
|
|
}
|