@aglyn/tenant-data-admin 1.0.0-beta.218 → 1.0.0-beta.220
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 +8 -8
- package/src/lib/server/api-http.js +5 -1
- package/src/lib/server/api-http.js.map +1 -1
- package/src/lib/server/api-v1-description.d.ts +112 -0
- package/src/lib/server/api-v1-description.js +126 -0
- package/src/lib/server/api-v1-description.js.map +1 -0
- package/src/lib/server/api-v1-kit.d.ts +163 -0
- package/src/lib/server/api-v1-kit.js +191 -0
- package/src/lib/server/api-v1-kit.js.map +1 -0
- package/src/lib/server/api-v1-resources.d.ts +143 -0
- package/src/lib/server/api-v1-resources.js +125 -0
- package/src/lib/server/api-v1-resources.js.map +1 -0
- package/src/lib/server/campaign-conversion-attribution.d.ts +1 -1
- package/src/lib/server/campaign-conversion-attribution.js +3 -3
- package/src/lib/server/campaign-conversion-attribution.js.map +1 -1
- package/src/lib/server/card-payment-velocity.d.ts +9 -0
- package/src/lib/server/card-payment-velocity.js +28 -2
- package/src/lib/server/card-payment-velocity.js.map +1 -1
- package/src/lib/server/consent-group-carry.js +1 -1
- package/src/lib/server/consent-group-carry.js.map +1 -1
- package/src/lib/server/consent-group-change.d.ts +2 -1
- package/src/lib/server/consent-group-change.js +43 -18
- package/src/lib/server/consent-group-change.js.map +1 -1
- package/src/lib/server/contact-email-index.d.ts +22 -0
- package/src/lib/server/contact-email-index.js +108 -0
- package/src/lib/server/contact-email-index.js.map +1 -1
- package/src/lib/server/crm-email-activity.d.ts +1 -1
- package/src/lib/server/crm-email-activity.js +1 -1
- package/src/lib/server/crm-email-activity.js.map +1 -1
- package/src/lib/server/crm-records.d.ts +9 -1
- package/src/lib/server/crm-records.js +19 -2
- package/src/lib/server/crm-records.js.map +1 -1
- package/src/lib/server/cron-auth.d.ts +44 -0
- package/src/lib/server/cron-auth.js +76 -0
- package/src/lib/server/cron-auth.js.map +1 -0
- package/src/lib/server/dataset-live-pages.js +6 -6
- package/src/lib/server/dataset-live-pages.js.map +1 -1
- package/src/lib/server/duplicate-resource.d.ts +26 -0
- package/src/lib/server/duplicate-resource.js +47 -25
- package/src/lib/server/duplicate-resource.js.map +1 -1
- package/src/lib/server/dynamic-list-materialize.js +6 -3
- package/src/lib/server/dynamic-list-materialize.js.map +1 -1
- package/src/lib/server/email-flow-gate.js +2 -2
- package/src/lib/server/email-flow-gate.js.map +1 -1
- package/src/lib/server/email-suppression.d.ts +11 -0
- package/src/lib/server/email-suppression.js +37 -2
- package/src/lib/server/email-suppression.js.map +1 -1
- package/src/lib/server/email-unsubscribe-link.d.ts +8 -5
- package/src/lib/server/email-unsubscribe-link.js +7 -7
- package/src/lib/server/email-unsubscribe-link.js.map +1 -1
- package/src/lib/server/erase.d.ts +19 -33
- package/src/lib/server/erase.js +37 -162
- package/src/lib/server/erase.js.map +1 -1
- package/src/lib/server/host-email-tokens.js +1 -1
- package/src/lib/server/host-email-tokens.js.map +1 -1
- package/src/lib/server/host-memberships.js +3 -3
- package/src/lib/server/host-memberships.js.map +1 -1
- package/src/lib/server/host-visitor-records.d.ts +32 -4
- package/src/lib/server/host-visitor-records.js +73 -14
- package/src/lib/server/host-visitor-records.js.map +1 -1
- package/src/lib/server/hosted-page-review.d.ts +27 -0
- package/src/lib/server/hosted-page-review.js +149 -12
- package/src/lib/server/hosted-page-review.js.map +1 -1
- package/src/lib/server/media-cdn-rate-limit.d.ts +6 -3
- package/src/lib/server/media-cdn-rate-limit.js +6 -3
- package/src/lib/server/media-cdn-rate-limit.js.map +1 -1
- package/src/lib/server/member-email-aliases.d.ts +10 -2
- package/src/lib/server/member-email-aliases.js +26 -2
- package/src/lib/server/member-email-aliases.js.map +1 -1
- package/src/lib/server/new-account-announcement.d.ts +62 -0
- package/src/lib/server/new-account-announcement.js +77 -0
- package/src/lib/server/new-account-announcement.js.map +1 -0
- package/src/lib/server/notifications.d.ts +22 -2
- package/src/lib/server/notifications.js +40 -3
- package/src/lib/server/notifications.js.map +1 -1
- package/src/lib/server/operator-alerts.d.ts +7 -1
- package/src/lib/server/operator-alerts.js +5 -3
- package/src/lib/server/operator-alerts.js.map +1 -1
- package/src/lib/server/org-containers.d.ts +66 -0
- package/src/lib/server/org-containers.js +95 -0
- package/src/lib/server/org-containers.js.map +1 -0
- package/src/lib/server/org-member-notice.js +3 -2
- package/src/lib/server/org-member-notice.js.map +1 -1
- package/src/lib/server/payment-fraud-signal.js +5 -1
- package/src/lib/server/payment-fraud-signal.js.map +1 -1
- package/src/lib/server/{connect-payout-failure.d.ts → payment-provider-stripe-connect-payouts.d.ts} +3 -0
- package/src/lib/server/{connect-payout-failure.js → payment-provider-stripe-connect-payouts.js} +36 -2
- package/src/lib/server/payment-provider-stripe-connect-payouts.js.map +1 -0
- package/src/lib/server/{connect-account-status.d.ts → payment-provider-stripe-connect-status.d.ts} +3 -0
- package/src/lib/server/{connect-account-status.js → payment-provider-stripe-connect-status.js} +1 -1
- package/src/lib/server/payment-provider-stripe-connect-status.js.map +1 -0
- package/src/lib/server/{stripe-account-mode.d.ts → payment-provider-stripe-connect.d.ts} +45 -70
- package/src/lib/server/{stripe-account-mode.js → payment-provider-stripe-connect.js} +78 -52
- package/src/lib/server/payment-provider-stripe-connect.js.map +1 -0
- package/src/lib/server/payment-provider.d.ts +173 -0
- package/src/lib/server/payment-provider.js +70 -0
- package/src/lib/server/payment-provider.js.map +1 -0
- package/src/lib/server/payment-risk-record.d.ts +1 -1
- package/src/lib/server/payment-risk-record.js +3 -1
- package/src/lib/server/payment-risk-record.js.map +1 -1
- package/src/lib/server/personal-data-export.js +1 -1
- package/src/lib/server/personal-data-export.js.map +1 -1
- package/src/lib/server/platform-marketing-consent.d.ts +1 -1
- package/src/lib/server/platform-marketing-consent.js +1 -1
- package/src/lib/server/platform-marketing-consent.js.map +1 -1
- package/src/lib/server/rate-limit-store.js +13 -3
- package/src/lib/server/rate-limit-store.js.map +1 -1
- package/src/lib/server/realm-plugins.js +8 -3
- package/src/lib/server/realm-plugins.js.map +1 -1
- package/src/lib/server/retained-refusals.d.ts +142 -0
- package/src/lib/server/retained-refusals.js +325 -0
- package/src/lib/server/retained-refusals.js.map +1 -0
- package/src/lib/server/risk-notice.d.ts +32 -1
- package/src/lib/server/risk-notice.js +90 -28
- package/src/lib/server/risk-notice.js.map +1 -1
- package/src/lib/server/sending-domains.d.ts +17 -0
- package/src/lib/server/sending-domains.js +28 -4
- package/src/lib/server/sending-domains.js.map +1 -1
- package/src/lib/server/sso-enforcement.js +7 -2
- package/src/lib/server/sso-enforcement.js.map +1 -1
- package/src/lib/server/staff-alert-email.js +4 -2
- package/src/lib/server/staff-alert-email.js.map +1 -1
- package/src/lib/server/test-firestore-queries.d.ts +7 -0
- package/src/lib/server/test-firestore-queries.js +70 -27
- package/src/lib/server/test-firestore-queries.js.map +1 -1
- package/src/lib/server/{email-topic-confirmation.js → topic-subscriptions.js} +15 -11
- package/src/lib/server/topic-subscriptions.js.map +1 -0
- package/src/lib/server/upsert-contact.d.ts +46 -1
- package/src/lib/server/upsert-contact.js +87 -20
- package/src/lib/server/upsert-contact.js.map +1 -1
- package/src/tenancy.d.ts +5 -8
- package/src/tenancy.js +12 -13
- package/src/tenancy.js.map +1 -1
- package/src/lib/server/connect-account-status.js.map +0 -1
- package/src/lib/server/connect-payout-failure.js.map +0 -1
- package/src/lib/server/contact-email-engagement.d.ts +0 -32
- package/src/lib/server/contact-email-engagement.js +0 -145
- package/src/lib/server/contact-email-engagement.js.map +0 -1
- package/src/lib/server/contact-lifecycle-floor.d.ts +0 -89
- package/src/lib/server/contact-lifecycle-floor.js +0 -91
- package/src/lib/server/contact-lifecycle-floor.js.map +0 -1
- package/src/lib/server/contact-merge.d.ts +0 -55
- package/src/lib/server/contact-merge.js +0 -288
- package/src/lib/server/contact-merge.js.map +0 -1
- package/src/lib/server/crm-booking-activity.d.ts +0 -86
- package/src/lib/server/crm-booking-activity.js +0 -257
- package/src/lib/server/crm-booking-activity.js.map +0 -1
- package/src/lib/server/crm-inbound-email.d.ts +0 -145
- package/src/lib/server/crm-inbound-email.js +0 -297
- package/src/lib/server/crm-inbound-email.js.map +0 -1
- package/src/lib/server/email-topic-confirmation.js.map +0 -1
- package/src/lib/server/form-dataset-binding-token.d.ts +0 -35
- package/src/lib/server/form-dataset-binding-token.js +0 -107
- package/src/lib/server/form-dataset-binding-token.js.map +0 -1
- package/src/lib/server/stripe-account-mode.js.map +0 -1
- /package/src/lib/server/{email-topic-confirmation.d.ts → topic-subscriptions.d.ts} +0 -0
|
@@ -57,8 +57,8 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
57
57
|
* short "more are arriving" note in-app; when the hour closes,
|
|
58
58
|
* {@link flushRiskNoticeDigests} (hourly cron, and the next notice after the
|
|
59
59
|
* window) sends ONE summary. A workspace under attack gets a digest, not
|
|
60
|
-
* five hundred emails. Locks, lifts
|
|
61
|
-
* go out on their own.
|
|
60
|
+
* five hundred emails. Locks, lifts, cancellations, disputes and card-testing
|
|
61
|
+
* warnings (`neverDigest`) always go out on their own.
|
|
62
62
|
*
|
|
63
63
|
* ## Never throws
|
|
64
64
|
*
|
|
@@ -68,7 +68,8 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
68
68
|
*=========================================*/ import { createHash } from "crypto";
|
|
69
69
|
import { ABUSE_REPORT_COLLECTION, abuseReportContactEmail } from "@aglyn/aglyn/app-utils/abuse-report";
|
|
70
70
|
import { DOCS_BASE_URL } from "@aglyn/aglyn/app-utils/docs-help";
|
|
71
|
-
import {
|
|
71
|
+
import { PLATFORM_BRAND_MERGE_TOKENS } from "@aglyn/aglyn/app-utils/system-email-chrome";
|
|
72
|
+
import { 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 } from "@aglyn/shared-util-email/risk-notice-catalog";
|
|
72
73
|
import { sendEmail as sendEmailImpl } from "@aglyn/shared-util-email";
|
|
73
74
|
import { heldPageDetails, heldPageLabel, heldPageTargets, heldPageVisitorSentence, isOpenPageHoldStatus, isPageHoldNoticeKind, PAGE_HOLD_NOTICE_KINDS, pageHoldChip, parseHeldPageItemPath } from "@aglyn/shared-util-email/held-page";
|
|
74
75
|
import { FieldValue } from "firebase-admin/firestore";
|
|
@@ -82,6 +83,14 @@ import { RISK_REVIEW_REQUESTED_EMAIL_KEY } from "@aglyn/shared-util-email/risk-n
|
|
|
82
83
|
/** Admin-SDK only; no client rule opens it. One document per notice. */ export const RISK_NOTICE_COLLECTION = 'riskNotices';
|
|
83
84
|
/** Admin-SDK only: each workspace's burst window and folded notices. */ export const RISK_NOTICE_LEDGER_COLLECTION = 'riskNoticeLedger';
|
|
84
85
|
/** The in-app notification type every owner notice is written as. */ export const RISK_NOTICE_NOTIFICATION_TYPE = 'system.riskNotice';
|
|
86
|
+
/**
|
|
87
|
+
* The level an owner notice is drawn at (AGL-3437), from its kind's
|
|
88
|
+
* severity: a release or a lift is `info`, not the red of the hold it ends.
|
|
89
|
+
*/ export const RISK_NOTICE_LEVELS = {
|
|
90
|
+
urgent: 'critical',
|
|
91
|
+
warning: 'warning',
|
|
92
|
+
info: 'info'
|
|
93
|
+
};
|
|
85
94
|
/**
|
|
86
95
|
* The burst allowance: how many notices one workspace's owners, and staff
|
|
87
96
|
* about one workspace, receive one by one in {@link RISK_NOTICE_BURST.windowMs}
|
|
@@ -249,12 +258,19 @@ function consoleOrigin() {
|
|
|
249
258
|
var _input_orgId;
|
|
250
259
|
let orgId = (_input_orgId = input.orgId) != null ? _input_orgId : null;
|
|
251
260
|
let hostSubdomain = null;
|
|
261
|
+
let siteLabel = null;
|
|
252
262
|
let siteManagerUids = [];
|
|
253
263
|
if (input.hostId) {
|
|
254
264
|
const host = await firestore.collection('hosts').doc(input.hostId).get().catch(()=>null);
|
|
255
265
|
if (host == null ? void 0 : host.exists) {
|
|
256
|
-
var _host_get, _host_get1, _host_get2;
|
|
266
|
+
var _host_get, _map_find, _host_get1, _host_get2;
|
|
257
267
|
hostSubdomain = String((_host_get = host.get('subdomain')) != null ? _host_get : '') || null;
|
|
268
|
+
const siteName = (_map_find = [
|
|
269
|
+
host.get('displayName'),
|
|
270
|
+
host.get('name'),
|
|
271
|
+
hostSubdomain
|
|
272
|
+
].map((value)=>typeof value === 'string' ? value.trim() : '').find(Boolean)) != null ? _map_find : '';
|
|
273
|
+
siteLabel = siteName ? `the site "${siteName.replace(/"/g, '”')}"` : null;
|
|
258
274
|
orgId = orgId != null ? orgId : String((_host_get1 = host.get('orgId')) != null ? _host_get1 : '') || null;
|
|
259
275
|
const roles = (_host_get2 = host.get('memberRoles')) != null ? _host_get2 : {};
|
|
260
276
|
siteManagerUids = Object.entries(roles).filter(([, role])=>role === 'admin' || role === 'editor').map(([uid])=>uid);
|
|
@@ -275,6 +291,7 @@ function consoleOrigin() {
|
|
|
275
291
|
orgName,
|
|
276
292
|
orgSlug,
|
|
277
293
|
hostSubdomain,
|
|
294
|
+
siteLabel,
|
|
278
295
|
siteManagerUids
|
|
279
296
|
};
|
|
280
297
|
}
|
|
@@ -285,6 +302,15 @@ function consoleOrigin() {
|
|
|
285
302
|
*/ export async function riskNoticeEmailBrand(kind, orgId) {
|
|
286
303
|
return kind && RISK_NOTICE_WORKSPACE_BRANDED.has(kind) ? orgSystemEmailBrand(orgId) : systemEmailBrand(null);
|
|
287
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* The product name a notice's own words use (`your Aglyn account`): the one
|
|
307
|
+
* the email it rides in is branded with, so the sentence and the header
|
|
308
|
+
* never disagree — the platform's for platform mail, a white-label
|
|
309
|
+
* workspace's own for the notices sent in its brand.
|
|
310
|
+
*/ export function riskNoticeProductName(brand) {
|
|
311
|
+
var _brand_merge_brandproductName, _PLATFORM_BRAND_MERGE_TOKENS_brandproductName;
|
|
312
|
+
return String((_brand_merge_brandproductName = brand.merge['brand.productName']) != null ? _brand_merge_brandproductName : '').trim() || String((_PLATFORM_BRAND_MERGE_TOKENS_brandproductName = PLATFORM_BRAND_MERGE_TOKENS['brand.productName']) != null ? _PLATFORM_BRAND_MERGE_TOKENS_brandproductName : '').trim();
|
|
313
|
+
}
|
|
288
314
|
/**
|
|
289
315
|
* Renders one notice email through the System emails seam (AGL-3367): the
|
|
290
316
|
* published design for the kind's own `risk-…` key, or its built-in copy,
|
|
@@ -429,7 +455,11 @@ function emptyDelivery(skipped = null) {
|
|
|
429
455
|
if (owner.email) emails[owner.uid] = owner.email;
|
|
430
456
|
}
|
|
431
457
|
}
|
|
432
|
-
|
|
458
|
+
// A closing kind is shared by openings with different audiences, so it
|
|
459
|
+
// follows the one it closes: a site manager told a review opened is told
|
|
460
|
+
// it closed.
|
|
461
|
+
const audience = isRiskEventKind(input.openedAs) ? RISK_NOTICE_CATALOG[input.openedAs] : definition;
|
|
462
|
+
if (audience.includeSiteManagers) for (const uid of siteManagerUids)uids.add(uid);
|
|
433
463
|
return {
|
|
434
464
|
uids: [
|
|
435
465
|
...uids
|
|
@@ -469,7 +499,7 @@ function emptyDelivery(skipped = null) {
|
|
|
469
499
|
const noticeId = riskNoticeId(riskNoticeDedupeKey(input, occurredAtMs));
|
|
470
500
|
result.noticeId = noticeId;
|
|
471
501
|
try {
|
|
472
|
-
var _abuseReportContactEmail, _ref1, _input_reference, _ref2, _ref3, _input_amount, _context_orgName, _input_staffEvidence, _ref4, _input_reviewId, _input_hostId, _input_stripeUrl, _ref5, _ref6, _ownerActions_find;
|
|
502
|
+
var _abuseReportContactEmail, _ref1, _input_reference, _ref2, _ref3, _input_amount, _input_paymentEvent, _context_orgName, _input_staffEvidence, _ref4, _input_reviewId, _input_hostId, _input_stripeUrl, _ref5, _ref6, _ownerActions_find;
|
|
473
503
|
var _input_item, _input_lock, _input_lock1, _input_item1, _input_lock2, _input_lock3;
|
|
474
504
|
const context = await readContext(deps.firestore, input);
|
|
475
505
|
const orgId = context.orgId;
|
|
@@ -480,8 +510,12 @@ function emptyDelivery(skipped = null) {
|
|
|
480
510
|
* The values the OWNER half renders with. `staff.evidence` is absent by
|
|
481
511
|
* construction: the owner record and the owner channels never hold it.
|
|
482
512
|
*/ const supportEmail = (_abuseReportContactEmail = abuseReportContactEmail('support')) != null ? _abuseReportContactEmail : '';
|
|
513
|
+
// The brand the email is sent in, read once: its product name is also a
|
|
514
|
+
// word in the notice (an account lock says whose account it is).
|
|
515
|
+
const brand = await riskNoticeEmailBrand(input.kind, orgId);
|
|
483
516
|
const ownerValues = {
|
|
484
517
|
'workspace.name': context.orgName,
|
|
518
|
+
'site.label': context.siteLabel,
|
|
485
519
|
'item.label': (_ref1 = (_input_item = input.item) == null ? void 0 : _input_item.label) != null ? _ref1 : null,
|
|
486
520
|
occurredAt: formatRiskNoticeTime(occurredAtMs),
|
|
487
521
|
reference: (_input_reference = input.reference) != null ? _input_reference : null,
|
|
@@ -489,7 +523,11 @@ function emptyDelivery(skipped = null) {
|
|
|
489
523
|
'lock.message': (_ref2 = (_input_lock = input.lock) == null ? void 0 : _input_lock.message) != null ? _ref2 : null,
|
|
490
524
|
'lock.affected': (_ref3 = (_input_lock1 = input.lock) == null ? void 0 : _input_lock1.affected) != null ? _ref3 : null,
|
|
491
525
|
amount: (_input_amount = input.amount) != null ? _input_amount : null,
|
|
492
|
-
'
|
|
526
|
+
'payment.event': (_input_paymentEvent = input.paymentEvent) != null ? _input_paymentEvent : null,
|
|
527
|
+
'evidence.dueBy': input.evidenceDueByMs ? formatRiskNoticeTime(input.evidenceDueByMs) : null,
|
|
528
|
+
'payout.delay': input.payout ? riskPayoutDelayText(input.payout) : null,
|
|
529
|
+
'page.visitors': input.page ? heldPageVisitorSentence(input.page) : null,
|
|
530
|
+
'brand.productName': riskNoticeProductName(brand) || null
|
|
493
531
|
};
|
|
494
532
|
const staffValues = _extends({}, ownerValues, {
|
|
495
533
|
'workspace.name': (_context_orgName = context.orgName) != null ? _context_orgName : orgId,
|
|
@@ -578,6 +616,7 @@ function emptyDelivery(skipped = null) {
|
|
|
578
616
|
type: RISK_NOTICE_NOTIFICATION_TYPE,
|
|
579
617
|
title: owner.title,
|
|
580
618
|
body: `${owner.summary} ${owner.meaning}`.slice(0, 1000),
|
|
619
|
+
level: RISK_NOTICE_LEVELS[definition.severity],
|
|
581
620
|
link: (_ref9 = primary == null ? void 0 : primary.href) != null ? _ref9 : riskHoldsPath(noticeId)
|
|
582
621
|
}, orgId ? {
|
|
583
622
|
orgId
|
|
@@ -613,7 +652,7 @@ function emptyDelivery(skipped = null) {
|
|
|
613
652
|
workspaceName: context.orgName,
|
|
614
653
|
lockMessage: (_ref10 = (_input_lock4 = input.lock) == null ? void 0 : _input_lock4.message) != null ? _ref10 : null
|
|
615
654
|
});
|
|
616
|
-
const rendered = await renderRiskNoticeEmail(riskNoticeEmailKey(input.kind), merge,
|
|
655
|
+
const rendered = await renderRiskNoticeEmail(riskNoticeEmailKey(input.kind), merge, brand);
|
|
617
656
|
const delivery = await emailPeople(deps, recipients, rendered, supportEmail, {
|
|
618
657
|
lockNotice: LOCK_NOTICE_KINDS.has(input.kind)
|
|
619
658
|
});
|
|
@@ -629,6 +668,7 @@ function emptyDelivery(skipped = null) {
|
|
|
629
668
|
await deps.notifyUsers(recipients.uids, _extends({
|
|
630
669
|
type: RISK_NOTICE_NOTIFICATION_TYPE,
|
|
631
670
|
title: 'More account notices are arriving',
|
|
671
|
+
level: 'warning',
|
|
632
672
|
body: 'Several items on your workspace were held or flagged in the last hour. ' + 'We will send one summary instead of a message for each. Every item is listed on Holds & reviews.',
|
|
633
673
|
link: riskHoldsPath()
|
|
634
674
|
}, orgId ? {
|
|
@@ -818,18 +858,17 @@ function isAlreadyExists(error) {
|
|
|
818
858
|
if (count > listed) lines.push(`• …and ${count - listed} more`);
|
|
819
859
|
return lines.join('\n');
|
|
820
860
|
};
|
|
861
|
+
// The workspace by name, for both halves: staff read a digest about
|
|
862
|
+
// `Harbor View`, not about a ledger key.
|
|
863
|
+
const context = orgId ? await readContext(deps.firestore, {
|
|
864
|
+
kind: 'email-held',
|
|
865
|
+
orgId
|
|
866
|
+
}) : {
|
|
867
|
+
orgName: null,
|
|
868
|
+
orgSlug: null
|
|
869
|
+
};
|
|
821
870
|
if (taken.ownerCount > 0) {
|
|
822
871
|
var _context_orgName;
|
|
823
|
-
const context = orgId ? await readContext(deps.firestore, {
|
|
824
|
-
kind: 'email-held',
|
|
825
|
-
orgId
|
|
826
|
-
}) : {
|
|
827
|
-
orgName: null,
|
|
828
|
-
orgSlug: null,
|
|
829
|
-
siteManagerUids: [],
|
|
830
|
-
orgId: null,
|
|
831
|
-
hostSubdomain: null
|
|
832
|
-
};
|
|
833
872
|
const recipients = scope === 'user' ? {
|
|
834
873
|
uids: [
|
|
835
874
|
id
|
|
@@ -842,7 +881,7 @@ function isAlreadyExists(error) {
|
|
|
842
881
|
uids: [],
|
|
843
882
|
emails: {}
|
|
844
883
|
};
|
|
845
|
-
const title = `Summary: more
|
|
884
|
+
const title = `Summary: more notices about ${(_context_orgName = context.orgName) != null ? _context_orgName : 'your workspace'} in the last hour`;
|
|
846
885
|
const summary = summarize(taken.owners, taken.ownerCount);
|
|
847
886
|
if (recipients.uids.length) {
|
|
848
887
|
var _context_orgName1, _abuseReportContactEmail;
|
|
@@ -850,6 +889,7 @@ function isAlreadyExists(error) {
|
|
|
850
889
|
type: RISK_NOTICE_NOTIFICATION_TYPE,
|
|
851
890
|
title,
|
|
852
891
|
body: summary.slice(0, 1000),
|
|
892
|
+
level: 'warning',
|
|
853
893
|
link: riskHoldsPath()
|
|
854
894
|
}, orgId ? {
|
|
855
895
|
orgId
|
|
@@ -863,7 +903,7 @@ function isAlreadyExists(error) {
|
|
|
863
903
|
});
|
|
864
904
|
const rendered = await renderRiskNoticeEmail(RISK_NOTICE_DIGEST_EMAIL_KEY, {
|
|
865
905
|
'notice.title': title,
|
|
866
|
-
'notice.summary': renderRiskNoticeText('In the last hour,
|
|
906
|
+
'notice.summary': renderRiskNoticeText('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:', {
|
|
867
907
|
'workspace.name': context.orgName
|
|
868
908
|
}),
|
|
869
909
|
'notice.meaning': summary,
|
|
@@ -881,10 +921,12 @@ function isAlreadyExists(error) {
|
|
|
881
921
|
}
|
|
882
922
|
}
|
|
883
923
|
if (taken.staffCount > 0) {
|
|
924
|
+
var _context_orgName2;
|
|
925
|
+
const about = context.orgName ? `workspace ${context.orgName} (${orgId})` : scope === 'user' ? `account ${id}` : scope === 'host' ? `site ${id}` : ledgerId;
|
|
884
926
|
await deps.notifyStaff({
|
|
885
927
|
type: 'system.abuseReportUrgent',
|
|
886
|
-
title: `Digest: ${taken.staffCount} more risk alerts on ${ledgerId}`,
|
|
887
|
-
body: summarize(taken.staff, taken.staffCount).slice(0, 1000),
|
|
928
|
+
title: `Digest: ${taken.staffCount} more risk alerts on ${(_context_orgName2 = context.orgName) != null ? _context_orgName2 : ledgerId}`,
|
|
929
|
+
body: (`${taken.staffCount} more risk alerts on ${about} arrived in the last hour ` + `and were batched here instead of sent one by one:\n` + summarize(taken.staff, taken.staffCount)).slice(0, 1000),
|
|
888
930
|
link: '/admin/abuse-reports'
|
|
889
931
|
});
|
|
890
932
|
}
|
|
@@ -942,7 +984,8 @@ function isAlreadyExists(error) {
|
|
|
942
984
|
label: stamp.itemLabel,
|
|
943
985
|
path: (_stamp_itemPath = stamp.itemPath) != null ? _stamp_itemPath : null
|
|
944
986
|
} : null,
|
|
945
|
-
dedupeKey: `${closing}:${input.reviewId}
|
|
987
|
+
dedupeKey: `${closing}:${input.reviewId}`,
|
|
988
|
+
openedAs: stamp.kind
|
|
946
989
|
}, overrides);
|
|
947
990
|
} catch (error) {
|
|
948
991
|
console.error('[risk-notice] the closing notice failed', error);
|
|
@@ -957,7 +1000,8 @@ function isAlreadyExists(error) {
|
|
|
957
1000
|
* here, so no request can release a hold or lift a lock. Staff see the
|
|
958
1001
|
* request as a note on the row, and are notified with a link to it.
|
|
959
1002
|
*/ export async function requestRiskReview(input, overrides = {}) {
|
|
960
|
-
var _input_note, _notice_get;
|
|
1003
|
+
var _input_note, _notice_get, _ref, _ref1, _ref2;
|
|
1004
|
+
var _notice_get1;
|
|
961
1005
|
const deps = resolveDeps(overrides);
|
|
962
1006
|
const note = String((_input_note = input.note) != null ? _input_note : '').trim().slice(0, RISK_REVIEW_NOTE_MAX);
|
|
963
1007
|
if (note.length < 10) {
|
|
@@ -1031,23 +1075,41 @@ function isAlreadyExists(error) {
|
|
|
1031
1075
|
error: 'We already have a request on this. You can add another in a few minutes.'
|
|
1032
1076
|
};
|
|
1033
1077
|
}
|
|
1078
|
+
// What the request is about, by name, for staff and for the person who
|
|
1079
|
+
// asked: the item in the owner's words and the workspace.
|
|
1080
|
+
const itemLabel = String((_ref = (_notice_get1 = notice.get('ownerValues')) == null ? void 0 : _notice_get1['item.label']) != null ? _ref : '').trim() || null;
|
|
1081
|
+
const org = await deps.firestore.collection('orgs').doc(input.orgId).get().catch(()=>null);
|
|
1082
|
+
const orgName = String((_ref1 = org == null ? void 0 : org.get('name')) != null ? _ref1 : '').trim() || null;
|
|
1083
|
+
const orgSlug = String((_ref2 = org == null ? void 0 : org.get('slug')) != null ? _ref2 : '').trim() || null;
|
|
1034
1084
|
await deps.notifyStaff({
|
|
1035
1085
|
type: 'system.abuseReportUrgent',
|
|
1036
1086
|
title: `Review requested${outcome.reference ? ` — ${outcome.reference}` : ''}`,
|
|
1037
|
-
body: `
|
|
1087
|
+
body: `An owner or admin of ${orgName ? `${orgName} (${input.orgId})` : `workspace ${input.orgId}`} ` + `asked for a review of ${itemLabel != null ? itemLabel : 'a held or flagged item'} (${kind}): "${note.slice(0, 280)}"`,
|
|
1038
1088
|
link: staffAbuseRowPath(reviewId)
|
|
1039
1089
|
});
|
|
1040
1090
|
// The person who asked is told it arrived — account mail, platform brand.
|
|
1041
1091
|
if (input.email) {
|
|
1042
1092
|
var _outcome_reference, _abuseReportContactEmail;
|
|
1093
|
+
const holdsPath = emailPath(riskHoldsPath(input.noticeId), {
|
|
1094
|
+
orgSlug,
|
|
1095
|
+
hostId: null,
|
|
1096
|
+
hostSubdomain: null
|
|
1097
|
+
});
|
|
1098
|
+
const holdsUrl = holdsPath ? absolute(holdsPath) : '';
|
|
1099
|
+
const about = `${itemLabel != null ? itemLabel : 'the held or flagged item'}${orgName ? ` on ${orgName}` : ''}`;
|
|
1043
1100
|
const rendered = await renderRiskNoticeEmail(RISK_REVIEW_REQUESTED_EMAIL_KEY, {
|
|
1044
1101
|
'notice.title': 'We received your review request',
|
|
1045
|
-
'notice.summary':
|
|
1046
|
-
'notice.meaning': 'Nothing changes until the review is done
|
|
1102
|
+
'notice.summary': `We received your request to review ${about}` + (outcome.reference ? ` (reference ${outcome.reference})` : '') + '. A person on our review team reads every request.',
|
|
1103
|
+
'notice.meaning': 'Nothing changes until the review is done, and you will get a notice either way.',
|
|
1047
1104
|
'notice.steps': numbered([
|
|
1048
1105
|
'Nothing more is needed.',
|
|
1049
|
-
'You can follow
|
|
1106
|
+
'You can follow its status on Holds & reviews.'
|
|
1050
1107
|
]),
|
|
1108
|
+
'notice.actions': holdsUrl ? `View holds and reviews: ${holdsUrl}` : '',
|
|
1109
|
+
'notice.primaryActionLabel': holdsUrl ? 'View holds and reviews' : '',
|
|
1110
|
+
'notice.primaryActionUrl': holdsUrl,
|
|
1111
|
+
'holds.url': holdsUrl,
|
|
1112
|
+
'workspace.name': orgName != null ? orgName : '',
|
|
1051
1113
|
reference: (_outcome_reference = outcome.reference) != null ? _outcome_reference : '',
|
|
1052
1114
|
'help.url': riskNoticeHelpUrl('requesting-a-review')
|
|
1053
1115
|
});
|