@aglyn/tenant-data-admin 1.0.0-beta.222 → 1.0.0-beta.223
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.
- package/package.json +7 -7
- package/src/lib/server/ga4-measurement-protocol.js +41 -28
- package/src/lib/server/ga4-measurement-protocol.js.map +1 -1
- package/src/lib/server/host-visitor-records.d.ts +13 -0
- package/src/lib/server/host-visitor-records.js +22 -4
- package/src/lib/server/host-visitor-records.js.map +1 -1
- package/src/lib/server/hosted-page-review.js +6 -3
- package/src/lib/server/hosted-page-review.js.map +1 -1
- package/src/lib/server/outbound-send-review.js +6 -3
- package/src/lib/server/outbound-send-review.js.map +1 -1
- package/src/lib/server/payment-fraud-signal.d.ts +49 -9
- package/src/lib/server/payment-fraud-signal.js +192 -48
- package/src/lib/server/payment-fraud-signal.js.map +1 -1
- package/src/lib/server/render-monitor.d.ts +166 -0
- package/src/lib/server/render-monitor.js +412 -0
- package/src/lib/server/render-monitor.js.map +1 -0
- package/src/lib/server/risk-notice.d.ts +7 -0
- package/src/lib/server/risk-notice.js +6 -0
- package/src/lib/server/risk-notice.js.map +1 -1
- package/src/lib/server/web-risk.d.ts +2 -0
- package/src/lib/server/web-risk.js +18 -21
- package/src/lib/server/web-risk.js.map +1 -1
- package/src/tenancy.d.ts +0 -2
- package/src/tenancy.js +0 -2
- package/src/tenancy.js.map +1 -1
- package/src/lib/server/crm-email-activity.d.ts +0 -118
- package/src/lib/server/crm-email-activity.js +0 -148
- package/src/lib/server/crm-email-activity.js.map +0 -1
- package/src/lib/server/crm-next-activity.d.ts +0 -74
- package/src/lib/server/crm-next-activity.js +0 -194
- package/src/lib/server/crm-next-activity.js.map +0 -1
|
@@ -467,6 +467,12 @@ function emptyDelivery(skipped = null) {
|
|
|
467
467
|
emails
|
|
468
468
|
};
|
|
469
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* How long a caller on a page render's path waits on a notice before it
|
|
472
|
+
* carries on without the answer (AGL-3565). The notice is not canceled; it
|
|
473
|
+
* finishes behind the page. A held page or redirect is held either way —
|
|
474
|
+
* what is bounded is how long the visitor waits to be told so.
|
|
475
|
+
*/ export const RISK_NOTICE_RENDER_DEADLINE_MS = 8000;
|
|
470
476
|
/**
|
|
471
477
|
* Tell the right people about one risk event. See the module header.
|
|
472
478
|
* Never throws.
|
|
@@ -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, cancellations, disputes and card-testing\n * warnings (`neverDigest`) always 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 { PLATFORM_BRAND_MERGE_TOKENS } from '@aglyn/aglyn/app-utils/system-email-chrome'\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 riskPayoutDelayText,\n type ResolvedRiskAction,\n type RiskActionParams,\n type RiskEventKind,\n type RiskNoticeSeverity,\n type RiskNoticeValues,\n} from '@aglyn/shared-util-email/risk-notice-catalog'\nimport type { NotificationLevel } from '@aglyn/aglyn/server'\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 level an owner notice is drawn at (AGL-3437), from its kind's\n * severity: a release or a lift is `info`, not the red of the hold it ends.\n */\nexport const RISK_NOTICE_LEVELS: Record<RiskNoticeSeverity, NotificationLevel> = {\n urgent: 'critical',\n warning: 'warning',\n info: 'info',\n}\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 /**\n * What happened to the payment, finishing \"the payment was …\"\n * (`RISK_PAYMENT_EVENTS`): a fraud report, a review, a dispute.\n */\n paymentEvent?: string | null\n evidenceDueByMs?: number | null\n /**\n * A new publisher's payout schedule: how many days each payout waits, and\n * the workspace age it waits until.\n */\n payout?: { delayDays: number; untilWorkspaceAgeDays: 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 * A closing notice's opening kind. The closing goes to the people the\n * opening went to: the site's managers too, when the opening told them.\n */\n openedAs?: RiskEventKind | 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 /** `the site \"Harbor View\"`: a manager of several sites reads which one. */\n siteLabel: string | null\n siteManagerUids: string[]\n}> {\n let orgId = input.orgId ?? null\n let hostSubdomain: string | null = null\n let siteLabel: 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 const siteName =\n [host.get('displayName'), host.get('name'), hostSubdomain]\n .map((value) => (typeof value === 'string' ? value.trim() : ''))\n .find(Boolean) ?? ''\n siteLabel = siteName ? `the site \"${siteName.replace(/\"/g, '”')}\"` : 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, siteLabel, 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 * The product name a notice's own words use (`your Aglyn account`): the one\n * the email it rides in is branded with, so the sentence and the header\n * never disagree — the platform's for platform mail, a white-label\n * workspace's own for the notices sent in its brand.\n */\nexport function riskNoticeProductName(brand: SystemEmailBrand): string {\n return (\n String(brand.merge['brand.productName'] ?? '').trim() ||\n String(PLATFORM_BRAND_MERGE_TOKENS['brand.productName'] ?? '').trim()\n )\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 // A closing kind is shared by openings with different audiences, so it\n // follows the one it closes: a site manager told a review opened is told\n // it closed.\n const audience = isRiskEventKind(input.openedAs) ? RISK_NOTICE_CATALOG[input.openedAs] : definition\n if (audience.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 // The brand the email is sent in, read once: its product name is also a\n // word in the notice (an account lock says whose account it is).\n const brand = await riskNoticeEmailBrand(input.kind, orgId)\n const ownerValues: RiskNoticeValues = {\n 'workspace.name': context.orgName,\n 'site.label': context.siteLabel,\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 'payment.event': input.paymentEvent ?? null,\n 'evidence.dueBy': input.evidenceDueByMs ? formatRiskNoticeTime(input.evidenceDueByMs) : null,\n 'payout.delay': input.payout ? riskPayoutDelayText(input.payout) : null,\n 'page.visitors': input.page ? heldPageVisitorSentence(input.page) : null,\n 'brand.productName': riskNoticeProductName(brand) || 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 level: RISK_NOTICE_LEVELS[definition.severity],\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(riskNoticeEmailKey(input.kind), merge, brand)\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 level: 'warning',\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 // The workspace by name, for both halves: staff read a digest about\n // `Harbor View`, not about a ledger key.\n const context = orgId\n ? await readContext(deps.firestore, { kind: 'email-held', orgId })\n : { orgName: null, orgSlug: null }\n if (taken.ownerCount > 0) {\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 notices about ${context.orgName ?? 'your workspace'} in the last hour`\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 level: 'warning',\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, {{workspace.name}} had more notices than we send one by one. Each one, with its status and anything that is due, is listed on Holds & reviews. By title:',\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 const about = context.orgName\n ? `workspace ${context.orgName} (${orgId})`\n : scope === 'user'\n ? `account ${id}`\n : scope === 'host'\n ? `site ${id}`\n : ledgerId\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: `Digest: ${taken.staffCount} more risk alerts on ${context.orgName ?? ledgerId}`,\n body: (\n `${taken.staffCount} more risk alerts on ${about} arrived in the last hour ` +\n `and were batched here instead of sent one by one:\\n` +\n summarize(taken.staff, taken.staffCount)\n ).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 openedAs: stamp.kind,\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 // What the request is about, by name, for staff and for the person who\n // asked: the item in the owner's words and the workspace.\n const itemLabel =\n String((notice.get('ownerValues') as RiskNoticeValues | undefined)?.['item.label'] ?? '').trim() ||\n null\n const org = await deps.firestore\n .collection('orgs')\n .doc(input.orgId)\n .get()\n .catch(() => null)\n const orgName = String(org?.get('name') ?? '').trim() || null\n const orgSlug = String(org?.get('slug') ?? '').trim() || null\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: `Review requested${outcome.reference ? ` — ${outcome.reference}` : ''}`,\n body:\n `An owner or admin of ${orgName ? `${orgName} (${input.orgId})` : `workspace ${input.orgId}`} ` +\n `asked for a review of ${itemLabel ?? 'a held or flagged item'} (${kind}): \"${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 holdsPath = emailPath(riskHoldsPath(input.noticeId), {\n orgSlug,\n hostId: null,\n hostSubdomain: null,\n })\n const holdsUrl = holdsPath ? absolute(holdsPath) : ''\n const about = `${itemLabel ?? 'the held or flagged item'}${orgName ? ` on ${orgName}` : ''}`\n const rendered = await renderRiskNoticeEmail(RISK_REVIEW_REQUESTED_EMAIL_KEY, {\n 'notice.title': 'We received your review request',\n 'notice.summary':\n `We received your request to review ${about}` +\n (outcome.reference ? ` (reference ${outcome.reference})` : '') +\n '. A person on our review team reads every request.',\n 'notice.meaning':\n 'Nothing changes until the review is done, and you will get a notice either way.',\n 'notice.steps': numbered([\n 'Nothing more is needed.',\n 'You can follow its status on Holds & reviews.',\n ]),\n 'notice.actions': holdsUrl ? `View holds and reviews: ${holdsUrl}` : '',\n 'notice.primaryActionLabel': holdsUrl ? 'View holds and reviews' : '',\n 'notice.primaryActionUrl': holdsUrl,\n 'holds.url': holdsUrl,\n 'workspace.name': orgName ?? '',\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","PLATFORM_BRAND_MERGE_TOKENS","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","riskPayoutDelayText","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_LEVELS","urgent","warning","info","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","siteLabel","siteManagerUids","exists","siteName","value","find","roles","Object","orgName","riskNoticeEmailBrand","has","riskNoticeProductName","brand","merge","renderRiskNoticeEmail","key","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","audience","openedAs","includeSiteManagers","notifyRiskEvent","result","duplicate","alerted","message","ownerActions","event","supportEmail","ownerValues","occurredAt","reference","lock","affected","amount","paymentEvent","evidenceDueByMs","payout","page","staffValues","staffEvidence","actionParams","itemPath","stripeUrl","lockScope","scope","lockTargetId","targetId","noticeRef","create","severity","stripEmpty","createdAtMs","createdAt","serverTimestamp","isAlreadyExists","riskNotice","itemLabel","ownersNotifiedAtMs","flushLedger","undefined","verdict","neverDigest","primary","action","type","body","summary","meaning","level","link","emailOwners","emailMerge","actions","helpAnchor","workspaceName","lockMessage","rendered","delivery","emailPeople","lockNotice","LOCK_NOTICE_KINDS","sent","failed","values","out","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","about","flushRiskNoticeDigests","cutoff","flushed","where","limit","docs","closeRiskNotice","row","stamp","closing","closesWith","decision","requestRiskReview","notice","note","ok","status","reviewable","rowRef","last","requests","ownerReviewRequests","reviewRequestedAtMs","updatedAt","holdsUrl","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, cancellations, disputes and card-testing\n * warnings (`neverDigest`) always 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 { PLATFORM_BRAND_MERGE_TOKENS } from '@aglyn/aglyn/app-utils/system-email-chrome'\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 riskPayoutDelayText,\n type ResolvedRiskAction,\n type RiskActionParams,\n type RiskEventKind,\n type RiskNoticeSeverity,\n type RiskNoticeValues,\n} from '@aglyn/shared-util-email/risk-notice-catalog'\nimport type { NotificationLevel } from '@aglyn/aglyn/server'\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 level an owner notice is drawn at (AGL-3437), from its kind's\n * severity: a release or a lift is `info`, not the red of the hold it ends.\n */\nexport const RISK_NOTICE_LEVELS: Record<RiskNoticeSeverity, NotificationLevel> = {\n urgent: 'critical',\n warning: 'warning',\n info: 'info',\n}\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 /**\n * What happened to the payment, finishing \"the payment was …\"\n * (`RISK_PAYMENT_EVENTS`): a fraud report, a review, a dispute.\n */\n paymentEvent?: string | null\n evidenceDueByMs?: number | null\n /**\n * A new publisher's payout schedule: how many days each payout waits, and\n * the workspace age it waits until.\n */\n payout?: { delayDays: number; untilWorkspaceAgeDays: 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 * A closing notice's opening kind. The closing goes to the people the\n * opening went to: the site's managers too, when the opening told them.\n */\n openedAs?: RiskEventKind | 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 /** `the site \"Harbor View\"`: a manager of several sites reads which one. */\n siteLabel: string | null\n siteManagerUids: string[]\n}> {\n let orgId = input.orgId ?? null\n let hostSubdomain: string | null = null\n let siteLabel: 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 const siteName =\n [host.get('displayName'), host.get('name'), hostSubdomain]\n .map((value) => (typeof value === 'string' ? value.trim() : ''))\n .find(Boolean) ?? ''\n siteLabel = siteName ? `the site \"${siteName.replace(/\"/g, '”')}\"` : 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, siteLabel, 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 * The product name a notice's own words use (`your Aglyn account`): the one\n * the email it rides in is branded with, so the sentence and the header\n * never disagree — the platform's for platform mail, a white-label\n * workspace's own for the notices sent in its brand.\n */\nexport function riskNoticeProductName(brand: SystemEmailBrand): string {\n return (\n String(brand.merge['brand.productName'] ?? '').trim() ||\n String(PLATFORM_BRAND_MERGE_TOKENS['brand.productName'] ?? '').trim()\n )\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 // A closing kind is shared by openings with different audiences, so it\n // follows the one it closes: a site manager told a review opened is told\n // it closed.\n const audience = isRiskEventKind(input.openedAs) ? RISK_NOTICE_CATALOG[input.openedAs] : definition\n if (audience.includeSiteManagers) for (const uid of siteManagerUids) uids.add(uid)\n return { uids: [...uids], emails }\n}\n\n/**\n * How long a caller on a page render's path waits on a notice before it\n * carries on without the answer (AGL-3565). The notice is not canceled; it\n * finishes behind the page. A held page or redirect is held either way —\n * what is bounded is how long the visitor waits to be told so.\n */\nexport const RISK_NOTICE_RENDER_DEADLINE_MS = 8_000\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 // The brand the email is sent in, read once: its product name is also a\n // word in the notice (an account lock says whose account it is).\n const brand = await riskNoticeEmailBrand(input.kind, orgId)\n const ownerValues: RiskNoticeValues = {\n 'workspace.name': context.orgName,\n 'site.label': context.siteLabel,\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 'payment.event': input.paymentEvent ?? null,\n 'evidence.dueBy': input.evidenceDueByMs ? formatRiskNoticeTime(input.evidenceDueByMs) : null,\n 'payout.delay': input.payout ? riskPayoutDelayText(input.payout) : null,\n 'page.visitors': input.page ? heldPageVisitorSentence(input.page) : null,\n 'brand.productName': riskNoticeProductName(brand) || 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 level: RISK_NOTICE_LEVELS[definition.severity],\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(riskNoticeEmailKey(input.kind), merge, brand)\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 level: 'warning',\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 // The workspace by name, for both halves: staff read a digest about\n // `Harbor View`, not about a ledger key.\n const context = orgId\n ? await readContext(deps.firestore, { kind: 'email-held', orgId })\n : { orgName: null, orgSlug: null }\n if (taken.ownerCount > 0) {\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 notices about ${context.orgName ?? 'your workspace'} in the last hour`\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 level: 'warning',\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, {{workspace.name}} had more notices than we send one by one. Each one, with its status and anything that is due, is listed on Holds & reviews. By title:',\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 const about = context.orgName\n ? `workspace ${context.orgName} (${orgId})`\n : scope === 'user'\n ? `account ${id}`\n : scope === 'host'\n ? `site ${id}`\n : ledgerId\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: `Digest: ${taken.staffCount} more risk alerts on ${context.orgName ?? ledgerId}`,\n body: (\n `${taken.staffCount} more risk alerts on ${about} arrived in the last hour ` +\n `and were batched here instead of sent one by one:\\n` +\n summarize(taken.staff, taken.staffCount)\n ).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 openedAs: stamp.kind,\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 // What the request is about, by name, for staff and for the person who\n // asked: the item in the owner's words and the workspace.\n const itemLabel =\n String((notice.get('ownerValues') as RiskNoticeValues | undefined)?.['item.label'] ?? '').trim() ||\n null\n const org = await deps.firestore\n .collection('orgs')\n .doc(input.orgId)\n .get()\n .catch(() => null)\n const orgName = String(org?.get('name') ?? '').trim() || null\n const orgSlug = String(org?.get('slug') ?? '').trim() || null\n await deps.notifyStaff({\n type: 'system.abuseReportUrgent',\n title: `Review requested${outcome.reference ? ` — ${outcome.reference}` : ''}`,\n body:\n `An owner or admin of ${orgName ? `${orgName} (${input.orgId})` : `workspace ${input.orgId}`} ` +\n `asked for a review of ${itemLabel ?? 'a held or flagged item'} (${kind}): \"${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 holdsPath = emailPath(riskHoldsPath(input.noticeId), {\n orgSlug,\n hostId: null,\n hostSubdomain: null,\n })\n const holdsUrl = holdsPath ? absolute(holdsPath) : ''\n const about = `${itemLabel ?? 'the held or flagged item'}${orgName ? ` on ${orgName}` : ''}`\n const rendered = await renderRiskNoticeEmail(RISK_REVIEW_REQUESTED_EMAIL_KEY, {\n 'notice.title': 'We received your review request',\n 'notice.summary':\n `We received your request to review ${about}` +\n (outcome.reference ? ` (reference ${outcome.reference})` : '') +\n '. A person on our review team reads every request.',\n 'notice.meaning':\n 'Nothing changes until the review is done, and you will get a notice either way.',\n 'notice.steps': numbered([\n 'Nothing more is needed.',\n 'You can follow its status on Holds & reviews.',\n ]),\n 'notice.actions': holdsUrl ? `View holds and reviews: ${holdsUrl}` : '',\n 'notice.primaryActionLabel': holdsUrl ? 'View holds and reviews' : '',\n 'notice.primaryActionUrl': holdsUrl,\n 'holds.url': holdsUrl,\n 'workspace.name': orgName ?? '',\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","PLATFORM_BRAND_MERGE_TOKENS","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","riskPayoutDelayText","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_LEVELS","urgent","warning","info","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","siteLabel","siteManagerUids","exists","siteName","value","find","roles","Object","orgName","riskNoticeEmailBrand","has","riskNoticeProductName","brand","merge","renderRiskNoticeEmail","key","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","audience","openedAs","includeSiteManagers","RISK_NOTICE_RENDER_DEADLINE_MS","notifyRiskEvent","result","duplicate","alerted","message","ownerActions","event","supportEmail","ownerValues","occurredAt","reference","lock","affected","amount","paymentEvent","evidenceDueByMs","payout","page","staffValues","staffEvidence","actionParams","itemPath","stripeUrl","lockScope","scope","lockTargetId","targetId","noticeRef","create","severity","stripEmpty","createdAtMs","createdAt","serverTimestamp","isAlreadyExists","riskNotice","itemLabel","ownersNotifiedAtMs","flushLedger","undefined","verdict","neverDigest","primary","action","type","body","summary","meaning","level","link","emailOwners","emailMerge","actions","helpAnchor","workspaceName","lockMessage","rendered","delivery","emailPeople","lockNotice","LOCK_NOTICE_KINDS","sent","failed","values","out","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","about","flushRiskNoticeDigests","cutoff","flushed","where","limit","docs","closeRiskNotice","row","stamp","closing","closesWith","decision","requestRiskReview","notice","note","ok","status","reviewable","rowRef","last","requests","ownerReviewRequests","reviewRequestedAtMs","updatedAt","holdsUrl","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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"}
|
|
@@ -32,6 +32,8 @@ export type WebRiskLookupMode = 'host' | 'url';
|
|
|
32
32
|
export declare const WEB_RISK_TIMEOUT_MS = 1000;
|
|
33
33
|
/** One lookup's budget, however many hosts and addresses it asks about. */
|
|
34
34
|
export declare const WEB_RISK_DEADLINE_MS = 1200;
|
|
35
|
+
/** The service account's token exchange; a cached token answers at once. */
|
|
36
|
+
export declare const WEB_RISK_TOKEN_DEADLINE_MS = 3000;
|
|
35
37
|
/** How long a clean answer is kept in the store. */
|
|
36
38
|
export declare const WEB_RISK_CLEAN_TTL_MS: number;
|
|
37
39
|
/** How long a clean answer is trusted from this process's memory. */
|