@aglyn/tenant-data-admin 1.0.0-beta.217 → 1.0.0-beta.218

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -614,7 +614,9 @@ function emptyDelivery(skipped = null) {
614
614
  lockMessage: (_ref10 = (_input_lock4 = input.lock) == null ? void 0 : _input_lock4.message) != null ? _ref10 : null
615
615
  });
616
616
  const rendered = await renderRiskNoticeEmail(riskNoticeEmailKey(input.kind), merge, await riskNoticeEmailBrand(input.kind, orgId));
617
- const delivery = await emailPeople(deps, recipients, rendered, supportEmail);
617
+ const delivery = await emailPeople(deps, recipients, rendered, supportEmail, {
618
+ lockNotice: LOCK_NOTICE_KINDS.has(input.kind)
619
+ });
618
620
  result.owners.emailed = delivery.sent;
619
621
  result.owners.emailFailed = delivery.failed;
620
622
  if (!delivery.sent && !delivery.failed) {
@@ -707,10 +709,24 @@ function isAlreadyExists(error) {
707
709
  'help.url': riskNoticeHelpUrl(input.helpAnchor)
708
710
  };
709
711
  }
712
+ /**
713
+ * The notices a banned account still receives (AGL-3420): the lock and lift
714
+ * notices, the only mail that tells it what happened. Everything else —
715
+ * digests, reviews, sale warnings — is refused like any other sender's.
716
+ */ const LOCK_NOTICE_KINDS = new Set([
717
+ 'workspace-locked',
718
+ 'workspace-unlocked',
719
+ 'site-locked',
720
+ 'site-unlocked',
721
+ 'domain-locked',
722
+ 'domain-unlocked',
723
+ 'account-locked',
724
+ 'account-unlocked'
725
+ ]);
710
726
  /**
711
727
  * One email per person — two owners have not agreed to see each other's
712
728
  * addresses. Platform sender, transactional, owed for the account.
713
- */ async function emailPeople(deps, recipients, rendered, supportEmail) {
729
+ */ async function emailPeople(deps, recipients, rendered, supportEmail, options = {}) {
714
730
  let sent = 0;
715
731
  let failed = 0;
716
732
  const seen = new Set();
@@ -732,7 +748,9 @@ function isAlreadyExists(error) {
732
748
  } : {}, {
733
749
  context: 'risk-notice',
734
750
  owedFor: 'account'
735
- })).catch((error)=>({
751
+ }, options.lockNotice ? {
752
+ accountBanExempt: true
753
+ } : {})).catch((error)=>({
736
754
  sent: false,
737
755
  reason: 'provider-error',
738
756
  detail: String(error)
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/risk-notice.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * notifyRiskEvent: ONE WAY TO TELL PEOPLE SOMETHING WAS HELD (AGL-3368).\n *\n * Every place the platform holds, flags, locks or pauses something on a\n * workspace calls this, with the kind from the catalog\n * (`@aglyn/shared-util-email/risk-notice-catalog`) and the facts it knows.\n * This decides who hears, on which channel, and in which words:\n *\n * - THE WORKSPACE'S OWNERS AND ADMINS (the org roster's `owner`/`admin`\n * roles, the audience `notifyOrgAdmins` reaches), plus — for a kind about\n * one site's sales or pages — the site's managers, the audience\n * `notifyHostManagers` reaches and the one a sale's fraud signal always\n * went to. An account lock goes to the account's own person instead.\n * They get an in-app notification (`system.riskNotice`) and, for every\n * kind the catalog marks `emailOwners`, an email.\n * - STAFF, through `notifyStaff`, for the kinds the catalog marks\n * `alertStaff`. A row-backed alert links the row itself.\n *\n * ## The owner email is account mail from the platform\n *\n * It is sent with no sending identity and no `audience: 'tenant'`, so it\n * leaves on the platform's own configured sender — never the workspace's.\n * That is what lets a LOCKED workspace's owners receive their lock notice:\n * a suspended workspace's own identity refuses every send\n * (`workspace-suspended`), but this mail is not the workspace's. It is\n * `owedFor: 'account'`, carries no marketing context, and ignores\n * notification preferences: the owner did not opt into it and cannot opt\n * out of being told their email was held. The tenant phishing screen never\n * sees it — that screen asks only about a site's mail.\n *\n * ## One notice per event, and a digest under a burst\n *\n * A notice's id is a hash of its dedupe key (by default the kind and the\n * abuse-queue row), created once — so a redelivered webhook or a re-held\n * message never tells anyone twice, on top of the one-alert-per-row rule\n * the row writers already keep.\n *\n * Past {@link RISK_NOTICE_BURST.owners} notices in an hour for one\n * workspace (and {@link RISK_NOTICE_BURST.staff} for staff), further notices\n * are recorded but not delivered one by one. The first one folded sends a\n * short \"more are arriving\" note in-app; when the hour closes,\n * {@link flushRiskNoticeDigests} (hourly cron, and the next notice after the\n * window) sends ONE summary. A workspace under attack gets a digest, not\n * five hundred emails. Locks, lifts and cancellations (`neverDigest`) always\n * go out on their own.\n *\n * ## Never throws\n *\n * Every caller is a write that has already happened — a hold, a lock, a\n * webhook. A failed notice is reported in the result (the lockdown route\n * shows it as an unconfirmed step) and logged; it never undoes the event.\n *=========================================*/\n\nimport { createHash } from 'crypto'\nimport { ABUSE_REPORT_COLLECTION, abuseReportContactEmail } from '@aglyn/aglyn/app-utils/abuse-report'\nimport { DOCS_BASE_URL } from '@aglyn/aglyn/app-utils/docs-help'\nimport {\n isRiskEventKind,\n renderOwnerRiskNotice,\n renderRiskNoticeText,\n renderStaffRiskNotice,\n resolveOwnerRiskActions,\n RISK_NOTICE_CATALOG,\n RISK_NOTICE_CLOSING_KINDS,\n RISK_NOTICE_DIGEST_EMAIL_KEY,\n RISK_NOTICE_HELP_PATH,\n RISK_NOTICE_WORKSPACE_BRANDED,\n riskNoticeEmailKey,\n type ResolvedRiskAction,\n type RiskActionParams,\n type RiskEventKind,\n type RiskNoticeValues,\n} from '@aglyn/shared-util-email/risk-notice-catalog'\nimport {\n sendEmail as sendEmailImpl,\n type SendEmailOptions,\n type SendEmailResult,\n} from '@aglyn/shared-util-email'\nimport {\n heldPageDetails,\n heldPageLabel,\n heldPageTargets,\n heldPageVisitorSentence,\n isOpenPageHoldStatus,\n isPageHoldNoticeKind,\n PAGE_HOLD_NOTICE_KINDS,\n pageHoldChip,\n parseHeldPageItemPath,\n type HeldPageSubject,\n type HeldPageTarget,\n type PageHoldNoticeKind,\n} from '@aglyn/shared-util-email/held-page'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { findUserByUidAcrossPools } from './auth-pools'\nimport firebaseAdmin from './firebase-admin'\nimport {\n type NotificationPayload,\n notifyStaff as notifyStaffImpl,\n notifyUsers as notifyUsersImpl,\n type NotifyUsersOptions,\n} from './notifications'\nimport { meterPlatformEmail } from './email-metering'\nimport { listOrgMembers } from './organizations'\nimport {\n orgSystemEmailBrand,\n renderSystemEmailContent,\n type SystemEmailBrand,\n systemEmailBrand,\n} from './render-system-email'\nimport { RISK_REVIEW_REQUESTED_EMAIL_KEY } from '@aglyn/shared-util-email/risk-notice-emails'\n\n/** Admin-SDK only; no client rule opens it. One document per notice. */\nexport const RISK_NOTICE_COLLECTION = 'riskNotices'\n\n/** Admin-SDK only: each workspace's burst window and folded notices. */\nexport const RISK_NOTICE_LEDGER_COLLECTION = 'riskNoticeLedger'\n\n/** The in-app notification type every owner notice is written as. */\nexport const RISK_NOTICE_NOTIFICATION_TYPE = 'system.riskNotice' as const\n\n/**\n * The burst allowance: how many notices one workspace's owners, and staff\n * about one workspace, receive one by one in {@link RISK_NOTICE_BURST.windowMs}\n * before the rest are folded into a digest.\n */\nexport const RISK_NOTICE_BURST = {\n owners: 5,\n staff: 10,\n windowMs: 60 * 60 * 1000,\n /** How many folded notices a digest lists by name. */\n digestListMax: 25,\n} as const\n\n/** Longest review-request note stored. */\nexport const RISK_REVIEW_NOTE_MAX = 2000\n/** How many review requests one row keeps. */\nexport const RISK_REVIEW_REQUESTS_MAX = 20\n/** How soon the same workspace may ask again about the same row. */\nexport const RISK_REVIEW_REQUEST_COOLDOWN_MS = 10 * 60 * 1000\n\n/** What a source knows about the thing the notice is about. */\nexport interface RiskEventItem {\n /** In the owner's words: `the campaign \"Spring sale\"`, `order 1042`. */\n label: string\n /**\n * Its console page, in the stored-notification shape (`/org/…` or\n * `/{hostId}/…`) the console rewrites when the link is followed.\n */\n path?: string | null\n}\n\nexport interface RiskEventInput {\n kind: RiskEventKind\n orgId: string | null\n hostId?: string | null\n /** An account lock's person. */\n userUid?: string | null\n /** The abuse-queue row, when the event has one. */\n reviewId?: string | null\n reference?: string | null\n item?: RiskEventItem | null\n occurredAtMs?: number\n /** `$56.00 USD`. */\n amount?: string | null\n evidenceDueByMs?: number | null\n /** Staff only: the Stripe Dashboard page for the charge, review or dispute. */\n stripeUrl?: string | null\n lock?: {\n /** The customer-facing message the lock stores, verbatim. */\n message?: string | null\n /** What the lock or pause covers, in plain words. */\n affected?: string | null\n scope?: string | null\n targetId?: string | null\n } | null\n /**\n * A held or flagged page, described (AGL-3374). Stored on the notice so\n * the console can put the hold on the page, template, layout or component\n * it is about, and name it there. Owner-safe: no signal, no evidence.\n */\n page?: HeldPageSubject | null\n /** Staff only: what the screen or Stripe reported. Never shown to owners. */\n staffEvidence?: string | null\n /** Overrides the default dedupe key. */\n dedupeKey?: string | null\n /**\n * `false` to send no owner EMAIL for this event (a lock placed under a\n * legal hold). The in-app notice and the record are still written.\n */\n emailOwners?: boolean\n /**\n * Exactly who the owner half goes to, in place of the workspace's owners\n * and admins: a batch that already resolved its people and groups several\n * events into one notice per person (the lockdown's \"Resend owner\n * notice\").\n */\n recipients?: ReadonlyArray<{ uid: string; email: string | null }>\n}\n\nexport interface RiskNoticeDelivery {\n /** How many people the owner half was for. */\n recipients: number\n /** In-app notifications written (before per-person mutes). */\n inApp: number\n emailed: number\n emailFailed: number\n /** Why no email went, when none did. */\n emailSkipped: string | null\n /** Folded into the next digest instead of delivered now. */\n digested: boolean\n}\n\nexport interface RiskNoticeResult {\n noticeId: string | null\n /** The same notice was already delivered: nothing was sent now. */\n duplicate: boolean\n owners: RiskNoticeDelivery\n staff: { alerted: boolean; digested: boolean }\n /** Set when the notice could not be processed at all. */\n error: string | null\n}\n\n/** Everything the seam touches, injectable for specs. */\nexport interface RiskNoticeDeps {\n firestore: FirebaseFirestore.Firestore\n sendEmail: (options: SendEmailOptions) => Promise<SendEmailResult>\n notifyUsers: (\n uids: Iterable<string>,\n payload: NotificationPayload,\n options?: NotifyUsersOptions,\n ) => Promise<void>\n notifyStaff: (payload: NotificationPayload) => Promise<void>\n listOwners: (orgId: string) => Promise<Array<{ uid: string; email: string | null }>>\n lookupEmail: (uid: string) => Promise<string | null>\n nowMs: () => number\n}\n\nasync function defaultListOwners(orgId: string) {\n const members = await listOrgMembers(orgId)\n return members\n .filter((member) => member.role === 'owner' || member.role === 'admin')\n .map((member) => ({\n uid: String(member.$id),\n email: (member as { email?: string | null }).email ?? null,\n }))\n}\n\nasync function defaultLookupEmail(uid: string): Promise<string | null> {\n const found = await findUserByUidAcrossPools(uid).catch(() => null)\n const email = String(found?.record?.email ?? '').trim()\n return email.includes('@') ? email : null\n}\n\nfunction resolveDeps(overrides: Partial<RiskNoticeDeps> = {}): RiskNoticeDeps {\n return {\n firestore: overrides.firestore ?? firebaseAdmin.app().firestore(),\n sendEmail: overrides.sendEmail ?? sendEmailImpl,\n notifyUsers: overrides.notifyUsers ?? notifyUsersImpl,\n notifyStaff: overrides.notifyStaff ?? notifyStaffImpl,\n listOwners: overrides.listOwners ?? defaultListOwners,\n lookupEmail: overrides.lookupEmail ?? defaultLookupEmail,\n nowMs: overrides.nowMs ?? (() => Date.now()),\n }\n}\n\nfunction consoleOrigin(): string {\n return (process.env.NEXT_PUBLIC_CONSOLE_URL ?? '').trim().replace(/\\/+$/, '')\n}\n\n/** The help page every notice links, on the docs site. */\nexport function riskNoticeHelpUrl(anchor?: string): string {\n return `${DOCS_BASE_URL.replace(/\\/+$/, '')}${RISK_NOTICE_HELP_PATH}${anchor ? `#${anchor}` : ''}`\n}\n\n/** `Sep 28, 2026, 16:31 UTC` — one format for every notice. */\nexport function formatRiskNoticeTime(ms: number): string {\n const date = new Date(ms)\n if (!Number.isFinite(date.getTime())) return ''\n return `${date.toLocaleString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZone: 'UTC',\n })} UTC`\n}\n\n/** The id of the notice a dedupe key names: hex, like the abuse queue's. */\nexport function riskNoticeId(dedupeKey: string): string {\n return createHash('sha256').update(`risk-notice:${dedupeKey}`).digest('hex').slice(0, 40)\n}\n\n/** The default dedupe key: one notice per kind per row, else per event. */\nexport function riskNoticeDedupeKey(input: RiskEventInput, occurredAtMs: number): string {\n if (input.dedupeKey) return input.dedupeKey\n if (input.reviewId) return `${input.kind}:${input.reviewId}`\n return [\n input.kind,\n input.orgId ?? '',\n input.hostId ?? '',\n input.userUid ?? '',\n input.item?.path ?? input.item?.label ?? '',\n String(occurredAtMs),\n ].join(':')\n}\n\n/** The ledger a notice's burst allowance is counted on. */\nfunction ledgerIdFor(input: RiskEventInput): string | null {\n if (input.orgId) return `org:${input.orgId}`\n if (input.userUid) return `user:${input.userUid}`\n if (input.hostId) return `host:${input.hostId}`\n return null\n}\n\n/** The console path an owner's Holds & reviews page is at, in stored shape. */\nexport function riskHoldsPath(noticeId?: string | null): string {\n return `/org/settings/holds${noticeId ? `?notice=${encodeURIComponent(noticeId)}` : ''}`\n}\n\n/** A staff deep link to one abuse-queue row. */\nexport function staffAbuseRowPath(reviewId: string): string {\n return `/admin/abuse-reports?report=${encodeURIComponent(reviewId)}`\n}\n\n/** Absolute, for an email: the console origin before a stored path. */\nfunction absolute(path: string | null | undefined): string {\n if (!path) return ''\n if (/^https?:\\/\\//.test(path)) return path\n const origin = consoleOrigin()\n return origin ? `${origin}${path}` : ''\n}\n\n/**\n * The stored-shape console links resolve per reader in the console\n * (`normalizeNotificationLink`); an EMAIL has no reader context, so its\n * links are rewritten here from the org slug and the site's subdomain.\n */\nfunction emailPath(\n path: string,\n context: { orgSlug: string | null; hostId: string | null; hostSubdomain: string | null },\n): string | null {\n if (/^https?:\\/\\//.test(path)) return path\n if (path.startsWith('/admin/') || path.startsWith('/manage/')) return path\n if (path === '/org' || path.startsWith('/org/') || path.startsWith('/org?')) {\n return context.orgSlug ? `/${context.orgSlug}${path.slice(4)}` : null\n }\n if (context.hostId && (path === `/${context.hostId}` || path.startsWith(`/${context.hostId}/`) || path.startsWith(`/${context.hostId}?`))) {\n return context.orgSlug && context.hostSubdomain\n ? `/${context.orgSlug}/hosts/${context.hostSubdomain}${path.slice(context.hostId.length + 1)}`\n : null\n }\n return path\n}\n\n/**\n * Stored-shape site paths (`/{hostId}/…`) as console routes, for a reader\n * with no org context of its own — staff reading the abuse queue\n * (AGL-3374). One read of the sites and one of their workspaces for the\n * whole batch; a path that cannot be resolved is left out.\n */\nexport async function resolveHostConsolePaths(\n firestore: FirebaseFirestore.Firestore,\n entries: ReadonlyArray<{ hostId: string; orgId?: string | null; path: string }>,\n): Promise<Map<string, string>> {\n const resolved = new Map<string, string>()\n const hostIds = [...new Set(entries.map((entry) => entry.hostId).filter(Boolean))]\n if (!hostIds.length) return resolved\n const hosts = await firestore.getAll(\n ...hostIds.map((id) => firestore.collection('hosts').doc(id)),\n )\n const hostFacts = new Map(\n hosts.map((host) => [\n host.id,\n {\n subdomain: String(host.get('subdomain') ?? '') || null,\n orgId: String(host.get('orgId') ?? '') || null,\n },\n ]),\n )\n // The row names its workspace; a site document may too.\n const orgOf = (entry: { hostId: string; orgId?: string | null }) =>\n entry.orgId || hostFacts.get(entry.hostId)?.orgId || null\n const orgIds = [...new Set(entries.map(orgOf).filter(Boolean))] as string[]\n const orgs = orgIds.length\n ? await firestore.getAll(...orgIds.map((id) => firestore.collection('orgs').doc(id)))\n : []\n const slugs = new Map(orgs.map((org) => [org.id, String(org.get('slug') ?? '') || null]))\n for (const entry of entries) {\n const facts = hostFacts.get(entry.hostId)\n const orgId = orgOf(entry)\n const href = emailPath(entry.path, {\n orgSlug: orgId ? (slugs.get(orgId) ?? null) : null,\n hostId: entry.hostId,\n hostSubdomain: facts?.subdomain ?? null,\n })\n if (href && href !== entry.path) resolved.set(`${entry.hostId}\\n${entry.path}`, href)\n }\n return resolved\n}\n\n/** The workspace facts every notice renders with. Never throws. */\nasync function readContext(\n firestore: FirebaseFirestore.Firestore,\n input: RiskEventInput,\n): Promise<{\n orgId: string | null\n orgName: string | null\n orgSlug: string | null\n hostSubdomain: string | null\n siteManagerUids: string[]\n}> {\n let orgId = input.orgId ?? null\n let hostSubdomain: string | null = null\n let siteManagerUids: string[] = []\n if (input.hostId) {\n const host = await firestore.collection('hosts').doc(input.hostId).get().catch(() => null)\n if (host?.exists) {\n hostSubdomain = String(host.get('subdomain') ?? '') || null\n orgId = orgId ?? (String(host.get('orgId') ?? '') || null)\n const roles = (host.get('memberRoles') as Record<string, string> | undefined) ?? {}\n siteManagerUids = Object.entries(roles)\n .filter(([, role]) => role === 'admin' || role === 'editor')\n .map(([uid]) => uid)\n }\n }\n let orgName: string | null = null\n let orgSlug: string | null = null\n if (orgId) {\n const org = await firestore.collection('orgs').doc(orgId).get().catch(() => null)\n if (org?.exists) {\n orgName = String(org.get('name') ?? '') || null\n orgSlug = String(org.get('slug') ?? '') || null\n }\n }\n return { orgId, orgName, orgSlug, hostSubdomain, siteManagerUids }\n}\n\n/** What one notice email sends. */\nexport interface RenderedRiskNoticeEmail {\n key: string\n subject: string\n text: string\n html?: string\n /** The display name to send under; absent sends under the platform's. */\n fromName?: string\n}\n\n/**\n * The brand a kind's email is sent in: the workspace's own for a routine\n * notice about its customers' payments, the platform's for everything the\n * platform does (holds, locks, lifts, cancellations, reviews).\n */\nexport async function riskNoticeEmailBrand(\n kind: RiskEventKind | null,\n orgId: string | null,\n): Promise<SystemEmailBrand> {\n return kind && RISK_NOTICE_WORKSPACE_BRANDED.has(kind)\n ? orgSystemEmailBrand(orgId)\n : systemEmailBrand(null)\n}\n\n/**\n * Renders one notice email through the System emails seam (AGL-3367): the\n * published design for the kind's own `risk-…` key, or its built-in copy,\n * inside the header and footer every platform email wears. The plain-text\n * composition below is the last resort for a render that produced nothing.\n * ONE function, so every notice renders the same way.\n */\nexport async function renderRiskNoticeEmail(\n key: string,\n merge: Record<string, string>,\n brand: SystemEmailBrand = systemEmailBrand(null),\n): Promise<RenderedRiskNoticeEmail> {\n const content = await renderSystemEmailContent(key, merge, brand, riskNoticeFallback(merge)).catch(\n () => riskNoticeFallback(merge),\n )\n return { key, ...content, ...(brand.fromName ? { fromName: brand.fromName } : {}) }\n}\n\n/** The notice as plain text, when no design renders. */\nfunction riskNoticeFallback(merge: Record<string, string>): { subject: string; text: string } {\n const lines = [\n merge['notice.summary'],\n merge['notice.meaning'],\n merge['notice.steps'] ? `What to do next:\\n${merge['notice.steps']}` : '',\n merge['notice.actions'],\n merge['reference'] ? `Reference: ${merge['reference']}` : '',\n merge['help.url'] ? `Why was something held or flagged? ${merge['help.url']}` : '',\n ].filter((line) => Boolean(line && line.trim()))\n return { subject: merge['notice.title'] ?? '', text: lines.join('\\n\\n') }\n}\n\nfunction numbered(steps: readonly string[]): string {\n return steps.map((step, index) => `${index + 1}. ${step}`).join('\\n')\n}\n\nfunction emptyDelivery(skipped: string | null = null): RiskNoticeDelivery {\n return {\n recipients: 0,\n inApp: 0,\n emailed: 0,\n emailFailed: 0,\n emailSkipped: skipped,\n digested: false,\n }\n}\n\ntype BurstVerdict = { owners: 'deliver' | 'fold-first' | 'fold'; staff: 'deliver' | 'fold' }\n\n/**\n * Count this notice against its workspace's hour and say whether it is\n * delivered now. Fails OPEN: a ledger that cannot be written never costs a\n * notice.\n */\nasync function countBurst(\n deps: RiskNoticeDeps,\n ledgerId: string,\n entry: { kind: RiskEventKind; title: string; noticeId: string; staffTitle: string; alertStaff: boolean },\n): Promise<BurstVerdict> {\n const nowMs = deps.nowMs()\n const ref = deps.firestore.collection(RISK_NOTICE_LEDGER_COLLECTION).doc(ledgerId)\n try {\n return await deps.firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n const startedAtMs = Number(snapshot.get('windowStartedAtMs') ?? 0)\n const fresh = !startedAtMs || nowMs - startedAtMs >= RISK_NOTICE_BURST.windowMs\n const ownerCount = (fresh ? 0 : Number(snapshot.get('ownerCount') ?? 0)) + 1\n const staffCount =\n (fresh ? 0 : Number(snapshot.get('staffCount') ?? 0)) + (entry.alertStaff ? 1 : 0)\n const owners: BurstVerdict['owners'] =\n ownerCount <= RISK_NOTICE_BURST.owners\n ? 'deliver'\n : ownerCount === RISK_NOTICE_BURST.owners + 1\n ? 'fold-first'\n : 'fold'\n const staff: BurstVerdict['staff'] =\n staffCount <= RISK_NOTICE_BURST.staff ? 'deliver' : 'fold'\n const pendingOwners = Array.isArray(snapshot.get('pendingOwners'))\n ? (snapshot.get('pendingOwners') as unknown[])\n : []\n const pendingStaff = Array.isArray(snapshot.get('pendingStaff'))\n ? (snapshot.get('pendingStaff') as unknown[])\n : []\n // How many of each notice were folded, however many the list keeps.\n const tally = (field: string, title: string) => {\n const current = (snapshot.get(field) as Record<string, number> | undefined) ?? {}\n return { ...current, [title]: Number(current[title] ?? 0) + 1 }\n }\n transaction.set(\n ref,\n {\n ledgerId,\n ...(fresh ? { windowStartedAtMs: nowMs } : {}),\n ownerCount,\n staffCount,\n ...(owners !== 'deliver'\n ? {\n pendingOwners: [\n ...pendingOwners,\n { kind: entry.kind, title: entry.title, noticeId: entry.noticeId, atMs: nowMs },\n ].slice(-RISK_NOTICE_BURST.digestListMax),\n pendingOwnerTally: tally('pendingOwnerTally', entry.title),\n pendingOwnerCount: FieldValue.increment(1),\n }\n : {}),\n ...(entry.alertStaff && staff !== 'deliver'\n ? {\n pendingStaff: [\n ...pendingStaff,\n { kind: entry.kind, title: entry.staffTitle, noticeId: entry.noticeId, atMs: nowMs },\n ].slice(-RISK_NOTICE_BURST.digestListMax),\n pendingStaffTally: tally('pendingStaffTally', entry.staffTitle),\n pendingStaffCount: FieldValue.increment(1),\n }\n : {}),\n updatedAtMs: nowMs,\n },\n { merge: true },\n )\n return { owners, staff }\n })\n } catch (error) {\n console.error('[risk-notice] burst ledger unavailable — delivering', error)\n return { owners: 'deliver', staff: 'deliver' }\n }\n}\n\n/** Where the owner half goes: uids, and the addresses already in hand. */\nasync function ownerRecipients(\n deps: RiskNoticeDeps,\n input: RiskEventInput,\n siteManagerUids: readonly string[],\n): Promise<{ uids: string[]; emails: Record<string, string | null> }> {\n const emails: Record<string, string | null> = {}\n const uids = new Set<string>()\n const definition = RISK_NOTICE_CATALOG[input.kind]\n if (input.recipients) {\n for (const recipient of input.recipients) {\n if (!recipient.uid) continue\n uids.add(recipient.uid)\n if (recipient.email) emails[recipient.uid] = recipient.email\n }\n return { uids: [...uids], emails }\n }\n if (input.kind === 'account-locked' || input.kind === 'account-unlocked') {\n if (input.userUid) uids.add(input.userUid)\n } else if (input.orgId) {\n const owners = await deps.listOwners(input.orgId).catch(() => [])\n for (const owner of owners) {\n uids.add(owner.uid)\n if (owner.email) emails[owner.uid] = owner.email\n }\n }\n if (definition.includeSiteManagers) for (const uid of siteManagerUids) uids.add(uid)\n return { uids: [...uids], emails }\n}\n\n/**\n * Tell the right people about one risk event. See the module header.\n * Never throws.\n */\nexport async function notifyRiskEvent(\n input: RiskEventInput,\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<RiskNoticeResult> {\n const result: RiskNoticeResult = {\n noticeId: null,\n duplicate: false,\n owners: emptyDelivery(),\n staff: { alerted: false, digested: false },\n error: null,\n }\n if (!isRiskEventKind(input?.kind)) {\n result.error = `Unknown risk event kind: ${String(input?.kind)}`\n return result\n }\n let deps: RiskNoticeDeps\n try {\n deps = resolveDeps(overrides)\n } catch (error) {\n result.error = (error as Error)?.message ?? String(error)\n return result\n }\n const definition = RISK_NOTICE_CATALOG[input.kind]\n const nowMs = deps.nowMs()\n const occurredAtMs = Number.isFinite(input.occurredAtMs) ? Number(input.occurredAtMs) : nowMs\n const noticeId = riskNoticeId(riskNoticeDedupeKey(input, occurredAtMs))\n result.noticeId = noticeId\n\n try {\n const context = await readContext(deps.firestore, input)\n const orgId = context.orgId\n const event: RiskEventInput = { ...input, orgId }\n\n /*\n * The values the OWNER half renders with. `staff.evidence` is absent by\n * construction: the owner record and the owner channels never hold it.\n */\n const supportEmail = abuseReportContactEmail('support') ?? ''\n const ownerValues: RiskNoticeValues = {\n 'workspace.name': context.orgName,\n 'item.label': input.item?.label ?? null,\n occurredAt: formatRiskNoticeTime(occurredAtMs),\n reference: input.reference ?? null,\n 'support.email': supportEmail || null,\n 'lock.message': input.lock?.message ?? null,\n 'lock.affected': input.lock?.affected ?? null,\n amount: input.amount ?? null,\n 'evidence.dueBy': input.evidenceDueByMs ? formatRiskNoticeTime(input.evidenceDueByMs) : null,\n }\n const staffValues: RiskNoticeValues = {\n ...ownerValues,\n 'workspace.name': context.orgName ?? orgId,\n 'staff.evidence': input.staffEvidence ?? null,\n }\n const actionParams: RiskActionParams = {\n itemPath: input.item?.path ?? null,\n noticeId,\n reviewId: input.reviewId ?? null,\n orgId,\n hostId: input.hostId ?? null,\n stripeUrl: input.stripeUrl ?? null,\n lockScope: input.lock?.scope ?? null,\n lockTargetId: input.lock?.targetId ?? null,\n }\n const owner = renderOwnerRiskNotice(input.kind, ownerValues)\n const staff = renderStaffRiskNotice(input.kind, staffValues)\n\n // ONE notice per dedupe key: `create` refuses a second.\n const noticeRef = deps.firestore.collection(RISK_NOTICE_COLLECTION).doc(noticeId)\n try {\n await noticeRef.create({\n kind: input.kind,\n severity: definition.severity,\n orgId,\n hostId: input.hostId ?? null,\n userUid: input.userUid ?? null,\n reviewId: input.reviewId ?? null,\n reference: input.reference ?? null,\n // What the owner surface renders, and nothing staff-only.\n ownerValues: stripEmpty(ownerValues),\n actionParams: stripEmpty(actionParams as Record<string, string | null | undefined>),\n occurredAtMs,\n createdAtMs: nowMs,\n createdAt: FieldValue.serverTimestamp(),\n ...(input.page ? { page: input.page } : {}),\n })\n } catch (error) {\n if (isAlreadyExists(error)) {\n result.duplicate = true\n return result\n }\n throw error\n }\n\n // A row-backed notice stamps the row, so the staff queue renders the\n // catalog's text and actions and the owner surface finds the row.\n if (input.reviewId && !RISK_NOTICE_CLOSING_KINDS.has(input.kind)) {\n await deps.firestore\n .collection(ABUSE_REPORT_COLLECTION)\n .doc(input.reviewId)\n .set(\n {\n riskNotice: {\n kind: input.kind,\n noticeId,\n itemLabel: input.item?.label ?? null,\n itemPath: input.item?.path ?? null,\n stripeUrl: input.stripeUrl ?? null,\n ownersNotifiedAtMs: nowMs,\n },\n },\n { merge: true },\n )\n .catch((error: unknown) =>\n console.error('[risk-notice] could not stamp the abuse row', error),\n )\n }\n\n // Anything folded in an hour that has now closed goes first.\n const ledgerId = ledgerIdFor(event)\n if (ledgerId) await flushLedger(deps, ledgerId).catch(() => undefined)\n\n const verdict: BurstVerdict =\n definition.neverDigest || !ledgerId\n ? { owners: 'deliver', staff: 'deliver' }\n : await countBurst(deps, ledgerId, {\n kind: input.kind,\n title: owner.title,\n staffTitle: staff.title,\n noticeId,\n alertStaff: definition.alertStaff,\n })\n\n // ---- The owners and admins ----\n const recipients = await ownerRecipients(deps, event, context.siteManagerUids)\n result.owners.recipients = recipients.uids.length\n const ownerActions = resolveOwnerRiskActions(input.kind, actionParams)\n const primary = ownerActions.find((action) => action.id === 'request-review') ?? ownerActions[0]\n if (verdict.owners === 'deliver') {\n if (recipients.uids.length) {\n await deps.notifyUsers(\n recipients.uids,\n {\n type: RISK_NOTICE_NOTIFICATION_TYPE,\n title: owner.title,\n body: `${owner.summary} ${owner.meaning}`.slice(0, 1000),\n link: primary?.href ?? riskHoldsPath(noticeId),\n ...(orgId ? { orgId } : {}),\n ...(input.hostId ? { hostId: input.hostId } : {}),\n },\n { emails: recipients.emails },\n )\n result.owners.inApp = recipients.uids.length\n }\n if (!definition.emailOwners) {\n result.owners.emailSkipped = 'This kind is not emailed.'\n } else if (input.emailOwners === false) {\n result.owners.emailSkipped = 'Staff chose not to email the owners for this event.'\n } else if (!recipients.uids.length) {\n result.owners.emailSkipped = 'Nobody to email: the workspace has no owner or admin on record.'\n } else {\n const merge = emailMerge({\n title: owner.title,\n summary: owner.summary,\n meaning: owner.meaning,\n steps: owner.steps,\n actions: ownerActions,\n context: { orgSlug: context.orgSlug, hostId: input.hostId ?? null, hostSubdomain: context.hostSubdomain },\n reference: input.reference ?? null,\n helpAnchor: definition.helpAnchor,\n workspaceName: context.orgName,\n lockMessage: input.lock?.message ?? null,\n })\n const rendered = await renderRiskNoticeEmail(\n riskNoticeEmailKey(input.kind),\n merge,\n await riskNoticeEmailBrand(input.kind, orgId),\n )\n const delivery = await emailPeople(deps, recipients, rendered, supportEmail)\n result.owners.emailed = delivery.sent\n result.owners.emailFailed = delivery.failed\n if (!delivery.sent && !delivery.failed) {\n result.owners.emailSkipped = 'No recipient has an email address on record.'\n }\n }\n } else {\n result.owners.digested = true\n if (verdict.owners === 'fold-first' && recipients.uids.length) {\n await deps.notifyUsers(\n recipients.uids,\n {\n type: RISK_NOTICE_NOTIFICATION_TYPE,\n title: 'More account notices are arriving',\n body:\n 'Several items on your workspace were held or flagged in the last hour. ' +\n 'We will send one summary instead of a message for each. Every item is listed on Holds & reviews.',\n link: riskHoldsPath(),\n ...(orgId ? { orgId } : {}),\n },\n { emails: recipients.emails },\n )\n result.owners.inApp = recipients.uids.length\n }\n }\n\n // ---- Staff ----\n if (definition.alertStaff) {\n if (verdict.staff === 'deliver') {\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: staff.title,\n body: staff.summary.slice(0, 1000),\n link: input.reviewId ? staffAbuseRowPath(input.reviewId) : '/admin/abuse-reports',\n })\n result.staff.alerted = true\n } else {\n result.staff.digested = true\n }\n }\n\n await noticeRef\n .set({ delivery: { owners: result.owners, staff: result.staff } }, { merge: true })\n .catch(() => undefined)\n return result\n } catch (error) {\n console.error('[risk-notice] a notice could not be delivered', error)\n result.error = (error as Error)?.message ?? String(error)\n return result\n }\n}\n\nfunction stripEmpty<T extends Record<string, string | null | undefined>>(values: T): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(values)) {\n if (typeof value === 'string' && value) out[key] = value\n }\n return out\n}\n\nfunction isAlreadyExists(error: unknown): boolean {\n const code = (error as { code?: unknown })?.code\n return code === 6 || code === 'already-exists' || code === 'ALREADY_EXISTS'\n}\n\n/** The flat merge map a notice email renders with. */\nfunction emailMerge(input: {\n title: string\n summary: string\n meaning: string\n steps: readonly string[]\n actions: readonly ResolvedRiskAction[]\n context: { orgSlug: string | null; hostId: string | null; hostSubdomain: string | null }\n reference: string | null\n helpAnchor: string\n workspaceName: string | null\n lockMessage: string | null\n}): Record<string, string> {\n const links = input.actions.flatMap((action) => {\n const path = emailPath(action.href, input.context)\n const url = path ? absolute(path) : ''\n return url ? [{ label: action.label, url }] : []\n })\n const primary = links.find((link) => link.label === 'Request a review') ?? links[0]\n const holdsPath = emailPath(riskHoldsPath(), input.context)\n return {\n 'notice.title': input.title,\n 'notice.summary': input.summary,\n 'notice.meaning': input.meaning,\n 'notice.steps': numbered(input.steps),\n 'notice.actions': links.map((link) => `${link.label}: ${link.url}`).join('\\n'),\n 'notice.primaryActionLabel': primary?.label ?? '',\n 'notice.primaryActionUrl': primary?.url ?? '',\n 'workspace.name': input.workspaceName ?? '',\n 'lock.message': input.lockMessage ?? '',\n reference: input.reference ?? '',\n 'holds.url': holdsPath ? absolute(holdsPath) : '',\n 'help.url': riskNoticeHelpUrl(input.helpAnchor),\n }\n}\n\n/**\n * One email per person — two owners have not agreed to see each other's\n * addresses. Platform sender, transactional, owed for the account.\n */\nasync function emailPeople(\n deps: RiskNoticeDeps,\n recipients: { uids: string[]; emails: Record<string, string | null> },\n rendered: RenderedRiskNoticeEmail,\n supportEmail: string,\n): Promise<{ sent: number; failed: number }> {\n let sent = 0\n let failed = 0\n const seen = new Set<string>()\n for (const uid of recipients.uids.slice(0, 50)) {\n const address = String(\n recipients.emails[uid] ?? (await deps.lookupEmail(uid).catch(() => null)) ?? '',\n )\n .trim()\n .toLowerCase()\n if (!address.includes('@') || seen.has(address)) continue\n seen.add(address)\n const outcome = await deps\n .sendEmail({\n to: address,\n subject: rendered.subject,\n text: rendered.text,\n ...(rendered.html ? { html: rendered.html } : {}),\n ...(rendered.fromName ? { fromName: rendered.fromName } : {}),\n ...(supportEmail ? { replyTo: supportEmail } : {}),\n context: 'risk-notice',\n owedFor: 'account',\n })\n .catch((error: unknown) => ({ sent: false as const, reason: 'provider-error' as const, detail: String(error) }))\n if (outcome.sent) {\n sent += 1\n // The platform's own account mail: its cost is the platform's, whatever\n // workspace it is about (AGL-1438).\n await meterPlatformEmail().catch(() => undefined)\n } else failed += 1\n }\n return { sent, failed }\n}\n\n/**\n * Send one ledger's digest if its hour has closed and anything was folded.\n * Returns whether a digest went out.\n */\nasync function flushLedger(deps: RiskNoticeDeps, ledgerId: string): Promise<boolean> {\n const nowMs = deps.nowMs()\n const ref = deps.firestore.collection(RISK_NOTICE_LEDGER_COLLECTION).doc(ledgerId)\n const taken = await deps.firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n if (!snapshot.exists) return null\n const startedAtMs = Number(snapshot.get('windowStartedAtMs') ?? 0)\n const ownerCount = Number(snapshot.get('pendingOwnerCount') ?? 0)\n const staffCount = Number(snapshot.get('pendingStaffCount') ?? 0)\n if (!ownerCount && !staffCount) return null\n if (startedAtMs && nowMs - startedAtMs < RISK_NOTICE_BURST.windowMs) return null\n transaction.set(\n ref,\n {\n pendingOwners: [],\n pendingStaff: [],\n pendingOwnerCount: 0,\n pendingStaffCount: 0,\n // Deleted rather than set to `{}`: a merge leaves an existing map's\n // keys in place when handed an empty one.\n pendingOwnerTally: FieldValue.delete(),\n pendingStaffTally: FieldValue.delete(),\n ownerCount: 0,\n staffCount: 0,\n windowStartedAtMs: 0,\n lastDigestAtMs: nowMs,\n },\n { merge: true },\n )\n return {\n owners: (snapshot.get('pendingOwnerTally') as Record<string, number> | undefined) ?? {},\n staff: (snapshot.get('pendingStaffTally') as Record<string, number> | undefined) ?? {},\n ownerCount,\n staffCount,\n }\n })\n if (!taken) return false\n const [scope, id] = [ledgerId.slice(0, ledgerId.indexOf(':')), ledgerId.slice(ledgerId.indexOf(':') + 1)]\n const orgId = scope === 'org' ? id : null\n const summarize = (tally: Record<string, number>, count: number) => {\n const entries = Object.entries(tally).sort((a, b) => b[1] - a[1])\n const lines = entries\n .slice(0, RISK_NOTICE_BURST.digestListMax)\n .map(([title, n]) => `• ${title}${n > 1 ? ` (${n})` : ''}`)\n const listed = entries\n .slice(0, RISK_NOTICE_BURST.digestListMax)\n .reduce((total, [, n]) => total + n, 0)\n if (count > listed) lines.push(`• …and ${count - listed} more`)\n return lines.join('\\n')\n }\n if (taken.ownerCount > 0) {\n const context = orgId\n ? await readContext(deps.firestore, { kind: 'email-held', orgId })\n : { orgName: null, orgSlug: null, siteManagerUids: [], orgId: null, hostSubdomain: null }\n const recipients =\n scope === 'user'\n ? { uids: [id], emails: {} as Record<string, string | null> }\n : orgId\n ? await ownerRecipients(deps, { kind: 'email-held', orgId }, [])\n : { uids: [] as string[], emails: {} as Record<string, string | null> }\n const title = `Summary: more items on ${context.orgName ?? 'your workspace'} were held or flagged`\n const summary = summarize(taken.owners, taken.ownerCount)\n if (recipients.uids.length) {\n await deps.notifyUsers(\n recipients.uids,\n {\n type: RISK_NOTICE_NOTIFICATION_TYPE,\n title,\n body: summary.slice(0, 1000),\n link: riskHoldsPath(),\n ...(orgId ? { orgId } : {}),\n },\n { emails: recipients.emails },\n )\n const holdsPath = emailPath(riskHoldsPath(), {\n orgSlug: context.orgSlug,\n hostId: null,\n hostSubdomain: null,\n })\n const rendered = await renderRiskNoticeEmail(RISK_NOTICE_DIGEST_EMAIL_KEY, {\n 'notice.title': title,\n 'notice.summary': renderRiskNoticeText(\n 'In the last hour, more items on {{workspace.name}} were held or flagged than we send one by one:',\n { 'workspace.name': context.orgName },\n ),\n 'notice.meaning': summary,\n 'notice.steps': numbered([\n 'Open Holds & reviews to see each item, its status and what to do.',\n 'If any of them is a mistake, choose Request a review on it.',\n ]),\n 'notice.actions': holdsPath && absolute(holdsPath) ? `View holds and reviews: ${absolute(holdsPath)}` : '',\n 'holds.url': holdsPath ? absolute(holdsPath) : '',\n 'help.url': riskNoticeHelpUrl(),\n reference: '',\n 'workspace.name': context.orgName ?? '',\n })\n await emailPeople(deps, recipients, rendered, abuseReportContactEmail('support') ?? '')\n }\n }\n if (taken.staffCount > 0) {\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: `Digest: ${taken.staffCount} more risk alerts on ${ledgerId}`,\n body: summarize(taken.staff, taken.staffCount).slice(0, 1000),\n link: '/admin/abuse-reports',\n })\n }\n return true\n}\n\n/**\n * Send every digest whose hour has closed (the hourly cron). Returns how\n * many went out. Never throws.\n */\nexport async function flushRiskNoticeDigests(\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<{ flushed: number; failed: number }> {\n const deps = resolveDeps(overrides)\n const cutoff = deps.nowMs() - RISK_NOTICE_BURST.windowMs\n let flushed = 0\n let failed = 0\n try {\n const snapshot = await deps.firestore\n .collection(RISK_NOTICE_LEDGER_COLLECTION)\n .where('windowStartedAtMs', '<=', cutoff)\n .where('windowStartedAtMs', '>', 0)\n .limit(200)\n .get()\n for (const doc of snapshot.docs) {\n try {\n if (await flushLedger(deps, doc.id)) flushed += 1\n } catch (error) {\n failed += 1\n console.error('[risk-notice] a digest could not be sent', error)\n }\n }\n } catch (error) {\n console.error('[risk-notice] the digest sweep failed', error)\n failed += 1\n }\n return { flushed, failed }\n}\n\n/**\n * The closing notice for a row staff decided: the kind the row's opening\n * notice names in `closesWith`, sent to the same people about the same item.\n * Silent (returns null) for a row with no risk notice or a kind that does\n * not close. Never throws.\n */\nexport async function closeRiskNotice(\n input: { reviewId: string; decision: 'released' | 'rejected' },\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<RiskNoticeResult | null> {\n try {\n const deps = resolveDeps(overrides)\n const row = await deps.firestore.collection(ABUSE_REPORT_COLLECTION).doc(input.reviewId).get()\n const stamp = row.get('riskNotice') as\n | { kind?: string; noticeId?: string; itemLabel?: string | null; itemPath?: string | null }\n | undefined\n if (!stamp?.kind || !isRiskEventKind(stamp.kind)) return null\n const closing = RISK_NOTICE_CATALOG[stamp.kind].closesWith?.[input.decision]\n if (!closing) return null\n return await notifyRiskEvent(\n {\n kind: closing,\n orgId: (row.get('orgId') as string | null) ?? null,\n hostId: (row.get('hostId') as string | null) ?? null,\n reviewId: input.reviewId,\n reference: (row.get('reference') as string | null) ?? null,\n item: stamp.itemLabel ? { label: stamp.itemLabel, path: stamp.itemPath ?? null } : null,\n dedupeKey: `${closing}:${input.reviewId}`,\n },\n overrides,\n )\n } catch (error) {\n console.error('[risk-notice] the closing notice failed', error)\n return null\n }\n}\n\n/** A review request as it is stored on the abuse-queue row. */\nexport interface RiskReviewRequest {\n atMs: number\n uid: string\n email: string | null\n note: string\n noticeId: string\n}\n\nexport type RiskReviewRequestOutcome =\n | { ok: true; reference: string | null; requestedAtMs: number }\n | { ok: false; status: 400 | 404 | 409 | 429; error: string }\n\n/**\n * An owner or admin asks for a review of a held or flagged item.\n *\n * APPENDS to the item's existing abuse-queue row — never a new row, and\n * never a decision: the row's `status` and its `heldSend` are not written\n * here, so no request can release a hold or lift a lock. Staff see the\n * request as a note on the row, and are notified with a link to it.\n */\nexport async function requestRiskReview(\n input: { orgId: string; noticeId: string; uid: string; email: string | null; note: string },\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<RiskReviewRequestOutcome> {\n const deps = resolveDeps(overrides)\n const note = String(input.note ?? '').trim().slice(0, RISK_REVIEW_NOTE_MAX)\n if (note.length < 10) {\n return { ok: false, status: 400, error: 'Tell us a little about it — at least a sentence.' }\n }\n const notice = await deps.firestore.collection(RISK_NOTICE_COLLECTION).doc(input.noticeId).get()\n if (!notice.exists || notice.get('orgId') !== input.orgId) {\n return { ok: false, status: 404, error: 'No such notice on this workspace.' }\n }\n const kind = notice.get('kind')\n const reviewId = String(notice.get('reviewId') ?? '')\n if (!isRiskEventKind(kind) || !RISK_NOTICE_CATALOG[kind].reviewable || !reviewId) {\n return { ok: false, status: 409, error: 'This notice has nothing to review. Contact support instead.' }\n }\n const nowMs = deps.nowMs()\n const rowRef = deps.firestore.collection(ABUSE_REPORT_COLLECTION).doc(reviewId)\n const outcome = await deps.firestore.runTransaction(async (transaction) => {\n const row = await transaction.get(rowRef)\n if (!row.exists || row.get('orgId') !== input.orgId) return 'missing' as const\n const requests = (\n Array.isArray(row.get('ownerReviewRequests')) ? row.get('ownerReviewRequests') : []\n ) as RiskReviewRequest[]\n const last = requests[requests.length - 1]\n if (last && nowMs - Number(last.atMs ?? 0) < RISK_REVIEW_REQUEST_COOLDOWN_MS) {\n return 'cooldown' as const\n }\n const entry: RiskReviewRequest = {\n atMs: nowMs,\n uid: input.uid,\n email: input.email,\n note,\n noticeId: input.noticeId,\n }\n // Only these three fields. Never `status`, never `heldSend`.\n transaction.set(\n rowRef,\n {\n ownerReviewRequests: [...requests, entry].slice(-RISK_REVIEW_REQUESTS_MAX),\n reviewRequestedAtMs: nowMs,\n updatedAt: FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n return { reference: (row.get('reference') as string | null) ?? null }\n })\n if (outcome === 'missing') {\n return { ok: false, status: 404, error: 'The case for this notice is gone. Contact support instead.' }\n }\n if (outcome === 'cooldown') {\n return {\n ok: false,\n status: 429,\n error: 'We already have a request on this. You can add another in a few minutes.',\n }\n }\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: `Review requested${outcome.reference ? ` — ${outcome.reference}` : ''}`,\n body: `A workspace owner or admin asked for a review: \"${note.slice(0, 280)}\"`,\n link: staffAbuseRowPath(reviewId),\n })\n // The person who asked is told it arrived — account mail, platform brand.\n if (input.email) {\n const rendered = await renderRiskNoticeEmail(RISK_REVIEW_REQUESTED_EMAIL_KEY, {\n 'notice.title': 'We received your review request',\n 'notice.summary':\n 'Your request reached our review team' +\n (outcome.reference ? ` under the reference ${outcome.reference}` : '') +\n '. A person reads every request.',\n 'notice.meaning': 'Nothing changes until the review is done; you will get a notice either way.',\n 'notice.steps': numbered([\n 'Nothing more is needed.',\n 'You can follow the item\\'s status on Holds & reviews.',\n ]),\n reference: outcome.reference ?? '',\n 'help.url': riskNoticeHelpUrl('requesting-a-review'),\n })\n await emailPeople(\n deps,\n { uids: [input.uid], emails: { [input.uid]: input.email } },\n rendered,\n abuseReportContactEmail('support') ?? '',\n ).catch(() => undefined)\n }\n return { ok: true, reference: outcome.reference, requestedAtMs: nowMs }\n}\n\n/** One owner notice, as the Holds & reviews page renders it. */\nexport interface OwnerRiskNoticeView {\n noticeId: string\n kind: RiskEventKind\n severity: string\n title: string\n summary: string\n meaning: string\n steps: string[]\n actions: ResolvedRiskAction[]\n reference: string | null\n occurredAtMs: number\n helpUrl: string\n /** The review state, for a notice with a row. */\n status: 'held' | 'in-review' | 'released' | 'rejected' | 'closed' | null\n reviewable: boolean\n reviewRequests: Array<{ atMs: number; note: string; mine: boolean }>\n /** A page notice: the page by name, where it lives, and what visitors see (AGL-3374). */\n page: OwnerPageHoldFacts | null\n}\n\n/** A page notice's facts, as every owner surface renders them (AGL-3374). */\nexport interface OwnerPageHoldFacts {\n /** `the \"Video detail\" template (/videos/:slug)`. */\n label: string\n /** Where it was seen, and the layout or component the content lives in. */\n details: string[]\n /** What visitors receive meanwhile. */\n visitorSentence: string\n /** The page, and the layout or component, the hold is shown on. */\n targets: HeldPageTarget[]\n}\n\n/**\n * A page notice's facts from its stored subject, or — for a notice written\n * before notices carried one — from its label and item path. Pure.\n */\nexport function ownerPageHoldFacts(\n kind: string,\n notice: { page?: unknown; ownerValues?: unknown; actionParams?: unknown },\n): OwnerPageHoldFacts | null {\n if (!isPageHoldNoticeKind(kind)) return null\n const subject = notice.page as HeldPageSubject | undefined\n if (subject && typeof subject === 'object' && subject.screenId) {\n return {\n label: heldPageLabel(subject),\n details: heldPageDetails(subject),\n visitorSentence: heldPageVisitorSentence(subject),\n targets: heldPageTargets(subject),\n }\n }\n const values = (notice.ownerValues ?? {}) as Record<string, string | undefined>\n const params = (notice.actionParams ?? {}) as Record<string, string | undefined>\n const parsed = parseHeldPageItemPath(params.itemPath)\n return {\n label: values['item.label'] || 'a page on your site',\n details: [],\n visitorSentence:\n kind === 'page-flagged'\n ? heldPageVisitorSentence({ visitorView: 'live' })\n : 'Visitors see the previous version of this page, or a not-found page if it was never published, until the review is done.',\n targets: parsed ? [{ type: 'screen', id: parsed.screenId }] : [],\n }\n}\n\n/** One open page hold on a site, as the site's console surfaces show it (AGL-3374). */\nexport interface PageHoldView extends OwnerPageHoldFacts {\n noticeId: string\n kind: PageHoldNoticeKind\n status: OwnerRiskNoticeView['status']\n /** The status chip. Never paired with a release control. */\n chip: { label: string; color: 'warning' | 'error' | 'info' }\n reference: string | null\n occurredAtMs: number\n /** Whether a review can be requested on it (the viewer's role decides the rest). */\n reviewable: boolean\n /** When anyone on the workspace last asked for a review, if they did. */\n reviewRequestedAtMs: number | null\n}\n\n/**\n * Every page on a site that is held, flagged or not approved (AGL-3374): the\n * `riskNotices` of the two page kinds for this host, newest first — one\n * query on (`hostId`, `kind`, `createdAtMs`), which\n * `cloud/firebase-firestore.indexes.json` holds — and each notice's row for\n * its state. A released or cleared page is not listed.\n */\nexport async function listHostPageHolds(\n input: { hostId: string; limit?: number },\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<PageHoldView[]> {\n const deps = resolveDeps(overrides)\n const snapshot = await deps.firestore\n .collection(RISK_NOTICE_COLLECTION)\n .where('hostId', '==', input.hostId)\n .where('kind', 'in', [...PAGE_HOLD_NOTICE_KINDS])\n .orderBy('createdAtMs', 'desc')\n .limit(Math.min(Math.max(input.limit ?? 50, 1), 100))\n .get()\n const reviewIds = [\n ...new Set(snapshot.docs.map((doc) => String(doc.get('reviewId') ?? '')).filter(Boolean)),\n ]\n const rows = new Map<string, FirebaseFirestore.DocumentSnapshot>()\n if (reviewIds.length) {\n const read = await deps.firestore.getAll(\n ...reviewIds.map((id) => deps.firestore.collection(ABUSE_REPORT_COLLECTION).doc(id)),\n )\n for (const row of read) rows.set(row.id, row)\n }\n const views: PageHoldView[] = []\n const seenRows = new Set<string>()\n for (const doc of snapshot.docs) {\n const kind = doc.get('kind')\n if (!isPageHoldNoticeKind(kind)) continue\n const reviewId = String(doc.get('reviewId') ?? '')\n // One entry per case: a row re-notified keeps its newest notice.\n if (reviewId && seenRows.has(reviewId)) continue\n if (reviewId) seenRows.add(reviewId)\n const row = reviewId ? rows.get(reviewId) : undefined\n const status = row?.exists ? ownerRowStatus(row) : kind === 'page-flagged' ? 'in-review' : 'held'\n if (!isOpenPageHoldStatus(status)) continue\n const facts = ownerPageHoldFacts(kind, {\n page: doc.get('page'),\n ownerValues: doc.get('ownerValues'),\n actionParams: doc.get('actionParams'),\n })\n if (!facts) continue\n views.push({\n ...facts,\n noticeId: doc.id,\n kind,\n status,\n chip: pageHoldChip(kind, status),\n reference: (doc.get('reference') as string | null) ?? null,\n occurredAtMs: Number(doc.get('occurredAtMs') ?? doc.get('createdAtMs') ?? 0),\n reviewable: RISK_NOTICE_CATALOG[kind].reviewable && Boolean(row?.exists),\n reviewRequestedAtMs:\n typeof row?.get('reviewRequestedAtMs') === 'number'\n ? (row.get('reviewRequestedAtMs') as number)\n : null,\n })\n }\n return views\n}\n\n/**\n * The workspace's notices, newest first, rendered for its owners: the\n * catalog's owner half only, and the row's decision state — never its\n * evidence, never its staff notes.\n */\nexport async function listOwnerRiskNotices(\n input: { orgId: string; viewerUid: string; limit?: number },\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<OwnerRiskNoticeView[]> {\n const deps = resolveDeps(overrides)\n const snapshot = await deps.firestore\n .collection(RISK_NOTICE_COLLECTION)\n .where('orgId', '==', input.orgId)\n .orderBy('createdAtMs', 'desc')\n .limit(Math.min(Math.max(input.limit ?? 50, 1), 100))\n .get()\n const reviewIds = [\n ...new Set(\n snapshot.docs.map((doc) => String(doc.get('reviewId') ?? '')).filter(Boolean),\n ),\n ]\n const rows = new Map<string, FirebaseFirestore.DocumentSnapshot>()\n if (reviewIds.length) {\n const read = await deps.firestore.getAll(\n ...reviewIds.map((id) => deps.firestore.collection(ABUSE_REPORT_COLLECTION).doc(id)),\n )\n for (const row of read) rows.set(row.id, row)\n }\n // The reader's own console paths: the org's slug and each site's\n // subdomain, read once for the page rather than per notice.\n const org = await deps.firestore.collection('orgs').doc(input.orgId).get()\n const orgSlug = String(org.get('slug') ?? '') || null\n const hostIds = [\n ...new Set(snapshot.docs.map((doc) => String(doc.get('hostId') ?? '')).filter(Boolean)),\n ]\n const subdomains = new Map<string, string | null>()\n if (hostIds.length) {\n const hosts = await deps.firestore.getAll(\n ...hostIds.map((id) => deps.firestore.collection('hosts').doc(id)),\n )\n for (const host of hosts) subdomains.set(host.id, String(host.get('subdomain') ?? '') || null)\n }\n const views: OwnerRiskNoticeView[] = []\n for (const doc of snapshot.docs) {\n const kind = doc.get('kind')\n if (!isRiskEventKind(kind)) continue\n const values = (doc.get('ownerValues') as RiskNoticeValues | undefined) ?? {}\n const params = (doc.get('actionParams') as RiskActionParams | undefined) ?? {}\n const definition = RISK_NOTICE_CATALOG[kind]\n const copy = renderOwnerRiskNotice(kind, values)\n const reviewId = String(doc.get('reviewId') ?? '')\n const row = reviewId ? rows.get(reviewId) : undefined\n const requests = (\n row && Array.isArray(row.get('ownerReviewRequests')) ? row.get('ownerReviewRequests') : []\n ) as RiskReviewRequest[]\n views.push({\n noticeId: doc.id,\n kind,\n severity: definition.severity,\n ...copy,\n actions: resolveOwnerRiskActions(kind, { ...params, noticeId: doc.id }).flatMap((action) => {\n const hostId = String(doc.get('hostId') ?? '') || null\n const href = emailPath(action.href, {\n orgSlug,\n hostId,\n hostSubdomain: hostId ? (subdomains.get(hostId) ?? null) : null,\n })\n return href ? [{ ...action, href }] : []\n }),\n reference: (doc.get('reference') as string | null) ?? null,\n occurredAtMs: Number(doc.get('occurredAtMs') ?? doc.get('createdAtMs') ?? 0),\n helpUrl: riskNoticeHelpUrl(definition.helpAnchor),\n status: row?.exists ? ownerRowStatus(row) : null,\n reviewable: definition.reviewable && Boolean(row?.exists),\n reviewRequests: requests\n .filter((request) => request.noticeId === doc.id)\n .map((request) => ({\n atMs: Number(request.atMs ?? 0),\n note: String(request.note ?? ''),\n mine: request.uid === input.viewerUid,\n })),\n page: ownerPageHoldFacts(kind, {\n page: doc.get('page'),\n ownerValues: values,\n actionParams: params,\n }),\n })\n }\n return views\n}\n\n/** A row's state in the owner's words. Pure. */\nexport function ownerRowStatus(\n row: Pick<FirebaseFirestore.DocumentSnapshot, 'get'>,\n): OwnerRiskNoticeView['status'] {\n const held = row.get('heldSend') as { state?: string } | undefined\n if (held?.state === 'released') return 'released'\n if (held?.state === 'rejected') return 'rejected'\n const status = String(row.get('status') ?? 'open')\n if (status === 'dismissed') return held ? 'released' : 'closed'\n if (status === 'actioned') return held ? 'rejected' : 'closed'\n if (held?.state === 'held') return 'held'\n return 'in-review'\n}\n"],"names":["createHash","ABUSE_REPORT_COLLECTION","abuseReportContactEmail","DOCS_BASE_URL","isRiskEventKind","renderOwnerRiskNotice","renderRiskNoticeText","renderStaffRiskNotice","resolveOwnerRiskActions","RISK_NOTICE_CATALOG","RISK_NOTICE_CLOSING_KINDS","RISK_NOTICE_DIGEST_EMAIL_KEY","RISK_NOTICE_HELP_PATH","RISK_NOTICE_WORKSPACE_BRANDED","riskNoticeEmailKey","sendEmail","sendEmailImpl","heldPageDetails","heldPageLabel","heldPageTargets","heldPageVisitorSentence","isOpenPageHoldStatus","isPageHoldNoticeKind","PAGE_HOLD_NOTICE_KINDS","pageHoldChip","parseHeldPageItemPath","FieldValue","findUserByUidAcrossPools","firebaseAdmin","notifyStaff","notifyStaffImpl","notifyUsers","notifyUsersImpl","meterPlatformEmail","listOrgMembers","orgSystemEmailBrand","renderSystemEmailContent","systemEmailBrand","RISK_REVIEW_REQUESTED_EMAIL_KEY","RISK_NOTICE_COLLECTION","RISK_NOTICE_LEDGER_COLLECTION","RISK_NOTICE_NOTIFICATION_TYPE","RISK_NOTICE_BURST","owners","staff","windowMs","digestListMax","RISK_REVIEW_NOTE_MAX","RISK_REVIEW_REQUESTS_MAX","RISK_REVIEW_REQUEST_COOLDOWN_MS","defaultListOwners","orgId","members","filter","member","role","map","uid","String","$id","email","defaultLookupEmail","found","catch","record","trim","includes","resolveDeps","overrides","firestore","app","listOwners","lookupEmail","nowMs","Date","now","consoleOrigin","process","env","NEXT_PUBLIC_CONSOLE_URL","replace","riskNoticeHelpUrl","anchor","formatRiskNoticeTime","ms","date","Number","isFinite","getTime","toLocaleString","month","day","year","hour","minute","hour12","timeZone","riskNoticeId","dedupeKey","update","digest","slice","riskNoticeDedupeKey","input","occurredAtMs","reviewId","kind","hostId","userUid","item","path","label","join","ledgerIdFor","riskHoldsPath","noticeId","encodeURIComponent","staffAbuseRowPath","absolute","test","origin","emailPath","context","startsWith","orgSlug","hostSubdomain","length","resolveHostConsolePaths","entries","resolved","Map","hostIds","Set","entry","Boolean","hosts","getAll","id","collection","doc","hostFacts","host","subdomain","get","orgOf","orgIds","orgs","slugs","org","facts","href","set","readContext","siteManagerUids","exists","roles","Object","orgName","riskNoticeEmailBrand","has","renderRiskNoticeEmail","key","merge","brand","content","riskNoticeFallback","fromName","lines","line","subject","text","numbered","steps","step","index","emptyDelivery","skipped","recipients","inApp","emailed","emailFailed","emailSkipped","digested","countBurst","deps","ledgerId","ref","runTransaction","transaction","snapshot","startedAtMs","fresh","ownerCount","staffCount","alertStaff","pendingOwners","Array","isArray","pendingStaff","tally","field","title","current","windowStartedAtMs","atMs","pendingOwnerTally","pendingOwnerCount","increment","staffTitle","pendingStaffTally","pendingStaffCount","updatedAtMs","error","console","ownerRecipients","emails","uids","definition","recipient","add","owner","includeSiteManagers","notifyRiskEvent","result","duplicate","alerted","message","ownerActions","event","supportEmail","ownerValues","occurredAt","reference","lock","affected","amount","evidenceDueByMs","staffValues","staffEvidence","actionParams","itemPath","stripeUrl","lockScope","scope","lockTargetId","targetId","noticeRef","create","severity","stripEmpty","createdAtMs","createdAt","serverTimestamp","page","isAlreadyExists","riskNotice","itemLabel","ownersNotifiedAtMs","flushLedger","undefined","verdict","neverDigest","primary","find","action","type","body","summary","meaning","link","emailOwners","emailMerge","actions","helpAnchor","workspaceName","lockMessage","rendered","delivery","emailPeople","sent","failed","values","out","value","code","links","flatMap","url","holdsPath","seen","address","toLowerCase","outcome","to","html","replyTo","owedFor","reason","detail","taken","delete","lastDigestAtMs","indexOf","summarize","count","sort","a","b","n","listed","reduce","total","push","flushRiskNoticeDigests","cutoff","flushed","where","limit","docs","closeRiskNotice","row","stamp","closing","closesWith","decision","requestRiskReview","notice","note","ok","status","reviewable","rowRef","last","requests","ownerReviewRequests","reviewRequestedAtMs","updatedAt","requestedAtMs","ownerPageHoldFacts","screenId","details","visitorSentence","targets","params","parsed","visitorView","listHostPageHolds","orderBy","Math","min","max","reviewIds","rows","read","views","seenRows","ownerRowStatus","chip","listOwnerRiskNotices","subdomains","copy","helpUrl","reviewRequests","request","mine","viewerUid","held","state"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/risk-notice.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * notifyRiskEvent: ONE WAY TO TELL PEOPLE SOMETHING WAS HELD (AGL-3368).\n *\n * Every place the platform holds, flags, locks or pauses something on a\n * workspace calls this, with the kind from the catalog\n * (`@aglyn/shared-util-email/risk-notice-catalog`) and the facts it knows.\n * This decides who hears, on which channel, and in which words:\n *\n * - THE WORKSPACE'S OWNERS AND ADMINS (the org roster's `owner`/`admin`\n * roles, the audience `notifyOrgAdmins` reaches), plus — for a kind about\n * one site's sales or pages — the site's managers, the audience\n * `notifyHostManagers` reaches and the one a sale's fraud signal always\n * went to. An account lock goes to the account's own person instead.\n * They get an in-app notification (`system.riskNotice`) and, for every\n * kind the catalog marks `emailOwners`, an email.\n * - STAFF, through `notifyStaff`, for the kinds the catalog marks\n * `alertStaff`. A row-backed alert links the row itself.\n *\n * ## The owner email is account mail from the platform\n *\n * It is sent with no sending identity and no `audience: 'tenant'`, so it\n * leaves on the platform's own configured sender — never the workspace's.\n * That is what lets a LOCKED workspace's owners receive their lock notice:\n * a suspended workspace's own identity refuses every send\n * (`workspace-suspended`), but this mail is not the workspace's. It is\n * `owedFor: 'account'`, carries no marketing context, and ignores\n * notification preferences: the owner did not opt into it and cannot opt\n * out of being told their email was held. The tenant phishing screen never\n * sees it — that screen asks only about a site's mail.\n *\n * ## One notice per event, and a digest under a burst\n *\n * A notice's id is a hash of its dedupe key (by default the kind and the\n * abuse-queue row), created once — so a redelivered webhook or a re-held\n * message never tells anyone twice, on top of the one-alert-per-row rule\n * the row writers already keep.\n *\n * Past {@link RISK_NOTICE_BURST.owners} notices in an hour for one\n * workspace (and {@link RISK_NOTICE_BURST.staff} for staff), further notices\n * are recorded but not delivered one by one. The first one folded sends a\n * short \"more are arriving\" note in-app; when the hour closes,\n * {@link flushRiskNoticeDigests} (hourly cron, and the next notice after the\n * window) sends ONE summary. A workspace under attack gets a digest, not\n * five hundred emails. Locks, lifts and cancellations (`neverDigest`) always\n * go out on their own.\n *\n * ## Never throws\n *\n * Every caller is a write that has already happened — a hold, a lock, a\n * webhook. A failed notice is reported in the result (the lockdown route\n * shows it as an unconfirmed step) and logged; it never undoes the event.\n *=========================================*/\n\nimport { createHash } from 'crypto'\nimport { ABUSE_REPORT_COLLECTION, abuseReportContactEmail } from '@aglyn/aglyn/app-utils/abuse-report'\nimport { DOCS_BASE_URL } from '@aglyn/aglyn/app-utils/docs-help'\nimport {\n isRiskEventKind,\n renderOwnerRiskNotice,\n renderRiskNoticeText,\n renderStaffRiskNotice,\n resolveOwnerRiskActions,\n RISK_NOTICE_CATALOG,\n RISK_NOTICE_CLOSING_KINDS,\n RISK_NOTICE_DIGEST_EMAIL_KEY,\n RISK_NOTICE_HELP_PATH,\n RISK_NOTICE_WORKSPACE_BRANDED,\n riskNoticeEmailKey,\n type ResolvedRiskAction,\n type RiskActionParams,\n type RiskEventKind,\n type RiskNoticeValues,\n} from '@aglyn/shared-util-email/risk-notice-catalog'\nimport {\n sendEmail as sendEmailImpl,\n type SendEmailOptions,\n type SendEmailResult,\n} from '@aglyn/shared-util-email'\nimport {\n heldPageDetails,\n heldPageLabel,\n heldPageTargets,\n heldPageVisitorSentence,\n isOpenPageHoldStatus,\n isPageHoldNoticeKind,\n PAGE_HOLD_NOTICE_KINDS,\n pageHoldChip,\n parseHeldPageItemPath,\n type HeldPageSubject,\n type HeldPageTarget,\n type PageHoldNoticeKind,\n} from '@aglyn/shared-util-email/held-page'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { findUserByUidAcrossPools } from './auth-pools'\nimport firebaseAdmin from './firebase-admin'\nimport {\n type NotificationPayload,\n notifyStaff as notifyStaffImpl,\n notifyUsers as notifyUsersImpl,\n type NotifyUsersOptions,\n} from './notifications'\nimport { meterPlatformEmail } from './email-metering'\nimport { listOrgMembers } from './organizations'\nimport {\n orgSystemEmailBrand,\n renderSystemEmailContent,\n type SystemEmailBrand,\n systemEmailBrand,\n} from './render-system-email'\nimport { RISK_REVIEW_REQUESTED_EMAIL_KEY } from '@aglyn/shared-util-email/risk-notice-emails'\n\n/** Admin-SDK only; no client rule opens it. One document per notice. */\nexport const RISK_NOTICE_COLLECTION = 'riskNotices'\n\n/** Admin-SDK only: each workspace's burst window and folded notices. */\nexport const RISK_NOTICE_LEDGER_COLLECTION = 'riskNoticeLedger'\n\n/** The in-app notification type every owner notice is written as. */\nexport const RISK_NOTICE_NOTIFICATION_TYPE = 'system.riskNotice' as const\n\n/**\n * The burst allowance: how many notices one workspace's owners, and staff\n * about one workspace, receive one by one in {@link RISK_NOTICE_BURST.windowMs}\n * before the rest are folded into a digest.\n */\nexport const RISK_NOTICE_BURST = {\n owners: 5,\n staff: 10,\n windowMs: 60 * 60 * 1000,\n /** How many folded notices a digest lists by name. */\n digestListMax: 25,\n} as const\n\n/** Longest review-request note stored. */\nexport const RISK_REVIEW_NOTE_MAX = 2000\n/** How many review requests one row keeps. */\nexport const RISK_REVIEW_REQUESTS_MAX = 20\n/** How soon the same workspace may ask again about the same row. */\nexport const RISK_REVIEW_REQUEST_COOLDOWN_MS = 10 * 60 * 1000\n\n/** What a source knows about the thing the notice is about. */\nexport interface RiskEventItem {\n /** In the owner's words: `the campaign \"Spring sale\"`, `order 1042`. */\n label: string\n /**\n * Its console page, in the stored-notification shape (`/org/…` or\n * `/{hostId}/…`) the console rewrites when the link is followed.\n */\n path?: string | null\n}\n\nexport interface RiskEventInput {\n kind: RiskEventKind\n orgId: string | null\n hostId?: string | null\n /** An account lock's person. */\n userUid?: string | null\n /** The abuse-queue row, when the event has one. */\n reviewId?: string | null\n reference?: string | null\n item?: RiskEventItem | null\n occurredAtMs?: number\n /** `$56.00 USD`. */\n amount?: string | null\n evidenceDueByMs?: number | null\n /** Staff only: the Stripe Dashboard page for the charge, review or dispute. */\n stripeUrl?: string | null\n lock?: {\n /** The customer-facing message the lock stores, verbatim. */\n message?: string | null\n /** What the lock or pause covers, in plain words. */\n affected?: string | null\n scope?: string | null\n targetId?: string | null\n } | null\n /**\n * A held or flagged page, described (AGL-3374). Stored on the notice so\n * the console can put the hold on the page, template, layout or component\n * it is about, and name it there. Owner-safe: no signal, no evidence.\n */\n page?: HeldPageSubject | null\n /** Staff only: what the screen or Stripe reported. Never shown to owners. */\n staffEvidence?: string | null\n /** Overrides the default dedupe key. */\n dedupeKey?: string | null\n /**\n * `false` to send no owner EMAIL for this event (a lock placed under a\n * legal hold). The in-app notice and the record are still written.\n */\n emailOwners?: boolean\n /**\n * Exactly who the owner half goes to, in place of the workspace's owners\n * and admins: a batch that already resolved its people and groups several\n * events into one notice per person (the lockdown's \"Resend owner\n * notice\").\n */\n recipients?: ReadonlyArray<{ uid: string; email: string | null }>\n}\n\nexport interface RiskNoticeDelivery {\n /** How many people the owner half was for. */\n recipients: number\n /** In-app notifications written (before per-person mutes). */\n inApp: number\n emailed: number\n emailFailed: number\n /** Why no email went, when none did. */\n emailSkipped: string | null\n /** Folded into the next digest instead of delivered now. */\n digested: boolean\n}\n\nexport interface RiskNoticeResult {\n noticeId: string | null\n /** The same notice was already delivered: nothing was sent now. */\n duplicate: boolean\n owners: RiskNoticeDelivery\n staff: { alerted: boolean; digested: boolean }\n /** Set when the notice could not be processed at all. */\n error: string | null\n}\n\n/** Everything the seam touches, injectable for specs. */\nexport interface RiskNoticeDeps {\n firestore: FirebaseFirestore.Firestore\n sendEmail: (options: SendEmailOptions) => Promise<SendEmailResult>\n notifyUsers: (\n uids: Iterable<string>,\n payload: NotificationPayload,\n options?: NotifyUsersOptions,\n ) => Promise<void>\n notifyStaff: (payload: NotificationPayload) => Promise<void>\n listOwners: (orgId: string) => Promise<Array<{ uid: string; email: string | null }>>\n lookupEmail: (uid: string) => Promise<string | null>\n nowMs: () => number\n}\n\nasync function defaultListOwners(orgId: string) {\n const members = await listOrgMembers(orgId)\n return members\n .filter((member) => member.role === 'owner' || member.role === 'admin')\n .map((member) => ({\n uid: String(member.$id),\n email: (member as { email?: string | null }).email ?? null,\n }))\n}\n\nasync function defaultLookupEmail(uid: string): Promise<string | null> {\n const found = await findUserByUidAcrossPools(uid).catch(() => null)\n const email = String(found?.record?.email ?? '').trim()\n return email.includes('@') ? email : null\n}\n\nfunction resolveDeps(overrides: Partial<RiskNoticeDeps> = {}): RiskNoticeDeps {\n return {\n firestore: overrides.firestore ?? firebaseAdmin.app().firestore(),\n sendEmail: overrides.sendEmail ?? sendEmailImpl,\n notifyUsers: overrides.notifyUsers ?? notifyUsersImpl,\n notifyStaff: overrides.notifyStaff ?? notifyStaffImpl,\n listOwners: overrides.listOwners ?? defaultListOwners,\n lookupEmail: overrides.lookupEmail ?? defaultLookupEmail,\n nowMs: overrides.nowMs ?? (() => Date.now()),\n }\n}\n\nfunction consoleOrigin(): string {\n return (process.env.NEXT_PUBLIC_CONSOLE_URL ?? '').trim().replace(/\\/+$/, '')\n}\n\n/** The help page every notice links, on the docs site. */\nexport function riskNoticeHelpUrl(anchor?: string): string {\n return `${DOCS_BASE_URL.replace(/\\/+$/, '')}${RISK_NOTICE_HELP_PATH}${anchor ? `#${anchor}` : ''}`\n}\n\n/** `Sep 28, 2026, 16:31 UTC` — one format for every notice. */\nexport function formatRiskNoticeTime(ms: number): string {\n const date = new Date(ms)\n if (!Number.isFinite(date.getTime())) return ''\n return `${date.toLocaleString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZone: 'UTC',\n })} UTC`\n}\n\n/** The id of the notice a dedupe key names: hex, like the abuse queue's. */\nexport function riskNoticeId(dedupeKey: string): string {\n return createHash('sha256').update(`risk-notice:${dedupeKey}`).digest('hex').slice(0, 40)\n}\n\n/** The default dedupe key: one notice per kind per row, else per event. */\nexport function riskNoticeDedupeKey(input: RiskEventInput, occurredAtMs: number): string {\n if (input.dedupeKey) return input.dedupeKey\n if (input.reviewId) return `${input.kind}:${input.reviewId}`\n return [\n input.kind,\n input.orgId ?? '',\n input.hostId ?? '',\n input.userUid ?? '',\n input.item?.path ?? input.item?.label ?? '',\n String(occurredAtMs),\n ].join(':')\n}\n\n/** The ledger a notice's burst allowance is counted on. */\nfunction ledgerIdFor(input: RiskEventInput): string | null {\n if (input.orgId) return `org:${input.orgId}`\n if (input.userUid) return `user:${input.userUid}`\n if (input.hostId) return `host:${input.hostId}`\n return null\n}\n\n/** The console path an owner's Holds & reviews page is at, in stored shape. */\nexport function riskHoldsPath(noticeId?: string | null): string {\n return `/org/settings/holds${noticeId ? `?notice=${encodeURIComponent(noticeId)}` : ''}`\n}\n\n/** A staff deep link to one abuse-queue row. */\nexport function staffAbuseRowPath(reviewId: string): string {\n return `/admin/abuse-reports?report=${encodeURIComponent(reviewId)}`\n}\n\n/** Absolute, for an email: the console origin before a stored path. */\nfunction absolute(path: string | null | undefined): string {\n if (!path) return ''\n if (/^https?:\\/\\//.test(path)) return path\n const origin = consoleOrigin()\n return origin ? `${origin}${path}` : ''\n}\n\n/**\n * The stored-shape console links resolve per reader in the console\n * (`normalizeNotificationLink`); an EMAIL has no reader context, so its\n * links are rewritten here from the org slug and the site's subdomain.\n */\nfunction emailPath(\n path: string,\n context: { orgSlug: string | null; hostId: string | null; hostSubdomain: string | null },\n): string | null {\n if (/^https?:\\/\\//.test(path)) return path\n if (path.startsWith('/admin/') || path.startsWith('/manage/')) return path\n if (path === '/org' || path.startsWith('/org/') || path.startsWith('/org?')) {\n return context.orgSlug ? `/${context.orgSlug}${path.slice(4)}` : null\n }\n if (context.hostId && (path === `/${context.hostId}` || path.startsWith(`/${context.hostId}/`) || path.startsWith(`/${context.hostId}?`))) {\n return context.orgSlug && context.hostSubdomain\n ? `/${context.orgSlug}/hosts/${context.hostSubdomain}${path.slice(context.hostId.length + 1)}`\n : null\n }\n return path\n}\n\n/**\n * Stored-shape site paths (`/{hostId}/…`) as console routes, for a reader\n * with no org context of its own — staff reading the abuse queue\n * (AGL-3374). One read of the sites and one of their workspaces for the\n * whole batch; a path that cannot be resolved is left out.\n */\nexport async function resolveHostConsolePaths(\n firestore: FirebaseFirestore.Firestore,\n entries: ReadonlyArray<{ hostId: string; orgId?: string | null; path: string }>,\n): Promise<Map<string, string>> {\n const resolved = new Map<string, string>()\n const hostIds = [...new Set(entries.map((entry) => entry.hostId).filter(Boolean))]\n if (!hostIds.length) return resolved\n const hosts = await firestore.getAll(\n ...hostIds.map((id) => firestore.collection('hosts').doc(id)),\n )\n const hostFacts = new Map(\n hosts.map((host) => [\n host.id,\n {\n subdomain: String(host.get('subdomain') ?? '') || null,\n orgId: String(host.get('orgId') ?? '') || null,\n },\n ]),\n )\n // The row names its workspace; a site document may too.\n const orgOf = (entry: { hostId: string; orgId?: string | null }) =>\n entry.orgId || hostFacts.get(entry.hostId)?.orgId || null\n const orgIds = [...new Set(entries.map(orgOf).filter(Boolean))] as string[]\n const orgs = orgIds.length\n ? await firestore.getAll(...orgIds.map((id) => firestore.collection('orgs').doc(id)))\n : []\n const slugs = new Map(orgs.map((org) => [org.id, String(org.get('slug') ?? '') || null]))\n for (const entry of entries) {\n const facts = hostFacts.get(entry.hostId)\n const orgId = orgOf(entry)\n const href = emailPath(entry.path, {\n orgSlug: orgId ? (slugs.get(orgId) ?? null) : null,\n hostId: entry.hostId,\n hostSubdomain: facts?.subdomain ?? null,\n })\n if (href && href !== entry.path) resolved.set(`${entry.hostId}\\n${entry.path}`, href)\n }\n return resolved\n}\n\n/** The workspace facts every notice renders with. Never throws. */\nasync function readContext(\n firestore: FirebaseFirestore.Firestore,\n input: RiskEventInput,\n): Promise<{\n orgId: string | null\n orgName: string | null\n orgSlug: string | null\n hostSubdomain: string | null\n siteManagerUids: string[]\n}> {\n let orgId = input.orgId ?? null\n let hostSubdomain: string | null = null\n let siteManagerUids: string[] = []\n if (input.hostId) {\n const host = await firestore.collection('hosts').doc(input.hostId).get().catch(() => null)\n if (host?.exists) {\n hostSubdomain = String(host.get('subdomain') ?? '') || null\n orgId = orgId ?? (String(host.get('orgId') ?? '') || null)\n const roles = (host.get('memberRoles') as Record<string, string> | undefined) ?? {}\n siteManagerUids = Object.entries(roles)\n .filter(([, role]) => role === 'admin' || role === 'editor')\n .map(([uid]) => uid)\n }\n }\n let orgName: string | null = null\n let orgSlug: string | null = null\n if (orgId) {\n const org = await firestore.collection('orgs').doc(orgId).get().catch(() => null)\n if (org?.exists) {\n orgName = String(org.get('name') ?? '') || null\n orgSlug = String(org.get('slug') ?? '') || null\n }\n }\n return { orgId, orgName, orgSlug, hostSubdomain, siteManagerUids }\n}\n\n/** What one notice email sends. */\nexport interface RenderedRiskNoticeEmail {\n key: string\n subject: string\n text: string\n html?: string\n /** The display name to send under; absent sends under the platform's. */\n fromName?: string\n}\n\n/**\n * The brand a kind's email is sent in: the workspace's own for a routine\n * notice about its customers' payments, the platform's for everything the\n * platform does (holds, locks, lifts, cancellations, reviews).\n */\nexport async function riskNoticeEmailBrand(\n kind: RiskEventKind | null,\n orgId: string | null,\n): Promise<SystemEmailBrand> {\n return kind && RISK_NOTICE_WORKSPACE_BRANDED.has(kind)\n ? orgSystemEmailBrand(orgId)\n : systemEmailBrand(null)\n}\n\n/**\n * Renders one notice email through the System emails seam (AGL-3367): the\n * published design for the kind's own `risk-…` key, or its built-in copy,\n * inside the header and footer every platform email wears. The plain-text\n * composition below is the last resort for a render that produced nothing.\n * ONE function, so every notice renders the same way.\n */\nexport async function renderRiskNoticeEmail(\n key: string,\n merge: Record<string, string>,\n brand: SystemEmailBrand = systemEmailBrand(null),\n): Promise<RenderedRiskNoticeEmail> {\n const content = await renderSystemEmailContent(key, merge, brand, riskNoticeFallback(merge)).catch(\n () => riskNoticeFallback(merge),\n )\n return { key, ...content, ...(brand.fromName ? { fromName: brand.fromName } : {}) }\n}\n\n/** The notice as plain text, when no design renders. */\nfunction riskNoticeFallback(merge: Record<string, string>): { subject: string; text: string } {\n const lines = [\n merge['notice.summary'],\n merge['notice.meaning'],\n merge['notice.steps'] ? `What to do next:\\n${merge['notice.steps']}` : '',\n merge['notice.actions'],\n merge['reference'] ? `Reference: ${merge['reference']}` : '',\n merge['help.url'] ? `Why was something held or flagged? ${merge['help.url']}` : '',\n ].filter((line) => Boolean(line && line.trim()))\n return { subject: merge['notice.title'] ?? '', text: lines.join('\\n\\n') }\n}\n\nfunction numbered(steps: readonly string[]): string {\n return steps.map((step, index) => `${index + 1}. ${step}`).join('\\n')\n}\n\nfunction emptyDelivery(skipped: string | null = null): RiskNoticeDelivery {\n return {\n recipients: 0,\n inApp: 0,\n emailed: 0,\n emailFailed: 0,\n emailSkipped: skipped,\n digested: false,\n }\n}\n\ntype BurstVerdict = { owners: 'deliver' | 'fold-first' | 'fold'; staff: 'deliver' | 'fold' }\n\n/**\n * Count this notice against its workspace's hour and say whether it is\n * delivered now. Fails OPEN: a ledger that cannot be written never costs a\n * notice.\n */\nasync function countBurst(\n deps: RiskNoticeDeps,\n ledgerId: string,\n entry: { kind: RiskEventKind; title: string; noticeId: string; staffTitle: string; alertStaff: boolean },\n): Promise<BurstVerdict> {\n const nowMs = deps.nowMs()\n const ref = deps.firestore.collection(RISK_NOTICE_LEDGER_COLLECTION).doc(ledgerId)\n try {\n return await deps.firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n const startedAtMs = Number(snapshot.get('windowStartedAtMs') ?? 0)\n const fresh = !startedAtMs || nowMs - startedAtMs >= RISK_NOTICE_BURST.windowMs\n const ownerCount = (fresh ? 0 : Number(snapshot.get('ownerCount') ?? 0)) + 1\n const staffCount =\n (fresh ? 0 : Number(snapshot.get('staffCount') ?? 0)) + (entry.alertStaff ? 1 : 0)\n const owners: BurstVerdict['owners'] =\n ownerCount <= RISK_NOTICE_BURST.owners\n ? 'deliver'\n : ownerCount === RISK_NOTICE_BURST.owners + 1\n ? 'fold-first'\n : 'fold'\n const staff: BurstVerdict['staff'] =\n staffCount <= RISK_NOTICE_BURST.staff ? 'deliver' : 'fold'\n const pendingOwners = Array.isArray(snapshot.get('pendingOwners'))\n ? (snapshot.get('pendingOwners') as unknown[])\n : []\n const pendingStaff = Array.isArray(snapshot.get('pendingStaff'))\n ? (snapshot.get('pendingStaff') as unknown[])\n : []\n // How many of each notice were folded, however many the list keeps.\n const tally = (field: string, title: string) => {\n const current = (snapshot.get(field) as Record<string, number> | undefined) ?? {}\n return { ...current, [title]: Number(current[title] ?? 0) + 1 }\n }\n transaction.set(\n ref,\n {\n ledgerId,\n ...(fresh ? { windowStartedAtMs: nowMs } : {}),\n ownerCount,\n staffCount,\n ...(owners !== 'deliver'\n ? {\n pendingOwners: [\n ...pendingOwners,\n { kind: entry.kind, title: entry.title, noticeId: entry.noticeId, atMs: nowMs },\n ].slice(-RISK_NOTICE_BURST.digestListMax),\n pendingOwnerTally: tally('pendingOwnerTally', entry.title),\n pendingOwnerCount: FieldValue.increment(1),\n }\n : {}),\n ...(entry.alertStaff && staff !== 'deliver'\n ? {\n pendingStaff: [\n ...pendingStaff,\n { kind: entry.kind, title: entry.staffTitle, noticeId: entry.noticeId, atMs: nowMs },\n ].slice(-RISK_NOTICE_BURST.digestListMax),\n pendingStaffTally: tally('pendingStaffTally', entry.staffTitle),\n pendingStaffCount: FieldValue.increment(1),\n }\n : {}),\n updatedAtMs: nowMs,\n },\n { merge: true },\n )\n return { owners, staff }\n })\n } catch (error) {\n console.error('[risk-notice] burst ledger unavailable — delivering', error)\n return { owners: 'deliver', staff: 'deliver' }\n }\n}\n\n/** Where the owner half goes: uids, and the addresses already in hand. */\nasync function ownerRecipients(\n deps: RiskNoticeDeps,\n input: RiskEventInput,\n siteManagerUids: readonly string[],\n): Promise<{ uids: string[]; emails: Record<string, string | null> }> {\n const emails: Record<string, string | null> = {}\n const uids = new Set<string>()\n const definition = RISK_NOTICE_CATALOG[input.kind]\n if (input.recipients) {\n for (const recipient of input.recipients) {\n if (!recipient.uid) continue\n uids.add(recipient.uid)\n if (recipient.email) emails[recipient.uid] = recipient.email\n }\n return { uids: [...uids], emails }\n }\n if (input.kind === 'account-locked' || input.kind === 'account-unlocked') {\n if (input.userUid) uids.add(input.userUid)\n } else if (input.orgId) {\n const owners = await deps.listOwners(input.orgId).catch(() => [])\n for (const owner of owners) {\n uids.add(owner.uid)\n if (owner.email) emails[owner.uid] = owner.email\n }\n }\n if (definition.includeSiteManagers) for (const uid of siteManagerUids) uids.add(uid)\n return { uids: [...uids], emails }\n}\n\n/**\n * Tell the right people about one risk event. See the module header.\n * Never throws.\n */\nexport async function notifyRiskEvent(\n input: RiskEventInput,\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<RiskNoticeResult> {\n const result: RiskNoticeResult = {\n noticeId: null,\n duplicate: false,\n owners: emptyDelivery(),\n staff: { alerted: false, digested: false },\n error: null,\n }\n if (!isRiskEventKind(input?.kind)) {\n result.error = `Unknown risk event kind: ${String(input?.kind)}`\n return result\n }\n let deps: RiskNoticeDeps\n try {\n deps = resolveDeps(overrides)\n } catch (error) {\n result.error = (error as Error)?.message ?? String(error)\n return result\n }\n const definition = RISK_NOTICE_CATALOG[input.kind]\n const nowMs = deps.nowMs()\n const occurredAtMs = Number.isFinite(input.occurredAtMs) ? Number(input.occurredAtMs) : nowMs\n const noticeId = riskNoticeId(riskNoticeDedupeKey(input, occurredAtMs))\n result.noticeId = noticeId\n\n try {\n const context = await readContext(deps.firestore, input)\n const orgId = context.orgId\n const event: RiskEventInput = { ...input, orgId }\n\n /*\n * The values the OWNER half renders with. `staff.evidence` is absent by\n * construction: the owner record and the owner channels never hold it.\n */\n const supportEmail = abuseReportContactEmail('support') ?? ''\n const ownerValues: RiskNoticeValues = {\n 'workspace.name': context.orgName,\n 'item.label': input.item?.label ?? null,\n occurredAt: formatRiskNoticeTime(occurredAtMs),\n reference: input.reference ?? null,\n 'support.email': supportEmail || null,\n 'lock.message': input.lock?.message ?? null,\n 'lock.affected': input.lock?.affected ?? null,\n amount: input.amount ?? null,\n 'evidence.dueBy': input.evidenceDueByMs ? formatRiskNoticeTime(input.evidenceDueByMs) : null,\n }\n const staffValues: RiskNoticeValues = {\n ...ownerValues,\n 'workspace.name': context.orgName ?? orgId,\n 'staff.evidence': input.staffEvidence ?? null,\n }\n const actionParams: RiskActionParams = {\n itemPath: input.item?.path ?? null,\n noticeId,\n reviewId: input.reviewId ?? null,\n orgId,\n hostId: input.hostId ?? null,\n stripeUrl: input.stripeUrl ?? null,\n lockScope: input.lock?.scope ?? null,\n lockTargetId: input.lock?.targetId ?? null,\n }\n const owner = renderOwnerRiskNotice(input.kind, ownerValues)\n const staff = renderStaffRiskNotice(input.kind, staffValues)\n\n // ONE notice per dedupe key: `create` refuses a second.\n const noticeRef = deps.firestore.collection(RISK_NOTICE_COLLECTION).doc(noticeId)\n try {\n await noticeRef.create({\n kind: input.kind,\n severity: definition.severity,\n orgId,\n hostId: input.hostId ?? null,\n userUid: input.userUid ?? null,\n reviewId: input.reviewId ?? null,\n reference: input.reference ?? null,\n // What the owner surface renders, and nothing staff-only.\n ownerValues: stripEmpty(ownerValues),\n actionParams: stripEmpty(actionParams as Record<string, string | null | undefined>),\n occurredAtMs,\n createdAtMs: nowMs,\n createdAt: FieldValue.serverTimestamp(),\n ...(input.page ? { page: input.page } : {}),\n })\n } catch (error) {\n if (isAlreadyExists(error)) {\n result.duplicate = true\n return result\n }\n throw error\n }\n\n // A row-backed notice stamps the row, so the staff queue renders the\n // catalog's text and actions and the owner surface finds the row.\n if (input.reviewId && !RISK_NOTICE_CLOSING_KINDS.has(input.kind)) {\n await deps.firestore\n .collection(ABUSE_REPORT_COLLECTION)\n .doc(input.reviewId)\n .set(\n {\n riskNotice: {\n kind: input.kind,\n noticeId,\n itemLabel: input.item?.label ?? null,\n itemPath: input.item?.path ?? null,\n stripeUrl: input.stripeUrl ?? null,\n ownersNotifiedAtMs: nowMs,\n },\n },\n { merge: true },\n )\n .catch((error: unknown) =>\n console.error('[risk-notice] could not stamp the abuse row', error),\n )\n }\n\n // Anything folded in an hour that has now closed goes first.\n const ledgerId = ledgerIdFor(event)\n if (ledgerId) await flushLedger(deps, ledgerId).catch(() => undefined)\n\n const verdict: BurstVerdict =\n definition.neverDigest || !ledgerId\n ? { owners: 'deliver', staff: 'deliver' }\n : await countBurst(deps, ledgerId, {\n kind: input.kind,\n title: owner.title,\n staffTitle: staff.title,\n noticeId,\n alertStaff: definition.alertStaff,\n })\n\n // ---- The owners and admins ----\n const recipients = await ownerRecipients(deps, event, context.siteManagerUids)\n result.owners.recipients = recipients.uids.length\n const ownerActions = resolveOwnerRiskActions(input.kind, actionParams)\n const primary = ownerActions.find((action) => action.id === 'request-review') ?? ownerActions[0]\n if (verdict.owners === 'deliver') {\n if (recipients.uids.length) {\n await deps.notifyUsers(\n recipients.uids,\n {\n type: RISK_NOTICE_NOTIFICATION_TYPE,\n title: owner.title,\n body: `${owner.summary} ${owner.meaning}`.slice(0, 1000),\n link: primary?.href ?? riskHoldsPath(noticeId),\n ...(orgId ? { orgId } : {}),\n ...(input.hostId ? { hostId: input.hostId } : {}),\n },\n { emails: recipients.emails },\n )\n result.owners.inApp = recipients.uids.length\n }\n if (!definition.emailOwners) {\n result.owners.emailSkipped = 'This kind is not emailed.'\n } else if (input.emailOwners === false) {\n result.owners.emailSkipped = 'Staff chose not to email the owners for this event.'\n } else if (!recipients.uids.length) {\n result.owners.emailSkipped = 'Nobody to email: the workspace has no owner or admin on record.'\n } else {\n const merge = emailMerge({\n title: owner.title,\n summary: owner.summary,\n meaning: owner.meaning,\n steps: owner.steps,\n actions: ownerActions,\n context: { orgSlug: context.orgSlug, hostId: input.hostId ?? null, hostSubdomain: context.hostSubdomain },\n reference: input.reference ?? null,\n helpAnchor: definition.helpAnchor,\n workspaceName: context.orgName,\n lockMessage: input.lock?.message ?? null,\n })\n const rendered = await renderRiskNoticeEmail(\n riskNoticeEmailKey(input.kind),\n merge,\n await riskNoticeEmailBrand(input.kind, orgId),\n )\n const delivery = await emailPeople(deps, recipients, rendered, supportEmail, {\n lockNotice: LOCK_NOTICE_KINDS.has(input.kind),\n })\n result.owners.emailed = delivery.sent\n result.owners.emailFailed = delivery.failed\n if (!delivery.sent && !delivery.failed) {\n result.owners.emailSkipped = 'No recipient has an email address on record.'\n }\n }\n } else {\n result.owners.digested = true\n if (verdict.owners === 'fold-first' && recipients.uids.length) {\n await deps.notifyUsers(\n recipients.uids,\n {\n type: RISK_NOTICE_NOTIFICATION_TYPE,\n title: 'More account notices are arriving',\n body:\n 'Several items on your workspace were held or flagged in the last hour. ' +\n 'We will send one summary instead of a message for each. Every item is listed on Holds & reviews.',\n link: riskHoldsPath(),\n ...(orgId ? { orgId } : {}),\n },\n { emails: recipients.emails },\n )\n result.owners.inApp = recipients.uids.length\n }\n }\n\n // ---- Staff ----\n if (definition.alertStaff) {\n if (verdict.staff === 'deliver') {\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: staff.title,\n body: staff.summary.slice(0, 1000),\n link: input.reviewId ? staffAbuseRowPath(input.reviewId) : '/admin/abuse-reports',\n })\n result.staff.alerted = true\n } else {\n result.staff.digested = true\n }\n }\n\n await noticeRef\n .set({ delivery: { owners: result.owners, staff: result.staff } }, { merge: true })\n .catch(() => undefined)\n return result\n } catch (error) {\n console.error('[risk-notice] a notice could not be delivered', error)\n result.error = (error as Error)?.message ?? String(error)\n return result\n }\n}\n\nfunction stripEmpty<T extends Record<string, string | null | undefined>>(values: T): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(values)) {\n if (typeof value === 'string' && value) out[key] = value\n }\n return out\n}\n\nfunction isAlreadyExists(error: unknown): boolean {\n const code = (error as { code?: unknown })?.code\n return code === 6 || code === 'already-exists' || code === 'ALREADY_EXISTS'\n}\n\n/** The flat merge map a notice email renders with. */\nfunction emailMerge(input: {\n title: string\n summary: string\n meaning: string\n steps: readonly string[]\n actions: readonly ResolvedRiskAction[]\n context: { orgSlug: string | null; hostId: string | null; hostSubdomain: string | null }\n reference: string | null\n helpAnchor: string\n workspaceName: string | null\n lockMessage: string | null\n}): Record<string, string> {\n const links = input.actions.flatMap((action) => {\n const path = emailPath(action.href, input.context)\n const url = path ? absolute(path) : ''\n return url ? [{ label: action.label, url }] : []\n })\n const primary = links.find((link) => link.label === 'Request a review') ?? links[0]\n const holdsPath = emailPath(riskHoldsPath(), input.context)\n return {\n 'notice.title': input.title,\n 'notice.summary': input.summary,\n 'notice.meaning': input.meaning,\n 'notice.steps': numbered(input.steps),\n 'notice.actions': links.map((link) => `${link.label}: ${link.url}`).join('\\n'),\n 'notice.primaryActionLabel': primary?.label ?? '',\n 'notice.primaryActionUrl': primary?.url ?? '',\n 'workspace.name': input.workspaceName ?? '',\n 'lock.message': input.lockMessage ?? '',\n reference: input.reference ?? '',\n 'holds.url': holdsPath ? absolute(holdsPath) : '',\n 'help.url': riskNoticeHelpUrl(input.helpAnchor),\n }\n}\n\n/**\n * The notices a banned account still receives (AGL-3420): the lock and lift\n * notices, the only mail that tells it what happened. Everything else —\n * digests, reviews, sale warnings — is refused like any other sender's.\n */\nconst LOCK_NOTICE_KINDS: ReadonlySet<string> = new Set<RiskEventKind>([\n 'workspace-locked',\n 'workspace-unlocked',\n 'site-locked',\n 'site-unlocked',\n 'domain-locked',\n 'domain-unlocked',\n 'account-locked',\n 'account-unlocked',\n])\n\n/**\n * One email per person — two owners have not agreed to see each other's\n * addresses. Platform sender, transactional, owed for the account.\n */\nasync function emailPeople(\n deps: RiskNoticeDeps,\n recipients: { uids: string[]; emails: Record<string, string | null> },\n rendered: RenderedRiskNoticeEmail,\n supportEmail: string,\n options: { lockNotice?: boolean } = {},\n): Promise<{ sent: number; failed: number }> {\n let sent = 0\n let failed = 0\n const seen = new Set<string>()\n for (const uid of recipients.uids.slice(0, 50)) {\n const address = String(\n recipients.emails[uid] ?? (await deps.lookupEmail(uid).catch(() => null)) ?? '',\n )\n .trim()\n .toLowerCase()\n if (!address.includes('@') || seen.has(address)) continue\n seen.add(address)\n const outcome = await deps\n .sendEmail({\n to: address,\n subject: rendered.subject,\n text: rendered.text,\n ...(rendered.html ? { html: rendered.html } : {}),\n ...(rendered.fromName ? { fromName: rendered.fromName } : {}),\n ...(supportEmail ? { replyTo: supportEmail } : {}),\n context: 'risk-notice',\n owedFor: 'account',\n ...(options.lockNotice ? { accountBanExempt: true } : {}),\n })\n .catch((error: unknown) => ({ sent: false as const, reason: 'provider-error' as const, detail: String(error) }))\n if (outcome.sent) {\n sent += 1\n // The platform's own account mail: its cost is the platform's, whatever\n // workspace it is about (AGL-1438).\n await meterPlatformEmail().catch(() => undefined)\n } else failed += 1\n }\n return { sent, failed }\n}\n\n/**\n * Send one ledger's digest if its hour has closed and anything was folded.\n * Returns whether a digest went out.\n */\nasync function flushLedger(deps: RiskNoticeDeps, ledgerId: string): Promise<boolean> {\n const nowMs = deps.nowMs()\n const ref = deps.firestore.collection(RISK_NOTICE_LEDGER_COLLECTION).doc(ledgerId)\n const taken = await deps.firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n if (!snapshot.exists) return null\n const startedAtMs = Number(snapshot.get('windowStartedAtMs') ?? 0)\n const ownerCount = Number(snapshot.get('pendingOwnerCount') ?? 0)\n const staffCount = Number(snapshot.get('pendingStaffCount') ?? 0)\n if (!ownerCount && !staffCount) return null\n if (startedAtMs && nowMs - startedAtMs < RISK_NOTICE_BURST.windowMs) return null\n transaction.set(\n ref,\n {\n pendingOwners: [],\n pendingStaff: [],\n pendingOwnerCount: 0,\n pendingStaffCount: 0,\n // Deleted rather than set to `{}`: a merge leaves an existing map's\n // keys in place when handed an empty one.\n pendingOwnerTally: FieldValue.delete(),\n pendingStaffTally: FieldValue.delete(),\n ownerCount: 0,\n staffCount: 0,\n windowStartedAtMs: 0,\n lastDigestAtMs: nowMs,\n },\n { merge: true },\n )\n return {\n owners: (snapshot.get('pendingOwnerTally') as Record<string, number> | undefined) ?? {},\n staff: (snapshot.get('pendingStaffTally') as Record<string, number> | undefined) ?? {},\n ownerCount,\n staffCount,\n }\n })\n if (!taken) return false\n const [scope, id] = [ledgerId.slice(0, ledgerId.indexOf(':')), ledgerId.slice(ledgerId.indexOf(':') + 1)]\n const orgId = scope === 'org' ? id : null\n const summarize = (tally: Record<string, number>, count: number) => {\n const entries = Object.entries(tally).sort((a, b) => b[1] - a[1])\n const lines = entries\n .slice(0, RISK_NOTICE_BURST.digestListMax)\n .map(([title, n]) => `• ${title}${n > 1 ? ` (${n})` : ''}`)\n const listed = entries\n .slice(0, RISK_NOTICE_BURST.digestListMax)\n .reduce((total, [, n]) => total + n, 0)\n if (count > listed) lines.push(`• …and ${count - listed} more`)\n return lines.join('\\n')\n }\n if (taken.ownerCount > 0) {\n const context = orgId\n ? await readContext(deps.firestore, { kind: 'email-held', orgId })\n : { orgName: null, orgSlug: null, siteManagerUids: [], orgId: null, hostSubdomain: null }\n const recipients =\n scope === 'user'\n ? { uids: [id], emails: {} as Record<string, string | null> }\n : orgId\n ? await ownerRecipients(deps, { kind: 'email-held', orgId }, [])\n : { uids: [] as string[], emails: {} as Record<string, string | null> }\n const title = `Summary: more items on ${context.orgName ?? 'your workspace'} were held or flagged`\n const summary = summarize(taken.owners, taken.ownerCount)\n if (recipients.uids.length) {\n await deps.notifyUsers(\n recipients.uids,\n {\n type: RISK_NOTICE_NOTIFICATION_TYPE,\n title,\n body: summary.slice(0, 1000),\n link: riskHoldsPath(),\n ...(orgId ? { orgId } : {}),\n },\n { emails: recipients.emails },\n )\n const holdsPath = emailPath(riskHoldsPath(), {\n orgSlug: context.orgSlug,\n hostId: null,\n hostSubdomain: null,\n })\n const rendered = await renderRiskNoticeEmail(RISK_NOTICE_DIGEST_EMAIL_KEY, {\n 'notice.title': title,\n 'notice.summary': renderRiskNoticeText(\n 'In the last hour, more items on {{workspace.name}} were held or flagged than we send one by one:',\n { 'workspace.name': context.orgName },\n ),\n 'notice.meaning': summary,\n 'notice.steps': numbered([\n 'Open Holds & reviews to see each item, its status and what to do.',\n 'If any of them is a mistake, choose Request a review on it.',\n ]),\n 'notice.actions': holdsPath && absolute(holdsPath) ? `View holds and reviews: ${absolute(holdsPath)}` : '',\n 'holds.url': holdsPath ? absolute(holdsPath) : '',\n 'help.url': riskNoticeHelpUrl(),\n reference: '',\n 'workspace.name': context.orgName ?? '',\n })\n await emailPeople(deps, recipients, rendered, abuseReportContactEmail('support') ?? '')\n }\n }\n if (taken.staffCount > 0) {\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: `Digest: ${taken.staffCount} more risk alerts on ${ledgerId}`,\n body: summarize(taken.staff, taken.staffCount).slice(0, 1000),\n link: '/admin/abuse-reports',\n })\n }\n return true\n}\n\n/**\n * Send every digest whose hour has closed (the hourly cron). Returns how\n * many went out. Never throws.\n */\nexport async function flushRiskNoticeDigests(\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<{ flushed: number; failed: number }> {\n const deps = resolveDeps(overrides)\n const cutoff = deps.nowMs() - RISK_NOTICE_BURST.windowMs\n let flushed = 0\n let failed = 0\n try {\n const snapshot = await deps.firestore\n .collection(RISK_NOTICE_LEDGER_COLLECTION)\n .where('windowStartedAtMs', '<=', cutoff)\n .where('windowStartedAtMs', '>', 0)\n .limit(200)\n .get()\n for (const doc of snapshot.docs) {\n try {\n if (await flushLedger(deps, doc.id)) flushed += 1\n } catch (error) {\n failed += 1\n console.error('[risk-notice] a digest could not be sent', error)\n }\n }\n } catch (error) {\n console.error('[risk-notice] the digest sweep failed', error)\n failed += 1\n }\n return { flushed, failed }\n}\n\n/**\n * The closing notice for a row staff decided: the kind the row's opening\n * notice names in `closesWith`, sent to the same people about the same item.\n * Silent (returns null) for a row with no risk notice or a kind that does\n * not close. Never throws.\n */\nexport async function closeRiskNotice(\n input: { reviewId: string; decision: 'released' | 'rejected' },\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<RiskNoticeResult | null> {\n try {\n const deps = resolveDeps(overrides)\n const row = await deps.firestore.collection(ABUSE_REPORT_COLLECTION).doc(input.reviewId).get()\n const stamp = row.get('riskNotice') as\n | { kind?: string; noticeId?: string; itemLabel?: string | null; itemPath?: string | null }\n | undefined\n if (!stamp?.kind || !isRiskEventKind(stamp.kind)) return null\n const closing = RISK_NOTICE_CATALOG[stamp.kind].closesWith?.[input.decision]\n if (!closing) return null\n return await notifyRiskEvent(\n {\n kind: closing,\n orgId: (row.get('orgId') as string | null) ?? null,\n hostId: (row.get('hostId') as string | null) ?? null,\n reviewId: input.reviewId,\n reference: (row.get('reference') as string | null) ?? null,\n item: stamp.itemLabel ? { label: stamp.itemLabel, path: stamp.itemPath ?? null } : null,\n dedupeKey: `${closing}:${input.reviewId}`,\n },\n overrides,\n )\n } catch (error) {\n console.error('[risk-notice] the closing notice failed', error)\n return null\n }\n}\n\n/** A review request as it is stored on the abuse-queue row. */\nexport interface RiskReviewRequest {\n atMs: number\n uid: string\n email: string | null\n note: string\n noticeId: string\n}\n\nexport type RiskReviewRequestOutcome =\n | { ok: true; reference: string | null; requestedAtMs: number }\n | { ok: false; status: 400 | 404 | 409 | 429; error: string }\n\n/**\n * An owner or admin asks for a review of a held or flagged item.\n *\n * APPENDS to the item's existing abuse-queue row — never a new row, and\n * never a decision: the row's `status` and its `heldSend` are not written\n * here, so no request can release a hold or lift a lock. Staff see the\n * request as a note on the row, and are notified with a link to it.\n */\nexport async function requestRiskReview(\n input: { orgId: string; noticeId: string; uid: string; email: string | null; note: string },\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<RiskReviewRequestOutcome> {\n const deps = resolveDeps(overrides)\n const note = String(input.note ?? '').trim().slice(0, RISK_REVIEW_NOTE_MAX)\n if (note.length < 10) {\n return { ok: false, status: 400, error: 'Tell us a little about it — at least a sentence.' }\n }\n const notice = await deps.firestore.collection(RISK_NOTICE_COLLECTION).doc(input.noticeId).get()\n if (!notice.exists || notice.get('orgId') !== input.orgId) {\n return { ok: false, status: 404, error: 'No such notice on this workspace.' }\n }\n const kind = notice.get('kind')\n const reviewId = String(notice.get('reviewId') ?? '')\n if (!isRiskEventKind(kind) || !RISK_NOTICE_CATALOG[kind].reviewable || !reviewId) {\n return { ok: false, status: 409, error: 'This notice has nothing to review. Contact support instead.' }\n }\n const nowMs = deps.nowMs()\n const rowRef = deps.firestore.collection(ABUSE_REPORT_COLLECTION).doc(reviewId)\n const outcome = await deps.firestore.runTransaction(async (transaction) => {\n const row = await transaction.get(rowRef)\n if (!row.exists || row.get('orgId') !== input.orgId) return 'missing' as const\n const requests = (\n Array.isArray(row.get('ownerReviewRequests')) ? row.get('ownerReviewRequests') : []\n ) as RiskReviewRequest[]\n const last = requests[requests.length - 1]\n if (last && nowMs - Number(last.atMs ?? 0) < RISK_REVIEW_REQUEST_COOLDOWN_MS) {\n return 'cooldown' as const\n }\n const entry: RiskReviewRequest = {\n atMs: nowMs,\n uid: input.uid,\n email: input.email,\n note,\n noticeId: input.noticeId,\n }\n // Only these three fields. Never `status`, never `heldSend`.\n transaction.set(\n rowRef,\n {\n ownerReviewRequests: [...requests, entry].slice(-RISK_REVIEW_REQUESTS_MAX),\n reviewRequestedAtMs: nowMs,\n updatedAt: FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n return { reference: (row.get('reference') as string | null) ?? null }\n })\n if (outcome === 'missing') {\n return { ok: false, status: 404, error: 'The case for this notice is gone. Contact support instead.' }\n }\n if (outcome === 'cooldown') {\n return {\n ok: false,\n status: 429,\n error: 'We already have a request on this. You can add another in a few minutes.',\n }\n }\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: `Review requested${outcome.reference ? ` — ${outcome.reference}` : ''}`,\n body: `A workspace owner or admin asked for a review: \"${note.slice(0, 280)}\"`,\n link: staffAbuseRowPath(reviewId),\n })\n // The person who asked is told it arrived — account mail, platform brand.\n if (input.email) {\n const rendered = await renderRiskNoticeEmail(RISK_REVIEW_REQUESTED_EMAIL_KEY, {\n 'notice.title': 'We received your review request',\n 'notice.summary':\n 'Your request reached our review team' +\n (outcome.reference ? ` under the reference ${outcome.reference}` : '') +\n '. A person reads every request.',\n 'notice.meaning': 'Nothing changes until the review is done; you will get a notice either way.',\n 'notice.steps': numbered([\n 'Nothing more is needed.',\n 'You can follow the item\\'s status on Holds & reviews.',\n ]),\n reference: outcome.reference ?? '',\n 'help.url': riskNoticeHelpUrl('requesting-a-review'),\n })\n await emailPeople(\n deps,\n { uids: [input.uid], emails: { [input.uid]: input.email } },\n rendered,\n abuseReportContactEmail('support') ?? '',\n ).catch(() => undefined)\n }\n return { ok: true, reference: outcome.reference, requestedAtMs: nowMs }\n}\n\n/** One owner notice, as the Holds & reviews page renders it. */\nexport interface OwnerRiskNoticeView {\n noticeId: string\n kind: RiskEventKind\n severity: string\n title: string\n summary: string\n meaning: string\n steps: string[]\n actions: ResolvedRiskAction[]\n reference: string | null\n occurredAtMs: number\n helpUrl: string\n /** The review state, for a notice with a row. */\n status: 'held' | 'in-review' | 'released' | 'rejected' | 'closed' | null\n reviewable: boolean\n reviewRequests: Array<{ atMs: number; note: string; mine: boolean }>\n /** A page notice: the page by name, where it lives, and what visitors see (AGL-3374). */\n page: OwnerPageHoldFacts | null\n}\n\n/** A page notice's facts, as every owner surface renders them (AGL-3374). */\nexport interface OwnerPageHoldFacts {\n /** `the \"Video detail\" template (/videos/:slug)`. */\n label: string\n /** Where it was seen, and the layout or component the content lives in. */\n details: string[]\n /** What visitors receive meanwhile. */\n visitorSentence: string\n /** The page, and the layout or component, the hold is shown on. */\n targets: HeldPageTarget[]\n}\n\n/**\n * A page notice's facts from its stored subject, or — for a notice written\n * before notices carried one — from its label and item path. Pure.\n */\nexport function ownerPageHoldFacts(\n kind: string,\n notice: { page?: unknown; ownerValues?: unknown; actionParams?: unknown },\n): OwnerPageHoldFacts | null {\n if (!isPageHoldNoticeKind(kind)) return null\n const subject = notice.page as HeldPageSubject | undefined\n if (subject && typeof subject === 'object' && subject.screenId) {\n return {\n label: heldPageLabel(subject),\n details: heldPageDetails(subject),\n visitorSentence: heldPageVisitorSentence(subject),\n targets: heldPageTargets(subject),\n }\n }\n const values = (notice.ownerValues ?? {}) as Record<string, string | undefined>\n const params = (notice.actionParams ?? {}) as Record<string, string | undefined>\n const parsed = parseHeldPageItemPath(params.itemPath)\n return {\n label: values['item.label'] || 'a page on your site',\n details: [],\n visitorSentence:\n kind === 'page-flagged'\n ? heldPageVisitorSentence({ visitorView: 'live' })\n : 'Visitors see the previous version of this page, or a not-found page if it was never published, until the review is done.',\n targets: parsed ? [{ type: 'screen', id: parsed.screenId }] : [],\n }\n}\n\n/** One open page hold on a site, as the site's console surfaces show it (AGL-3374). */\nexport interface PageHoldView extends OwnerPageHoldFacts {\n noticeId: string\n kind: PageHoldNoticeKind\n status: OwnerRiskNoticeView['status']\n /** The status chip. Never paired with a release control. */\n chip: { label: string; color: 'warning' | 'error' | 'info' }\n reference: string | null\n occurredAtMs: number\n /** Whether a review can be requested on it (the viewer's role decides the rest). */\n reviewable: boolean\n /** When anyone on the workspace last asked for a review, if they did. */\n reviewRequestedAtMs: number | null\n}\n\n/**\n * Every page on a site that is held, flagged or not approved (AGL-3374): the\n * `riskNotices` of the two page kinds for this host, newest first — one\n * query on (`hostId`, `kind`, `createdAtMs`), which\n * `cloud/firebase-firestore.indexes.json` holds — and each notice's row for\n * its state. A released or cleared page is not listed.\n */\nexport async function listHostPageHolds(\n input: { hostId: string; limit?: number },\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<PageHoldView[]> {\n const deps = resolveDeps(overrides)\n const snapshot = await deps.firestore\n .collection(RISK_NOTICE_COLLECTION)\n .where('hostId', '==', input.hostId)\n .where('kind', 'in', [...PAGE_HOLD_NOTICE_KINDS])\n .orderBy('createdAtMs', 'desc')\n .limit(Math.min(Math.max(input.limit ?? 50, 1), 100))\n .get()\n const reviewIds = [\n ...new Set(snapshot.docs.map((doc) => String(doc.get('reviewId') ?? '')).filter(Boolean)),\n ]\n const rows = new Map<string, FirebaseFirestore.DocumentSnapshot>()\n if (reviewIds.length) {\n const read = await deps.firestore.getAll(\n ...reviewIds.map((id) => deps.firestore.collection(ABUSE_REPORT_COLLECTION).doc(id)),\n )\n for (const row of read) rows.set(row.id, row)\n }\n const views: PageHoldView[] = []\n const seenRows = new Set<string>()\n for (const doc of snapshot.docs) {\n const kind = doc.get('kind')\n if (!isPageHoldNoticeKind(kind)) continue\n const reviewId = String(doc.get('reviewId') ?? '')\n // One entry per case: a row re-notified keeps its newest notice.\n if (reviewId && seenRows.has(reviewId)) continue\n if (reviewId) seenRows.add(reviewId)\n const row = reviewId ? rows.get(reviewId) : undefined\n const status = row?.exists ? ownerRowStatus(row) : kind === 'page-flagged' ? 'in-review' : 'held'\n if (!isOpenPageHoldStatus(status)) continue\n const facts = ownerPageHoldFacts(kind, {\n page: doc.get('page'),\n ownerValues: doc.get('ownerValues'),\n actionParams: doc.get('actionParams'),\n })\n if (!facts) continue\n views.push({\n ...facts,\n noticeId: doc.id,\n kind,\n status,\n chip: pageHoldChip(kind, status),\n reference: (doc.get('reference') as string | null) ?? null,\n occurredAtMs: Number(doc.get('occurredAtMs') ?? doc.get('createdAtMs') ?? 0),\n reviewable: RISK_NOTICE_CATALOG[kind].reviewable && Boolean(row?.exists),\n reviewRequestedAtMs:\n typeof row?.get('reviewRequestedAtMs') === 'number'\n ? (row.get('reviewRequestedAtMs') as number)\n : null,\n })\n }\n return views\n}\n\n/**\n * The workspace's notices, newest first, rendered for its owners: the\n * catalog's owner half only, and the row's decision state — never its\n * evidence, never its staff notes.\n */\nexport async function listOwnerRiskNotices(\n input: { orgId: string; viewerUid: string; limit?: number },\n overrides: Partial<RiskNoticeDeps> = {},\n): Promise<OwnerRiskNoticeView[]> {\n const deps = resolveDeps(overrides)\n const snapshot = await deps.firestore\n .collection(RISK_NOTICE_COLLECTION)\n .where('orgId', '==', input.orgId)\n .orderBy('createdAtMs', 'desc')\n .limit(Math.min(Math.max(input.limit ?? 50, 1), 100))\n .get()\n const reviewIds = [\n ...new Set(\n snapshot.docs.map((doc) => String(doc.get('reviewId') ?? '')).filter(Boolean),\n ),\n ]\n const rows = new Map<string, FirebaseFirestore.DocumentSnapshot>()\n if (reviewIds.length) {\n const read = await deps.firestore.getAll(\n ...reviewIds.map((id) => deps.firestore.collection(ABUSE_REPORT_COLLECTION).doc(id)),\n )\n for (const row of read) rows.set(row.id, row)\n }\n // The reader's own console paths: the org's slug and each site's\n // subdomain, read once for the page rather than per notice.\n const org = await deps.firestore.collection('orgs').doc(input.orgId).get()\n const orgSlug = String(org.get('slug') ?? '') || null\n const hostIds = [\n ...new Set(snapshot.docs.map((doc) => String(doc.get('hostId') ?? '')).filter(Boolean)),\n ]\n const subdomains = new Map<string, string | null>()\n if (hostIds.length) {\n const hosts = await deps.firestore.getAll(\n ...hostIds.map((id) => deps.firestore.collection('hosts').doc(id)),\n )\n for (const host of hosts) subdomains.set(host.id, String(host.get('subdomain') ?? '') || null)\n }\n const views: OwnerRiskNoticeView[] = []\n for (const doc of snapshot.docs) {\n const kind = doc.get('kind')\n if (!isRiskEventKind(kind)) continue\n const values = (doc.get('ownerValues') as RiskNoticeValues | undefined) ?? {}\n const params = (doc.get('actionParams') as RiskActionParams | undefined) ?? {}\n const definition = RISK_NOTICE_CATALOG[kind]\n const copy = renderOwnerRiskNotice(kind, values)\n const reviewId = String(doc.get('reviewId') ?? '')\n const row = reviewId ? rows.get(reviewId) : undefined\n const requests = (\n row && Array.isArray(row.get('ownerReviewRequests')) ? row.get('ownerReviewRequests') : []\n ) as RiskReviewRequest[]\n views.push({\n noticeId: doc.id,\n kind,\n severity: definition.severity,\n ...copy,\n actions: resolveOwnerRiskActions(kind, { ...params, noticeId: doc.id }).flatMap((action) => {\n const hostId = String(doc.get('hostId') ?? '') || null\n const href = emailPath(action.href, {\n orgSlug,\n hostId,\n hostSubdomain: hostId ? (subdomains.get(hostId) ?? null) : null,\n })\n return href ? [{ ...action, href }] : []\n }),\n reference: (doc.get('reference') as string | null) ?? null,\n occurredAtMs: Number(doc.get('occurredAtMs') ?? doc.get('createdAtMs') ?? 0),\n helpUrl: riskNoticeHelpUrl(definition.helpAnchor),\n status: row?.exists ? ownerRowStatus(row) : null,\n reviewable: definition.reviewable && Boolean(row?.exists),\n reviewRequests: requests\n .filter((request) => request.noticeId === doc.id)\n .map((request) => ({\n atMs: Number(request.atMs ?? 0),\n note: String(request.note ?? ''),\n mine: request.uid === input.viewerUid,\n })),\n page: ownerPageHoldFacts(kind, {\n page: doc.get('page'),\n ownerValues: values,\n actionParams: params,\n }),\n })\n }\n return views\n}\n\n/** A row's state in the owner's words. Pure. */\nexport function ownerRowStatus(\n row: Pick<FirebaseFirestore.DocumentSnapshot, 'get'>,\n): OwnerRiskNoticeView['status'] {\n const held = row.get('heldSend') as { state?: string } | undefined\n if (held?.state === 'released') return 'released'\n if (held?.state === 'rejected') return 'rejected'\n const status = String(row.get('status') ?? 'open')\n if (status === 'dismissed') return held ? 'released' : 'closed'\n if (status === 'actioned') return held ? 'rejected' : 'closed'\n if (held?.state === 'held') return 'held'\n return 'in-review'\n}\n"],"names":["createHash","ABUSE_REPORT_COLLECTION","abuseReportContactEmail","DOCS_BASE_URL","isRiskEventKind","renderOwnerRiskNotice","renderRiskNoticeText","renderStaffRiskNotice","resolveOwnerRiskActions","RISK_NOTICE_CATALOG","RISK_NOTICE_CLOSING_KINDS","RISK_NOTICE_DIGEST_EMAIL_KEY","RISK_NOTICE_HELP_PATH","RISK_NOTICE_WORKSPACE_BRANDED","riskNoticeEmailKey","sendEmail","sendEmailImpl","heldPageDetails","heldPageLabel","heldPageTargets","heldPageVisitorSentence","isOpenPageHoldStatus","isPageHoldNoticeKind","PAGE_HOLD_NOTICE_KINDS","pageHoldChip","parseHeldPageItemPath","FieldValue","findUserByUidAcrossPools","firebaseAdmin","notifyStaff","notifyStaffImpl","notifyUsers","notifyUsersImpl","meterPlatformEmail","listOrgMembers","orgSystemEmailBrand","renderSystemEmailContent","systemEmailBrand","RISK_REVIEW_REQUESTED_EMAIL_KEY","RISK_NOTICE_COLLECTION","RISK_NOTICE_LEDGER_COLLECTION","RISK_NOTICE_NOTIFICATION_TYPE","RISK_NOTICE_BURST","owners","staff","windowMs","digestListMax","RISK_REVIEW_NOTE_MAX","RISK_REVIEW_REQUESTS_MAX","RISK_REVIEW_REQUEST_COOLDOWN_MS","defaultListOwners","orgId","members","filter","member","role","map","uid","String","$id","email","defaultLookupEmail","found","catch","record","trim","includes","resolveDeps","overrides","firestore","app","listOwners","lookupEmail","nowMs","Date","now","consoleOrigin","process","env","NEXT_PUBLIC_CONSOLE_URL","replace","riskNoticeHelpUrl","anchor","formatRiskNoticeTime","ms","date","Number","isFinite","getTime","toLocaleString","month","day","year","hour","minute","hour12","timeZone","riskNoticeId","dedupeKey","update","digest","slice","riskNoticeDedupeKey","input","occurredAtMs","reviewId","kind","hostId","userUid","item","path","label","join","ledgerIdFor","riskHoldsPath","noticeId","encodeURIComponent","staffAbuseRowPath","absolute","test","origin","emailPath","context","startsWith","orgSlug","hostSubdomain","length","resolveHostConsolePaths","entries","resolved","Map","hostIds","Set","entry","Boolean","hosts","getAll","id","collection","doc","hostFacts","host","subdomain","get","orgOf","orgIds","orgs","slugs","org","facts","href","set","readContext","siteManagerUids","exists","roles","Object","orgName","riskNoticeEmailBrand","has","renderRiskNoticeEmail","key","merge","brand","content","riskNoticeFallback","fromName","lines","line","subject","text","numbered","steps","step","index","emptyDelivery","skipped","recipients","inApp","emailed","emailFailed","emailSkipped","digested","countBurst","deps","ledgerId","ref","runTransaction","transaction","snapshot","startedAtMs","fresh","ownerCount","staffCount","alertStaff","pendingOwners","Array","isArray","pendingStaff","tally","field","title","current","windowStartedAtMs","atMs","pendingOwnerTally","pendingOwnerCount","increment","staffTitle","pendingStaffTally","pendingStaffCount","updatedAtMs","error","console","ownerRecipients","emails","uids","definition","recipient","add","owner","includeSiteManagers","notifyRiskEvent","result","duplicate","alerted","message","ownerActions","event","supportEmail","ownerValues","occurredAt","reference","lock","affected","amount","evidenceDueByMs","staffValues","staffEvidence","actionParams","itemPath","stripeUrl","lockScope","scope","lockTargetId","targetId","noticeRef","create","severity","stripEmpty","createdAtMs","createdAt","serverTimestamp","page","isAlreadyExists","riskNotice","itemLabel","ownersNotifiedAtMs","flushLedger","undefined","verdict","neverDigest","primary","find","action","type","body","summary","meaning","link","emailOwners","emailMerge","actions","helpAnchor","workspaceName","lockMessage","rendered","delivery","emailPeople","lockNotice","LOCK_NOTICE_KINDS","sent","failed","values","out","value","code","links","flatMap","url","holdsPath","options","seen","address","toLowerCase","outcome","to","html","replyTo","owedFor","accountBanExempt","reason","detail","taken","delete","lastDigestAtMs","indexOf","summarize","count","sort","a","b","n","listed","reduce","total","push","flushRiskNoticeDigests","cutoff","flushed","where","limit","docs","closeRiskNotice","row","stamp","closing","closesWith","decision","requestRiskReview","notice","note","ok","status","reviewable","rowRef","last","requests","ownerReviewRequests","reviewRequestedAtMs","updatedAt","requestedAtMs","ownerPageHoldFacts","screenId","details","visitorSentence","targets","params","parsed","visitorView","listHostPageHolds","orderBy","Math","min","max","reviewIds","rows","read","views","seenRows","ownerRowStatus","chip","listOwnerRiskNotices","subdomains","copy","helpUrl","reviewRequests","request","mine","viewerUid","held","state"],"mappings":"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"}
@@ -15,7 +15,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */ import { pipeline } from "node:stream/promises";
18
- import { isLockdownActive, MEDIA_CDN_POSTER_PARAM, MEDIA_CDN_RENDITION_AUTO, MEDIA_CDN_RENDITION_PARAM, MEDIA_CDN_ROUTE, MEDIA_POSTER_OBJECT_SUFFIX, mediaPosterObjectPath, mediaRenditionObjectPath, normalizeHostLockdown, normalizeOrgLockdown, parseMediaRenditions } from "@aglyn/aglyn/server";
18
+ import { isLockdownActive, MEDIA_CDN_POSTER_PARAM, MEDIA_CDN_RENDITION_AUTO, MEDIA_CDN_RENDITION_PARAM, MEDIA_CDN_ROUTE, MEDIA_POSTER_OBJECT_SUFFIX, isSecurityClassLockdownReason, mediaPosterObjectPath, mediaRenditionObjectPath, normalizeHostLockdown, normalizeOrgLockdown, parseMediaRenditions } from "@aglyn/aglyn/server";
19
19
  // By path, and out of every barrel: see the module note in `media-cdn-scope`.
20
20
  import { MEDIA_CDN_SEGMENT, mediaCdnScopeRefusal, parseMediaCdnScope } from "@aglyn/aglyn/app-utils/media-cdn-scope";
21
21
  import { mediaDeliveryProvider } from "@aglyn/aglyn/plugin-manager/media-delivery-provider";
@@ -296,7 +296,7 @@ const SEGMENT = MEDIA_CDN_SEGMENT;
296
296
  * question is only "does ANY active lockdown demand these bytes stop".
297
297
  */ export function lockdownStopsMediaDelivery(state, nowMs) {
298
298
  if (!state || !isLockdownActive(state, nowMs)) return false;
299
- return state.reason === 'security' || state.reason === 'manual';
299
+ return isSecurityClassLockdownReason(state.reason) || state.reason === 'manual';
300
300
  }
301
301
  /**
302
302
  * **Read cost (AGL-1302):** the verdict inputs are TTL-cached in-process