@aglyn/plugins-crm 1.0.0-beta.143
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/LICENSE +201 -0
- package/README.md +7 -0
- package/package.json +57 -0
- package/src/index.d.ts +21 -0
- package/src/index.js +29 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/activity-list.d.ts +98 -0
- package/src/lib/components/activity-list.js +321 -0
- package/src/lib/components/activity-list.js.map +1 -0
- package/src/lib/components/activity-queries.d.ts +117 -0
- package/src/lib/components/activity-queries.js +198 -0
- package/src/lib/components/activity-queries.js.map +1 -0
- package/src/lib/components/add-to-list-button.d.ts +25 -0
- package/src/lib/components/add-to-list-button.js +64 -0
- package/src/lib/components/add-to-list-button.js.map +1 -0
- package/src/lib/components/add-to-list-dialog.d.ts +24 -0
- package/src/lib/components/add-to-list-dialog.js +327 -0
- package/src/lib/components/add-to-list-dialog.js.map +1 -0
- package/src/lib/components/assignment-rule-drawer.d.ts +43 -0
- package/src/lib/components/assignment-rule-drawer.js +293 -0
- package/src/lib/components/assignment-rule-drawer.js.map +1 -0
- package/src/lib/components/companies-bulk-bar.d.ts +29 -0
- package/src/lib/components/companies-bulk-bar.js +274 -0
- package/src/lib/components/companies-bulk-bar.js.map +1 -0
- package/src/lib/components/companies-section.d.ts +54 -0
- package/src/lib/components/companies-section.js +522 -0
- package/src/lib/components/companies-section.js.map +1 -0
- package/src/lib/components/company-contacts-card.d.ts +49 -0
- package/src/lib/components/company-contacts-card.js +435 -0
- package/src/lib/components/company-contacts-card.js.map +1 -0
- package/src/lib/components/company-deals-card.d.ts +20 -0
- package/src/lib/components/company-deals-card.js +38 -0
- package/src/lib/components/company-deals-card.js.map +1 -0
- package/src/lib/components/company-detail-page.d.ts +20 -0
- package/src/lib/components/company-detail-page.js +165 -0
- package/src/lib/components/company-detail-page.js.map +1 -0
- package/src/lib/components/company-edit-drawer.d.ts +65 -0
- package/src/lib/components/company-edit-drawer.js +433 -0
- package/src/lib/components/company-edit-drawer.js.map +1 -0
- package/src/lib/components/company-import-drawer.d.ts +42 -0
- package/src/lib/components/company-import-drawer.js +99 -0
- package/src/lib/components/company-import-drawer.js.map +1 -0
- package/src/lib/components/company-picker.d.ts +127 -0
- package/src/lib/components/company-picker.js +323 -0
- package/src/lib/components/company-picker.js.map +1 -0
- package/src/lib/components/company-properties-card.d.ts +47 -0
- package/src/lib/components/company-properties-card.js +289 -0
- package/src/lib/components/company-properties-card.js.map +1 -0
- package/src/lib/components/contact-address-fields.d.ts +29 -0
- package/src/lib/components/contact-address-fields.js +149 -0
- package/src/lib/components/contact-address-fields.js.map +1 -0
- package/src/lib/components/contact-associations-card.d.ts +72 -0
- package/src/lib/components/contact-associations-card.js +296 -0
- package/src/lib/components/contact-associations-card.js.map +1 -0
- package/src/lib/components/contact-custom-columns.d.ts +38 -0
- package/src/lib/components/contact-custom-columns.js +102 -0
- package/src/lib/components/contact-custom-columns.js.map +1 -0
- package/src/lib/components/contact-custom-fields-card.d.ts +39 -0
- package/src/lib/components/contact-custom-fields-card.js +204 -0
- package/src/lib/components/contact-custom-fields-card.js.map +1 -0
- package/src/lib/components/contact-deals-card.d.ts +20 -0
- package/src/lib/components/contact-deals-card.js +38 -0
- package/src/lib/components/contact-deals-card.js.map +1 -0
- package/src/lib/components/contact-detail-page.d.ts +46 -0
- package/src/lib/components/contact-detail-page.js +535 -0
- package/src/lib/components/contact-detail-page.js.map +1 -0
- package/src/lib/components/contact-duplicates-card.d.ts +42 -0
- package/src/lib/components/contact-duplicates-card.js +167 -0
- package/src/lib/components/contact-duplicates-card.js.map +1 -0
- package/src/lib/components/contact-field-drawer.d.ts +64 -0
- package/src/lib/components/contact-field-drawer.js +251 -0
- package/src/lib/components/contact-field-drawer.js.map +1 -0
- package/src/lib/components/contact-import-drawer.d.ts +46 -0
- package/src/lib/components/contact-import-drawer.js +149 -0
- package/src/lib/components/contact-import-drawer.js.map +1 -0
- package/src/lib/components/contact-known-by-card.d.ts +39 -0
- package/src/lib/components/contact-known-by-card.js +142 -0
- package/src/lib/components/contact-known-by-card.js.map +1 -0
- package/src/lib/components/contact-list-columns.d.ts +53 -0
- package/src/lib/components/contact-list-columns.js +288 -0
- package/src/lib/components/contact-list-columns.js.map +1 -0
- package/src/lib/components/contact-merge-dialog.d.ts +79 -0
- package/src/lib/components/contact-merge-dialog.js +405 -0
- package/src/lib/components/contact-merge-dialog.js.map +1 -0
- package/src/lib/components/contact-properties-card.d.ts +89 -0
- package/src/lib/components/contact-properties-card.js +492 -0
- package/src/lib/components/contact-properties-card.js.map +1 -0
- package/src/lib/components/contact-timeline-card.d.ts +68 -0
- package/src/lib/components/contact-timeline-card.js +435 -0
- package/src/lib/components/contact-timeline-card.js.map +1 -0
- package/src/lib/components/contacts-bulk-bar.d.ts +91 -0
- package/src/lib/components/contacts-bulk-bar.js +493 -0
- package/src/lib/components/contacts-bulk-bar.js.map +1 -0
- package/src/lib/components/contacts-section.d.ts +26 -0
- package/src/lib/components/contacts-section.js +1025 -0
- package/src/lib/components/contacts-section.js.map +1 -0
- package/src/lib/components/copy-capture-address-button.d.ts +33 -0
- package/src/lib/components/copy-capture-address-button.js +60 -0
- package/src/lib/components/copy-capture-address-button.js.map +1 -0
- package/src/lib/components/crm-attribution-zone.d.ts +38 -0
- package/src/lib/components/crm-attribution-zone.js +36 -0
- package/src/lib/components/crm-attribution-zone.js.map +1 -0
- package/src/lib/components/crm-booking-zone.d.ts +61 -0
- package/src/lib/components/crm-booking-zone.js +50 -0
- package/src/lib/components/crm-booking-zone.js.map +1 -0
- package/src/lib/components/crm-bulk-bar-frame.d.ts +47 -0
- package/src/lib/components/crm-bulk-bar-frame.js +137 -0
- package/src/lib/components/crm-bulk-bar-frame.js.map +1 -0
- package/src/lib/components/crm-call-actions.d.ts +65 -0
- package/src/lib/components/crm-call-actions.js +110 -0
- package/src/lib/components/crm-call-actions.js.map +1 -0
- package/src/lib/components/crm-column-menu.d.ts +21 -0
- package/src/lib/components/crm-column-menu.js +103 -0
- package/src/lib/components/crm-column-menu.js.map +1 -0
- package/src/lib/components/crm-console-page.d.ts +19 -0
- package/src/lib/components/crm-console-page.js +151 -0
- package/src/lib/components/crm-console-page.js.map +1 -0
- package/src/lib/components/crm-console-sections.d.ts +52 -0
- package/src/lib/components/crm-console-sections.js +83 -0
- package/src/lib/components/crm-console-sections.js.map +1 -0
- package/src/lib/components/crm-custom-field-control.d.ts +26 -0
- package/src/lib/components/crm-custom-field-control.js +131 -0
- package/src/lib/components/crm-custom-field-control.js.map +1 -0
- package/src/lib/components/crm-email-template-tools.d.ts +69 -0
- package/src/lib/components/crm-email-template-tools.js +240 -0
- package/src/lib/components/crm-email-template-tools.js.map +1 -0
- package/src/lib/components/crm-export-all-button.d.ts +39 -0
- package/src/lib/components/crm-export-all-button.js +98 -0
- package/src/lib/components/crm-export-all-button.js.map +1 -0
- package/src/lib/components/crm-filter-bar.d.ts +53 -0
- package/src/lib/components/crm-filter-bar.js +294 -0
- package/src/lib/components/crm-filter-bar.js.map +1 -0
- package/src/lib/components/crm-glance-card.d.ts +54 -0
- package/src/lib/components/crm-glance-card.js +219 -0
- package/src/lib/components/crm-glance-card.js.map +1 -0
- package/src/lib/components/crm-next-activity-column.d.ts +35 -0
- package/src/lib/components/crm-next-activity-column.js +100 -0
- package/src/lib/components/crm-next-activity-column.js.map +1 -0
- package/src/lib/components/crm-record-header.d.ts +97 -0
- package/src/lib/components/crm-record-header.js +160 -0
- package/src/lib/components/crm-record-header.js.map +1 -0
- package/src/lib/components/crm-record-insights-zone.d.ts +50 -0
- package/src/lib/components/crm-record-insights-zone.js +169 -0
- package/src/lib/components/crm-record-insights-zone.js.map +1 -0
- package/src/lib/components/crm-record-picker.d.ts +42 -0
- package/src/lib/components/crm-record-picker.js +104 -0
- package/src/lib/components/crm-record-picker.js.map +1 -0
- package/src/lib/components/crm-send-email-button.d.ts +39 -0
- package/src/lib/components/crm-send-email-button.js +81 -0
- package/src/lib/components/crm-send-email-button.js.map +1 -0
- package/src/lib/components/crm-send-email-dialog.d.ts +61 -0
- package/src/lib/components/crm-send-email-dialog.js +851 -0
- package/src/lib/components/crm-send-email-dialog.js.map +1 -0
- package/src/lib/components/crm-site-picker.d.ts +61 -0
- package/src/lib/components/crm-site-picker.js +99 -0
- package/src/lib/components/crm-site-picker.js.map +1 -0
- package/src/lib/components/crm-suite-lock.d.ts +29 -0
- package/src/lib/components/crm-suite-lock.js +114 -0
- package/src/lib/components/crm-suite-lock.js.map +1 -0
- package/src/lib/components/crm-tasks-due-card.d.ts +42 -0
- package/src/lib/components/crm-tasks-due-card.js +225 -0
- package/src/lib/components/crm-tasks-due-card.js.map +1 -0
- package/src/lib/components/crm-views-control.d.ts +65 -0
- package/src/lib/components/crm-views-control.js +518 -0
- package/src/lib/components/crm-views-control.js.map +1 -0
- package/src/lib/components/csv-import-drawer.d.ts +132 -0
- package/src/lib/components/csv-import-drawer.js +746 -0
- package/src/lib/components/csv-import-drawer.js.map +1 -0
- package/src/lib/components/deal-board.d.ts +70 -0
- package/src/lib/components/deal-board.js +478 -0
- package/src/lib/components/deal-board.js.map +1 -0
- package/src/lib/components/deal-detail-page.d.ts +18 -0
- package/src/lib/components/deal-detail-page.js +323 -0
- package/src/lib/components/deal-detail-page.js.map +1 -0
- package/src/lib/components/deal-edit-drawer.d.ts +59 -0
- package/src/lib/components/deal-edit-drawer.js +640 -0
- package/src/lib/components/deal-edit-drawer.js.map +1 -0
- package/src/lib/components/deal-import-drawer.d.ts +24 -0
- package/src/lib/components/deal-import-drawer.js +83 -0
- package/src/lib/components/deal-import-drawer.js.map +1 -0
- package/src/lib/components/deal-products-card.d.ts +48 -0
- package/src/lib/components/deal-products-card.js +614 -0
- package/src/lib/components/deal-products-card.js.map +1 -0
- package/src/lib/components/deal-properties-card.d.ts +29 -0
- package/src/lib/components/deal-properties-card.js +144 -0
- package/src/lib/components/deal-properties-card.js.map +1 -0
- package/src/lib/components/deal-stage-card.d.ts +24 -0
- package/src/lib/components/deal-stage-card.js +199 -0
- package/src/lib/components/deal-stage-card.js.map +1 -0
- package/src/lib/components/deals-bulk-bar.d.ts +31 -0
- package/src/lib/components/deals-bulk-bar.js +340 -0
- package/src/lib/components/deals-bulk-bar.js.map +1 -0
- package/src/lib/components/deals-section.d.ts +49 -0
- package/src/lib/components/deals-section.js +767 -0
- package/src/lib/components/deals-section.js.map +1 -0
- package/src/lib/components/email-capture-card.d.ts +42 -0
- package/src/lib/components/email-capture-card.js +134 -0
- package/src/lib/components/email-capture-card.js.map +1 -0
- package/src/lib/components/email-template-drawer.d.ts +33 -0
- package/src/lib/components/email-template-drawer.js +209 -0
- package/src/lib/components/email-template-drawer.js.map +1 -0
- package/src/lib/components/email-templates-card.d.ts +33 -0
- package/src/lib/components/email-templates-card.js +370 -0
- package/src/lib/components/email-templates-card.js.map +1 -0
- package/src/lib/components/erase-person-action.d.ts +92 -0
- package/src/lib/components/erase-person-action.js +261 -0
- package/src/lib/components/erase-person-action.js.map +1 -0
- package/src/lib/components/fields-section.d.ts +37 -0
- package/src/lib/components/fields-section.js +641 -0
- package/src/lib/components/fields-section.js.map +1 -0
- package/src/lib/components/form-contact-fields-card.d.ts +46 -0
- package/src/lib/components/form-contact-fields-card.js +253 -0
- package/src/lib/components/form-contact-fields-card.js.map +1 -0
- package/src/lib/components/insert-link-at-caret.d.ts +28 -0
- package/src/lib/components/insert-link-at-caret.js +37 -0
- package/src/lib/components/insert-link-at-caret.js.map +1 -0
- package/src/lib/components/lead-convert-dialog.d.ts +32 -0
- package/src/lib/components/lead-convert-dialog.js +493 -0
- package/src/lib/components/lead-convert-dialog.js.map +1 -0
- package/src/lib/components/lead-detail-page.d.ts +16 -0
- package/src/lib/components/lead-detail-page.js +163 -0
- package/src/lib/components/lead-detail-page.js.map +1 -0
- package/src/lib/components/lead-history-card.d.ts +32 -0
- package/src/lib/components/lead-history-card.js +165 -0
- package/src/lib/components/lead-history-card.js.map +1 -0
- package/src/lib/components/lead-import-drawer.d.ts +24 -0
- package/src/lib/components/lead-import-drawer.js +95 -0
- package/src/lib/components/lead-import-drawer.js.map +1 -0
- package/src/lib/components/lead-owner-select.d.ts +35 -0
- package/src/lib/components/lead-owner-select.js +91 -0
- package/src/lib/components/lead-owner-select.js.map +1 -0
- package/src/lib/components/lead-properties-card.d.ts +61 -0
- package/src/lib/components/lead-properties-card.js +395 -0
- package/src/lib/components/lead-properties-card.js.map +1 -0
- package/src/lib/components/lead-status-chip.d.ts +12 -0
- package/src/lib/components/lead-status-chip.js +43 -0
- package/src/lib/components/lead-status-chip.js.map +1 -0
- package/src/lib/components/lead-surfaces-note.d.ts +126 -0
- package/src/lib/components/lead-surfaces-note.js +262 -0
- package/src/lib/components/lead-surfaces-note.js.map +1 -0
- package/src/lib/components/lead-unqualify-dialog.d.ts +24 -0
- package/src/lib/components/lead-unqualify-dialog.js +125 -0
- package/src/lib/components/lead-unqualify-dialog.js.map +1 -0
- package/src/lib/components/leads-bulk-bar.d.ts +58 -0
- package/src/lib/components/leads-bulk-bar.js +307 -0
- package/src/lib/components/leads-bulk-bar.js.map +1 -0
- package/src/lib/components/leads-section.d.ts +23 -0
- package/src/lib/components/leads-section.js +664 -0
- package/src/lib/components/leads-section.js.map +1 -0
- package/src/lib/components/linked-deals-card.d.ts +32 -0
- package/src/lib/components/linked-deals-card.js +163 -0
- package/src/lib/components/linked-deals-card.js.map +1 -0
- package/src/lib/components/log-activity-dialog.d.ts +60 -0
- package/src/lib/components/log-activity-dialog.js +368 -0
- package/src/lib/components/log-activity-dialog.js.map +1 -0
- package/src/lib/components/lost-reason-dialog.d.ts +26 -0
- package/src/lib/components/lost-reason-dialog.js +95 -0
- package/src/lib/components/lost-reason-dialog.js.map +1 -0
- package/src/lib/components/new-contact-drawer.d.ts +78 -0
- package/src/lib/components/new-contact-drawer.js +343 -0
- package/src/lib/components/new-contact-drawer.js.map +1 -0
- package/src/lib/components/org-lead-surfaces-note.d.ts +57 -0
- package/src/lib/components/org-lead-surfaces-note.js +172 -0
- package/src/lib/components/org-lead-surfaces-note.js.map +1 -0
- package/src/lib/components/owner-avatar.d.ts +18 -0
- package/src/lib/components/owner-avatar.js +61 -0
- package/src/lib/components/owner-avatar.js.map +1 -0
- package/src/lib/components/pipeline-stages-dialog.d.ts +43 -0
- package/src/lib/components/pipeline-stages-dialog.js +339 -0
- package/src/lib/components/pipeline-stages-dialog.js.map +1 -0
- package/src/lib/components/pipelines-dialog.d.ts +55 -0
- package/src/lib/components/pipelines-dialog.js +517 -0
- package/src/lib/components/pipelines-dialog.js.map +1 -0
- package/src/lib/components/recent-activity-feed.d.ts +39 -0
- package/src/lib/components/recent-activity-feed.js +103 -0
- package/src/lib/components/recent-activity-feed.js.map +1 -0
- package/src/lib/components/recipes-card.d.ts +39 -0
- package/src/lib/components/recipes-card.js +485 -0
- package/src/lib/components/recipes-card.js.map +1 -0
- package/src/lib/components/record-activity-card.d.ts +26 -0
- package/src/lib/components/record-activity-card.js +121 -0
- package/src/lib/components/record-activity-card.js.map +1 -0
- package/src/lib/components/record-files-card.d.ts +67 -0
- package/src/lib/components/record-files-card.js +290 -0
- package/src/lib/components/record-files-card.js.map +1 -0
- package/src/lib/components/record-tasks-card.d.ts +32 -0
- package/src/lib/components/record-tasks-card.js +306 -0
- package/src/lib/components/record-tasks-card.js.map +1 -0
- package/src/lib/components/reports/activity-card.d.ts +57 -0
- package/src/lib/components/reports/activity-card.js +281 -0
- package/src/lib/components/reports/activity-card.js.map +1 -0
- package/src/lib/components/reports/closed-deals-card.d.ts +20 -0
- package/src/lib/components/reports/closed-deals-card.js +194 -0
- package/src/lib/components/reports/closed-deals-card.js.map +1 -0
- package/src/lib/components/reports/contacts-mix-card.d.ts +27 -0
- package/src/lib/components/reports/contacts-mix-card.js +161 -0
- package/src/lib/components/reports/contacts-mix-card.js.map +1 -0
- package/src/lib/components/reports/contacts-trend-card.d.ts +31 -0
- package/src/lib/components/reports/contacts-trend-card.js +144 -0
- package/src/lib/components/reports/contacts-trend-card.js.map +1 -0
- package/src/lib/components/reports/forecast-card.d.ts +22 -0
- package/src/lib/components/reports/forecast-card.js +251 -0
- package/src/lib/components/reports/forecast-card.js.map +1 -0
- package/src/lib/components/reports/lead-counts.d.ts +32 -0
- package/src/lib/components/reports/lead-counts.js +44 -0
- package/src/lib/components/reports/lead-counts.js.map +1 -0
- package/src/lib/components/reports/lead-funnel-card.d.ts +38 -0
- package/src/lib/components/reports/lead-funnel-card.js +375 -0
- package/src/lib/components/reports/lead-funnel-card.js.map +1 -0
- package/src/lib/components/reports/pipeline-card.d.ts +19 -0
- package/src/lib/components/reports/pipeline-card.js +338 -0
- package/src/lib/components/reports/pipeline-card.js.map +1 -0
- package/src/lib/components/reports/report-bar-chart.d.ts +39 -0
- package/src/lib/components/reports/report-bar-chart.js +133 -0
- package/src/lib/components/reports/report-bar-chart.js.map +1 -0
- package/src/lib/components/reports/report-breakdown.d.ts +32 -0
- package/src/lib/components/reports/report-breakdown.js +105 -0
- package/src/lib/components/reports/report-breakdown.js.map +1 -0
- package/src/lib/components/reports/report-export.d.ts +37 -0
- package/src/lib/components/reports/report-export.js +66 -0
- package/src/lib/components/reports/report-export.js.map +1 -0
- package/src/lib/components/reports/report-format.d.ts +36 -0
- package/src/lib/components/reports/report-format.js +43 -0
- package/src/lib/components/reports/report-format.js.map +1 -0
- package/src/lib/components/reports/report-scope.d.ts +81 -0
- package/src/lib/components/reports/report-scope.js +58 -0
- package/src/lib/components/reports/report-scope.js.map +1 -0
- package/src/lib/components/reports/report-stat-tile.d.ts +47 -0
- package/src/lib/components/reports/report-stat-tile.js +96 -0
- package/src/lib/components/reports/report-stat-tile.js.map +1 -0
- package/src/lib/components/reports/source-conversion-card.d.ts +28 -0
- package/src/lib/components/reports/source-conversion-card.js +205 -0
- package/src/lib/components/reports/source-conversion-card.js.map +1 -0
- package/src/lib/components/reports/tasks-card.d.ts +26 -0
- package/src/lib/components/reports/tasks-card.js +279 -0
- package/src/lib/components/reports/tasks-card.js.map +1 -0
- package/src/lib/components/reports/use-aggregate-read.d.ts +99 -0
- package/src/lib/components/reports/use-aggregate-read.js +195 -0
- package/src/lib/components/reports/use-aggregate-read.js.map +1 -0
- package/src/lib/components/reports/won-lost-by-owner-card.d.ts +30 -0
- package/src/lib/components/reports/won-lost-by-owner-card.js +168 -0
- package/src/lib/components/reports/won-lost-by-owner-card.js.map +1 -0
- package/src/lib/components/reports-section.d.ts +42 -0
- package/src/lib/components/reports-section.js +308 -0
- package/src/lib/components/reports-section.js.map +1 -0
- package/src/lib/components/sending-addresses-card.d.ts +49 -0
- package/src/lib/components/sending-addresses-card.js +262 -0
- package/src/lib/components/sending-addresses-card.js.map +1 -0
- package/src/lib/components/settings-section.d.ts +151 -0
- package/src/lib/components/settings-section.js +783 -0
- package/src/lib/components/settings-section.js.map +1 -0
- package/src/lib/components/task-cells.d.ts +51 -0
- package/src/lib/components/task-cells.js +139 -0
- package/src/lib/components/task-cells.js.map +1 -0
- package/src/lib/components/task-edit-drawer.d.ts +53 -0
- package/src/lib/components/task-edit-drawer.js +483 -0
- package/src/lib/components/task-edit-drawer.js.map +1 -0
- package/src/lib/components/task-import-drawer.d.ts +24 -0
- package/src/lib/components/task-import-drawer.js +82 -0
- package/src/lib/components/task-import-drawer.js.map +1 -0
- package/src/lib/components/task-snooze-menu.d.ts +65 -0
- package/src/lib/components/task-snooze-menu.js +207 -0
- package/src/lib/components/task-snooze-menu.js.map +1 -0
- package/src/lib/components/tasks-bulk-bar.d.ts +25 -0
- package/src/lib/components/tasks-bulk-bar.js +348 -0
- package/src/lib/components/tasks-bulk-bar.js.map +1 -0
- package/src/lib/components/tasks-calendar.d.ts +38 -0
- package/src/lib/components/tasks-calendar.js +228 -0
- package/src/lib/components/tasks-calendar.js.map +1 -0
- package/src/lib/components/tasks-section.d.ts +27 -0
- package/src/lib/components/tasks-section.js +579 -0
- package/src/lib/components/tasks-section.js.map +1 -0
- package/src/lib/components/use-contact-campaign-emails.d.ts +32 -0
- package/src/lib/components/use-contact-campaign-emails.js +83 -0
- package/src/lib/components/use-contact-campaign-emails.js.map +1 -0
- package/src/lib/components/use-crm-api.d.ts +44 -0
- package/src/lib/components/use-crm-api.js +84 -0
- package/src/lib/components/use-crm-api.js.map +1 -0
- package/src/lib/components/use-crm-inbound-address.d.ts +60 -0
- package/src/lib/components/use-crm-inbound-address.js +124 -0
- package/src/lib/components/use-crm-inbound-address.js.map +1 -0
- package/src/lib/components/use-emails-hub-path.d.ts +24 -0
- package/src/lib/components/use-emails-hub-path.js +59 -0
- package/src/lib/components/use-emails-hub-path.js.map +1 -0
- package/src/lib/components/use-member-email-aliases.d.ts +54 -0
- package/src/lib/components/use-member-email-aliases.js +186 -0
- package/src/lib/components/use-member-email-aliases.js.map +1 -0
- package/src/lib/components/use-org-members.d.ts +40 -0
- package/src/lib/components/use-org-members.js +100 -0
- package/src/lib/components/use-org-members.js.map +1 -0
- package/src/lib/constants/api-routes.d.ts +109 -0
- package/src/lib/constants/api-routes.js +74 -0
- package/src/lib/constants/api-routes.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +14 -0
- package/src/lib/constants/bundle-common.js +14 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/constants/company-filters.d.ts +18 -0
- package/src/lib/constants/company-filters.js +51 -0
- package/src/lib/constants/company-filters.js.map +1 -0
- package/src/lib/constants/contact-filters.d.ts +38 -0
- package/src/lib/constants/contact-filters.js +294 -0
- package/src/lib/constants/contact-filters.js.map +1 -0
- package/src/lib/hooks/use-contact-field-definitions.d.ts +48 -0
- package/src/lib/hooks/use-contact-field-definitions.js +72 -0
- package/src/lib/hooks/use-contact-field-definitions.js.map +1 -0
- package/src/lib/hooks/use-contact-update.d.ts +25 -0
- package/src/lib/hooks/use-contact-update.js +80 -0
- package/src/lib/hooks/use-contact-update.js.map +1 -0
- package/src/lib/hooks/use-crm-activity-logger.d.ts +73 -0
- package/src/lib/hooks/use-crm-activity-logger.js +101 -0
- package/src/lib/hooks/use-crm-activity-logger.js.map +1 -0
- package/src/lib/hooks/use-crm-bulk-apply.d.ts +52 -0
- package/src/lib/hooks/use-crm-bulk-apply.js +112 -0
- package/src/lib/hooks/use-crm-bulk-apply.js.map +1 -0
- package/src/lib/hooks/use-crm-default-view.d.ts +24 -0
- package/src/lib/hooks/use-crm-default-view.js +64 -0
- package/src/lib/hooks/use-crm-default-view.js.map +1 -0
- package/src/lib/hooks/use-crm-email-templates.d.ts +59 -0
- package/src/lib/hooks/use-crm-email-templates.js +103 -0
- package/src/lib/hooks/use-crm-email-templates.js.map +1 -0
- package/src/lib/hooks/use-crm-hub-path.d.ts +29 -0
- package/src/lib/hooks/use-crm-hub-path.js +41 -0
- package/src/lib/hooks/use-crm-hub-path.js.map +1 -0
- package/src/lib/hooks/use-crm-org-mount.d.ts +97 -0
- package/src/lib/hooks/use-crm-org-mount.js +215 -0
- package/src/lib/hooks/use-crm-org-mount.js.map +1 -0
- package/src/lib/hooks/use-crm-record-names.d.ts +52 -0
- package/src/lib/hooks/use-crm-record-names.js +126 -0
- package/src/lib/hooks/use-crm-record-names.js.map +1 -0
- package/src/lib/hooks/use-crm-records-quota.d.ts +68 -0
- package/src/lib/hooks/use-crm-records-quota.js +118 -0
- package/src/lib/hooks/use-crm-records-quota.js.map +1 -0
- package/src/lib/hooks/use-crm-saved-view.d.ts +96 -0
- package/src/lib/hooks/use-crm-saved-view.js +329 -0
- package/src/lib/hooks/use-crm-saved-view.js.map +1 -0
- package/src/lib/hooks/use-crm-scope.d.ts +117 -0
- package/src/lib/hooks/use-crm-scope.js +147 -0
- package/src/lib/hooks/use-crm-scope.js.map +1 -0
- package/src/lib/hooks/use-crm-tasks.d.ts +85 -0
- package/src/lib/hooks/use-crm-tasks.js +153 -0
- package/src/lib/hooks/use-crm-tasks.js.map +1 -0
- package/src/lib/hooks/use-crm-view-grid.d.ts +55 -0
- package/src/lib/hooks/use-crm-view-grid.js +170 -0
- package/src/lib/hooks/use-crm-view-grid.js.map +1 -0
- package/src/lib/hooks/use-crm-views.d.ts +42 -0
- package/src/lib/hooks/use-crm-views.js +84 -0
- package/src/lib/hooks/use-crm-views.js.map +1 -0
- package/src/lib/hooks/use-deal-stage-api.d.ts +69 -0
- package/src/lib/hooks/use-deal-stage-api.js +78 -0
- package/src/lib/hooks/use-deal-stage-api.js.map +1 -0
- package/src/lib/hooks/use-deals.d.ts +47 -0
- package/src/lib/hooks/use-deals.js +105 -0
- package/src/lib/hooks/use-deals.js.map +1 -0
- package/src/lib/hooks/use-org-leads.d.ts +53 -0
- package/src/lib/hooks/use-org-leads.js +114 -0
- package/src/lib/hooks/use-org-leads.js.map +1 -0
- package/src/lib/hooks/use-org-member-directory.d.ts +36 -0
- package/src/lib/hooks/use-org-member-directory.js +116 -0
- package/src/lib/hooks/use-org-member-directory.js.map +1 -0
- package/src/lib/hooks/use-org-member-options.d.ts +38 -0
- package/src/lib/hooks/use-org-member-options.js +131 -0
- package/src/lib/hooks/use-org-member-options.js.map +1 -0
- package/src/lib/hooks/use-pipeline.d.ts +71 -0
- package/src/lib/hooks/use-pipeline.js +159 -0
- package/src/lib/hooks/use-pipeline.js.map +1 -0
- package/src/lib/model/companies-bulk-writes.d.ts +40 -0
- package/src/lib/model/companies-bulk-writes.js +103 -0
- package/src/lib/model/companies-bulk-writes.js.map +1 -0
- package/src/lib/model/companies-csv.d.ts +36 -0
- package/src/lib/model/companies-csv.js +38 -0
- package/src/lib/model/companies-csv.js.map +1 -0
- package/src/lib/model/companies.d.ts +99 -0
- package/src/lib/model/companies.js +168 -0
- package/src/lib/model/companies.js.map +1 -0
- package/src/lib/model/company-delete-route.d.ts +43 -0
- package/src/lib/model/company-delete-route.js +31 -0
- package/src/lib/model/company-delete-route.js.map +1 -0
- package/src/lib/model/company-delete.d.ts +42 -0
- package/src/lib/model/company-delete.js +52 -0
- package/src/lib/model/company-delete.js.map +1 -0
- package/src/lib/model/contact-duplicates.d.ts +58 -0
- package/src/lib/model/contact-duplicates.js +67 -0
- package/src/lib/model/contact-duplicates.js.map +1 -0
- package/src/lib/model/contact-record.d.ts +113 -0
- package/src/lib/model/contact-record.js +70 -0
- package/src/lib/model/contact-record.js.map +1 -0
- package/src/lib/model/contact-update.d.ts +93 -0
- package/src/lib/model/contact-update.js +50 -0
- package/src/lib/model/contact-update.js.map +1 -0
- package/src/lib/model/contacts-bulk-writes.d.ts +173 -0
- package/src/lib/model/contacts-bulk-writes.js +196 -0
- package/src/lib/model/contacts-bulk-writes.js.map +1 -0
- package/src/lib/model/contacts-csv.d.ts +54 -0
- package/src/lib/model/contacts-csv.js +67 -0
- package/src/lib/model/contacts-csv.js.map +1 -0
- package/src/lib/model/contacts-list-seed.d.ts +51 -0
- package/src/lib/model/contacts-list-seed.js +63 -0
- package/src/lib/model/contacts-list-seed.js.map +1 -0
- package/src/lib/model/crm-activity-report.d.ts +27 -0
- package/src/lib/model/crm-activity-report.js +56 -0
- package/src/lib/model/crm-activity-report.js.map +1 -0
- package/src/lib/model/crm-api.d.ts +49 -0
- package/src/lib/model/crm-api.js +47 -0
- package/src/lib/model/crm-api.js.map +1 -0
- package/src/lib/model/crm-bulk-writes.d.ts +147 -0
- package/src/lib/model/crm-bulk-writes.js +207 -0
- package/src/lib/model/crm-bulk-writes.js.map +1 -0
- package/src/lib/model/crm-company-import.d.ts +154 -0
- package/src/lib/model/crm-company-import.js +331 -0
- package/src/lib/model/crm-company-import.js.map +1 -0
- package/src/lib/model/crm-custom-draft.d.ts +62 -0
- package/src/lib/model/crm-custom-draft.js +67 -0
- package/src/lib/model/crm-custom-draft.js.map +1 -0
- package/src/lib/model/crm-deal-import.d.ts +112 -0
- package/src/lib/model/crm-deal-import.js +263 -0
- package/src/lib/model/crm-deal-import.js.map +1 -0
- package/src/lib/model/crm-import.d.ts +244 -0
- package/src/lib/model/crm-import.js +466 -0
- package/src/lib/model/crm-import.js.map +1 -0
- package/src/lib/model/crm-lead-import.d.ts +105 -0
- package/src/lib/model/crm-lead-import.js +255 -0
- package/src/lib/model/crm-lead-import.js.map +1 -0
- package/src/lib/model/crm-record-routes.d.ts +21 -0
- package/src/lib/model/crm-record-routes.js +70 -0
- package/src/lib/model/crm-record-routes.js.map +1 -0
- package/src/lib/model/crm-routes.d.ts +101 -0
- package/src/lib/model/crm-routes.js +92 -0
- package/src/lib/model/crm-routes.js.map +1 -0
- package/src/lib/model/crm-task-import.d.ts +94 -0
- package/src/lib/model/crm-task-import.js +275 -0
- package/src/lib/model/crm-task-import.js.map +1 -0
- package/src/lib/model/crm-view-param.d.ts +43 -0
- package/src/lib/model/crm-view-param.js +49 -0
- package/src/lib/model/crm-view-param.js.map +1 -0
- package/src/lib/model/deal-board-model.d.ts +241 -0
- package/src/lib/model/deal-board-model.js +439 -0
- package/src/lib/model/deal-board-model.js.map +1 -0
- package/src/lib/model/deal-form-model.d.ts +114 -0
- package/src/lib/model/deal-form-model.js +218 -0
- package/src/lib/model/deal-form-model.js.map +1 -0
- package/src/lib/model/deals-csv.d.ts +32 -0
- package/src/lib/model/deals-csv.js +37 -0
- package/src/lib/model/deals-csv.js.map +1 -0
- package/src/lib/model/import-column-shapes.d.ts +36 -0
- package/src/lib/model/import-column-shapes.js +76 -0
- package/src/lib/model/import-column-shapes.js.map +1 -0
- package/src/lib/model/lead-company-suggestion.d.ts +61 -0
- package/src/lib/model/lead-company-suggestion.js +65 -0
- package/src/lib/model/lead-company-suggestion.js.map +1 -0
- package/src/lib/model/lead-filters.d.ts +29 -0
- package/src/lib/model/lead-filters.js +39 -0
- package/src/lib/model/lead-filters.js.map +1 -0
- package/src/lib/model/lead-surfaces.d.ts +58 -0
- package/src/lib/model/lead-surfaces.js +67 -0
- package/src/lib/model/lead-surfaces.js.map +1 -0
- package/src/lib/model/leads-csv.d.ts +40 -0
- package/src/lib/model/leads-csv.js +40 -0
- package/src/lib/model/leads-csv.js.map +1 -0
- package/src/lib/model/next-activity-api.d.ts +31 -0
- package/src/lib/model/next-activity-api.js +57 -0
- package/src/lib/model/next-activity-api.js.map +1 -0
- package/src/lib/model/next-activity.d.ts +64 -0
- package/src/lib/model/next-activity.js +47 -0
- package/src/lib/model/next-activity.js.map +1 -0
- package/src/lib/model/person-key-web.d.ts +43 -0
- package/src/lib/model/person-key-web.js +52 -0
- package/src/lib/model/person-key-web.js.map +1 -0
- package/src/lib/model/record-facts.d.ts +282 -0
- package/src/lib/model/record-facts.js +434 -0
- package/src/lib/model/record-facts.js.map +1 -0
- package/src/lib/model/task-api.d.ts +28 -0
- package/src/lib/model/task-api.js +57 -0
- package/src/lib/model/task-api.js.map +1 -0
- package/src/lib/model/task-calendar.d.ts +108 -0
- package/src/lib/model/task-calendar.js +139 -0
- package/src/lib/model/task-calendar.js.map +1 -0
- package/src/lib/model/task-routes.d.ts +177 -0
- package/src/lib/model/task-routes.js +197 -0
- package/src/lib/model/task-routes.js.map +1 -0
- package/src/lib/model/task-scope.d.ts +45 -0
- package/src/lib/model/task-scope.js +51 -0
- package/src/lib/model/task-scope.js.map +1 -0
- package/src/lib/model/task-views.d.ts +149 -0
- package/src/lib/model/task-views.js +286 -0
- package/src/lib/model/task-views.js.map +1 -0
- package/src/lib/model/tasks-csv.d.ts +28 -0
- package/src/lib/model/tasks-csv.js +28 -0
- package/src/lib/model/tasks-csv.js.map +1 -0
- package/src/lib/plugin.d.ts +32 -0
- package/src/lib/plugin.js +208 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/server/companies-import.d.ts +57 -0
- package/src/lib/server/companies-import.js +242 -0
- package/src/lib/server/companies-import.js.map +1 -0
- package/src/lib/server/company-delete.d.ts +59 -0
- package/src/lib/server/company-delete.js +146 -0
- package/src/lib/server/company-delete.js.map +1 -0
- package/src/lib/server/contact-email-history.d.ts +82 -0
- package/src/lib/server/contact-email-history.js +264 -0
- package/src/lib/server/contact-email-history.js.map +1 -0
- package/src/lib/server/contact-profile.d.ts +41 -0
- package/src/lib/server/contact-profile.js +45 -0
- package/src/lib/server/contact-profile.js.map +1 -0
- package/src/lib/server/contact-update.d.ts +92 -0
- package/src/lib/server/contact-update.js +512 -0
- package/src/lib/server/contact-update.js.map +1 -0
- package/src/lib/server/contacts-import.d.ts +73 -0
- package/src/lib/server/contacts-import.js +263 -0
- package/src/lib/server/contacts-import.js.map +1 -0
- package/src/lib/server/contacts-merge.d.ts +86 -0
- package/src/lib/server/contacts-merge.js +206 -0
- package/src/lib/server/contacts-merge.js.map +1 -0
- package/src/lib/server/deals-import.d.ts +76 -0
- package/src/lib/server/deals-import.js +246 -0
- package/src/lib/server/deals-import.js.map +1 -0
- package/src/lib/server/email-send.d.ts +119 -0
- package/src/lib/server/email-send.js +667 -0
- package/src/lib/server/email-send.js.map +1 -0
- package/src/lib/server/email-template-duplicate.d.ts +19 -0
- package/src/lib/server/email-template-duplicate.js +206 -0
- package/src/lib/server/email-template-duplicate.js.map +1 -0
- package/src/lib/server/erase-person.d.ts +108 -0
- package/src/lib/server/erase-person.js +309 -0
- package/src/lib/server/erase-person.js.map +1 -0
- package/src/lib/server/import-context.d.ts +85 -0
- package/src/lib/server/import-context.js +181 -0
- package/src/lib/server/import-context.js.map +1 -0
- package/src/lib/server/inbound-address.d.ts +61 -0
- package/src/lib/server/inbound-address.js +188 -0
- package/src/lib/server/inbound-address.js.map +1 -0
- package/src/lib/server/lead-convert.d.ts +78 -0
- package/src/lib/server/lead-convert.js +250 -0
- package/src/lib/server/lead-convert.js.map +1 -0
- package/src/lib/server/leads-import.d.ts +84 -0
- package/src/lib/server/leads-import.js +261 -0
- package/src/lib/server/leads-import.js.map +1 -0
- package/src/lib/server/next-activity-routes.d.ts +33 -0
- package/src/lib/server/next-activity-routes.js +110 -0
- package/src/lib/server/next-activity-routes.js.map +1 -0
- package/src/lib/server/org-activity.d.ts +61 -0
- package/src/lib/server/org-activity.js +120 -0
- package/src/lib/server/org-activity.js.map +1 -0
- package/src/lib/server/org-caller.d.ts +110 -0
- package/src/lib/server/org-caller.js +154 -0
- package/src/lib/server/org-caller.js.map +1 -0
- package/src/lib/server/recipe-routes.d.ts +75 -0
- package/src/lib/server/recipe-routes.js +442 -0
- package/src/lib/server/recipe-routes.js.map +1 -0
- package/src/lib/server/record-facts.d.ts +65 -0
- package/src/lib/server/record-facts.js +337 -0
- package/src/lib/server/record-facts.js.map +1 -0
- package/src/lib/server/record-timeline.d.ts +51 -0
- package/src/lib/server/record-timeline.js +215 -0
- package/src/lib/server/record-timeline.js.map +1 -0
- package/src/lib/server/suite-gate.d.ts +42 -0
- package/src/lib/server/suite-gate.js +87 -0
- package/src/lib/server/suite-gate.js.map +1 -0
- package/src/lib/server/task-routes.d.ts +132 -0
- package/src/lib/server/task-routes.js +699 -0
- package/src/lib/server/task-routes.js.map +1 -0
- package/src/lib/server/tasks-import.d.ts +54 -0
- package/src/lib/server/tasks-import.js +157 -0
- package/src/lib/server/tasks-import.js.map +1 -0
- package/src/lib/server-deal-stage.d.ts +88 -0
- package/src/lib/server-deal-stage.js +316 -0
- package/src/lib/server-deal-stage.js.map +1 -0
- package/src/lib/server.d.ts +149 -0
- package/src/lib/server.js +615 -0
- package/src/lib/server.js.map +1 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* `POST /api/crm/org-activity` — one line in the organization's activity
|
|
18
|
+
* feed (AGL-2634).
|
|
19
|
+
*
|
|
20
|
+
* Body: `{ orgId, action, target: { type, id?, name? } }`. Answers
|
|
21
|
+
* `{ ok: true }` once the line is appended.
|
|
22
|
+
*
|
|
23
|
+
* ## Why a route, when the site feed takes the browser's word for it
|
|
24
|
+
*
|
|
25
|
+
* A site's feed at `hosts/{hostId}/activity` is written client-direct: the
|
|
26
|
+
* bulk bars log "Deleted deal" per row through `useHostActivityLogger`,
|
|
27
|
+
* under a rule that lets a member holding a content role append. The
|
|
28
|
+
* organization's feed at `orgs/{orgId}/activity` is not — `allow write: if
|
|
29
|
+
* false` for every client, because the feed names who did what across every
|
|
30
|
+
* site and its only reader is `/api/orgs/activity`, gated on `org.auditLog`
|
|
31
|
+
* with the Admin SDK. So an act performed at the ORGANIZATION level with a
|
|
32
|
+
* client-direct write — a bulk bar over the org's deals, which writes the
|
|
33
|
+
* documents from the browser — has no feed it can reach, and the org hub
|
|
34
|
+
* wrote no line at all for its bulk actions. This is the door: the same
|
|
35
|
+
* trust the site feed extends to the browser (a verified member, holding
|
|
36
|
+
* the permission the CRM is gated on), narrowed to the org hub's reach
|
|
37
|
+
* (org-wide, never a site collaborator) and to the CRM's own record kinds.
|
|
38
|
+
*
|
|
39
|
+
* What it does NOT vouch for is that the act happened: like every
|
|
40
|
+
* client-written feed line, the sentence is the caller's. The routes that
|
|
41
|
+
* perform an org-level act themselves — the stage move, the merge, the
|
|
42
|
+
* erasure, the email — write their own line with the Admin SDK and never
|
|
43
|
+
* call here.
|
|
44
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
45
|
+
import { logOrgActivity } from "@aglyn/tenant-data-admin";
|
|
46
|
+
import { CRM_API_ROUTES, CRM_ORG_ACTIVITY_ACTION_MAX, CRM_ORG_ACTIVITY_KINDS } from "../constants/api-routes.js";
|
|
47
|
+
import { authorizeOrgCaller } from "./org-caller.js";
|
|
48
|
+
import { crmSuiteRefusal } from "./suite-gate.js";
|
|
49
|
+
export const CRM_ORG_ACTIVITY_ROUTE = CRM_API_ROUTES.orgActivity;
|
|
50
|
+
function typed(value, max) {
|
|
51
|
+
return String(value != null ? value : '').trim().slice(0, max);
|
|
52
|
+
}
|
|
53
|
+
const isKind = (value)=>typeof value === 'string' && CRM_ORG_ACTIVITY_KINDS.includes(value);
|
|
54
|
+
export const crmOrgActivityHandler = async (req, res)=>{
|
|
55
|
+
var _req_body;
|
|
56
|
+
if (req.method !== 'POST') {
|
|
57
|
+
res.setHeader('Allow', 'POST');
|
|
58
|
+
res.status(405).json({
|
|
59
|
+
error: 'Method not allowed'
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const body = typeof req.body === 'string' ? JSON.parse(req.body) : (_req_body = req.body) != null ? _req_body : {};
|
|
64
|
+
const orgId = typed(body.orgId, 128);
|
|
65
|
+
const action = typed(body.action, CRM_ORG_ACTIVITY_ACTION_MAX);
|
|
66
|
+
const target = body.target && typeof body.target === 'object' ? body.target : null;
|
|
67
|
+
if (!orgId || !action || !target) {
|
|
68
|
+
res.status(400).json({
|
|
69
|
+
error: 'Missing orgId, action or target'
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!isKind(target.type)) {
|
|
74
|
+
res.status(400).json({
|
|
75
|
+
error: 'The target names no CRM record kind'
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const id = typed(target.id, 200);
|
|
80
|
+
const name = typed(target.name, 200);
|
|
81
|
+
try {
|
|
82
|
+
const caller = await authorizeOrgCaller(req, orgId, {
|
|
83
|
+
needs: 'data.manage',
|
|
84
|
+
refusal: 'Logging organization activity requires the data permission across the whole workspace'
|
|
85
|
+
});
|
|
86
|
+
if (caller.ok === false) {
|
|
87
|
+
res.status(caller.status).json({
|
|
88
|
+
error: caller.error
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// The plan after the caller, before the line is written (AGL-2851).
|
|
93
|
+
const suite = crmSuiteRefusal(caller.org, "Logging a CRM act to the organization's feed");
|
|
94
|
+
if (suite) {
|
|
95
|
+
res.status(suite.status).json(suite.body);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
await logOrgActivity(orgId, {
|
|
99
|
+
uid: caller.uid,
|
|
100
|
+
email: caller.email
|
|
101
|
+
}, action, _extends({
|
|
102
|
+
type: target.type
|
|
103
|
+
}, id ? {
|
|
104
|
+
id
|
|
105
|
+
} : {}, name ? {
|
|
106
|
+
name
|
|
107
|
+
} : {}));
|
|
108
|
+
res.status(200).json({
|
|
109
|
+
ok: true
|
|
110
|
+
});
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('[crm] org-activity failed', orgId, error);
|
|
113
|
+
res.status(500).json({
|
|
114
|
+
error: 'The activity could not be logged'
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
export default crmOrgActivityHandler;
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=org-activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/org-activity.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 * `POST /api/crm/org-activity` — one line in the organization's activity\n * feed (AGL-2634).\n *\n * Body: `{ orgId, action, target: { type, id?, name? } }`. Answers\n * `{ ok: true }` once the line is appended.\n *\n * ## Why a route, when the site feed takes the browser's word for it\n *\n * A site's feed at `hosts/{hostId}/activity` is written client-direct: the\n * bulk bars log \"Deleted deal\" per row through `useHostActivityLogger`,\n * under a rule that lets a member holding a content role append. The\n * organization's feed at `orgs/{orgId}/activity` is not — `allow write: if\n * false` for every client, because the feed names who did what across every\n * site and its only reader is `/api/orgs/activity`, gated on `org.auditLog`\n * with the Admin SDK. So an act performed at the ORGANIZATION level with a\n * client-direct write — a bulk bar over the org's deals, which writes the\n * documents from the browser — has no feed it can reach, and the org hub\n * wrote no line at all for its bulk actions. This is the door: the same\n * trust the site feed extends to the browser (a verified member, holding\n * the permission the CRM is gated on), narrowed to the org hub's reach\n * (org-wide, never a site collaborator) and to the CRM's own record kinds.\n *\n * What it does NOT vouch for is that the act happened: like every\n * client-written feed line, the sentence is the caller's. The routes that\n * perform an org-level act themselves — the stage move, the merge, the\n * erasure, the email — write their own line with the Admin SDK and never\n * call here.\n */\n\nimport type { PluginApiHandler } from '@aglyn/aglyn/server'\nimport { logOrgActivity } from '@aglyn/tenant-data-admin'\nimport {\n CRM_API_ROUTES,\n CRM_ORG_ACTIVITY_ACTION_MAX,\n CRM_ORG_ACTIVITY_KINDS,\n type CrmOrgActivityKind,\n} from '../constants/api-routes'\nimport { authorizeOrgCaller } from './org-caller'\nimport { crmSuiteRefusal } from './suite-gate'\n\nexport const CRM_ORG_ACTIVITY_ROUTE = CRM_API_ROUTES.orgActivity\n\n/** The body the org-level bulk bars post. */\nexport interface OrgActivityRequest {\n orgId: string\n /** The sentence — \"Owner set on 3 deals\" — as the bar's snackbar said it. */\n action: string\n target: {\n type: CrmOrgActivityKind\n id?: string\n name?: string\n }\n}\n\nfunction typed(value: unknown, max: number): string {\n return String(value ?? '')\n .trim()\n .slice(0, max)\n}\n\nconst isKind = (value: unknown): value is CrmOrgActivityKind =>\n typeof value === 'string' &&\n (CRM_ORG_ACTIVITY_KINDS as readonly string[]).includes(value)\n\nexport const crmOrgActivityHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST')\n res.status(405).json({ error: 'Method not allowed' })\n return\n }\n const body: Partial<OrgActivityRequest> =\n typeof req.body === 'string' ? JSON.parse(req.body) : (req.body ?? {})\n const orgId = typed(body.orgId, 128)\n const action = typed(body.action, CRM_ORG_ACTIVITY_ACTION_MAX)\n const target = body.target && typeof body.target === 'object' ? body.target : null\n if (!orgId || !action || !target) {\n res.status(400).json({ error: 'Missing orgId, action or target' })\n return\n }\n if (!isKind(target.type)) {\n res.status(400).json({ error: 'The target names no CRM record kind' })\n return\n }\n const id = typed(target.id, 200)\n const name = typed(target.name, 200)\n\n try {\n const caller = await authorizeOrgCaller(req, orgId, {\n needs: 'data.manage',\n refusal:\n 'Logging organization activity requires the data permission across the whole workspace',\n })\n if (caller.ok === false) {\n res.status(caller.status).json({ error: caller.error })\n return\n }\n // The plan after the caller, before the line is written (AGL-2851).\n const suite = crmSuiteRefusal(caller.org, \"Logging a CRM act to the organization's feed\")\n if (suite) {\n res.status(suite.status).json(suite.body)\n return\n }\n await logOrgActivity(orgId, { uid: caller.uid, email: caller.email }, action, {\n type: target.type,\n ...(id ? { id } : {}),\n ...(name ? { name } : {}),\n })\n res.status(200).json({ ok: true })\n } catch (error) {\n console.error('[crm] org-activity failed', orgId, error)\n res.status(500).json({ error: 'The activity could not be logged' })\n }\n}\n\nexport default crmOrgActivityHandler\n"],"names":["logOrgActivity","CRM_API_ROUTES","CRM_ORG_ACTIVITY_ACTION_MAX","CRM_ORG_ACTIVITY_KINDS","authorizeOrgCaller","crmSuiteRefusal","CRM_ORG_ACTIVITY_ROUTE","orgActivity","typed","value","max","String","trim","slice","isKind","includes","crmOrgActivityHandler","req","res","method","setHeader","status","json","error","body","JSON","parse","orgId","action","target","type","id","name","caller","needs","refusal","ok","suite","org","uid","email","console"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC;AAGD,SAASA,cAAc,QAAQ,2BAA0B;AACzD,SACEC,cAAc,EACdC,2BAA2B,EAC3BC,sBAAsB,QAEjB,6BAAyB;AAChC,SAASC,kBAAkB,QAAQ,kBAAc;AACjD,SAASC,eAAe,QAAQ,kBAAc;AAE9C,OAAO,MAAMC,yBAAyBL,eAAeM,WAAW,CAAA;AAchE,SAASC,MAAMC,KAAc,EAAEC,GAAW;IACxC,OAAOC,OAAOF,gBAAAA,QAAS,IACpBG,IAAI,GACJC,KAAK,CAAC,GAAGH;AACd;AAEA,MAAMI,SAAS,CAACL,QACd,OAAOA,UAAU,YACjB,AAACN,uBAA6CY,QAAQ,CAACN;AAEzD,OAAO,MAAMO,wBAA0C,OAAOC,KAAKC;QAORD;IANzD,IAAIA,IAAIE,MAAM,KAAK,QAAQ;QACzBD,IAAIE,SAAS,CAAC,SAAS;QACvBF,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAqB;QACnD;IACF;IACA,MAAMC,OACJ,OAAOP,IAAIO,IAAI,KAAK,WAAWC,KAAKC,KAAK,CAACT,IAAIO,IAAI,KAAKP,YAAAA,IAAIO,IAAI,YAARP,YAAY,CAAC;IACtE,MAAMU,QAAQnB,MAAMgB,KAAKG,KAAK,EAAE;IAChC,MAAMC,SAASpB,MAAMgB,KAAKI,MAAM,EAAE1B;IAClC,MAAM2B,SAASL,KAAKK,MAAM,IAAI,OAAOL,KAAKK,MAAM,KAAK,WAAWL,KAAKK,MAAM,GAAG;IAC9E,IAAI,CAACF,SAAS,CAACC,UAAU,CAACC,QAAQ;QAChCX,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAkC;QAChE;IACF;IACA,IAAI,CAACT,OAAOe,OAAOC,IAAI,GAAG;QACxBZ,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAsC;QACpE;IACF;IACA,MAAMQ,KAAKvB,MAAMqB,OAAOE,EAAE,EAAE;IAC5B,MAAMC,OAAOxB,MAAMqB,OAAOG,IAAI,EAAE;IAEhC,IAAI;QACF,MAAMC,SAAS,MAAM7B,mBAAmBa,KAAKU,OAAO;YAClDO,OAAO;YACPC,SACE;QACJ;QACA,IAAIF,OAAOG,EAAE,KAAK,OAAO;YACvBlB,IAAIG,MAAM,CAACY,OAAOZ,MAAM,EAAEC,IAAI,CAAC;gBAAEC,OAAOU,OAAOV,KAAK;YAAC;YACrD;QACF;QACA,oEAAoE;QACpE,MAAMc,QAAQhC,gBAAgB4B,OAAOK,GAAG,EAAE;QAC1C,IAAID,OAAO;YACTnB,IAAIG,MAAM,CAACgB,MAAMhB,MAAM,EAAEC,IAAI,CAACe,MAAMb,IAAI;YACxC;QACF;QACA,MAAMxB,eAAe2B,OAAO;YAAEY,KAAKN,OAAOM,GAAG;YAAEC,OAAOP,OAAOO,KAAK;QAAC,GAAGZ,QAAQ;YAC5EE,MAAMD,OAAOC,IAAI;WACbC,KAAK;YAAEA;QAAG,IAAI,CAAC,GACfC,OAAO;YAAEA;QAAK,IAAI,CAAC;QAEzBd,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEc,IAAI;QAAK;IAClC,EAAE,OAAOb,OAAO;QACdkB,QAAQlB,KAAK,CAAC,6BAA6BI,OAAOJ;QAClDL,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAmC;IACnE;AACF,EAAC;AAED,eAAeP,sBAAqB"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* WHO IS CALLING AT THE ORGANIZATION LEVEL (AGL-2634).
|
|
19
|
+
*
|
|
20
|
+
* The CRM's host-routed actions — a stage move, a merge, an erasure, an
|
|
21
|
+
* email — were written for the site hub: each resolves the org FROM the
|
|
22
|
+
* site, checks the caller's role ON that site, and logs into that site's
|
|
23
|
+
* feed. The organization-level hub (AGL-2630) mounts the same surfaces with
|
|
24
|
+
* no site, and for a while every one of those actions ran "as the record's
|
|
25
|
+
* own capturing site", which left a record nobody's site had captured with
|
|
26
|
+
* nothing to run as. The org variant of each route authorizes HERE instead:
|
|
27
|
+
* by the org the body names, for an org-wide member, with the permission
|
|
28
|
+
* the whole CRM is gated on — the same three facts the org hub's own page
|
|
29
|
+
* gate checks (`resolveOrgCrmAccess`), so what the console offers and what
|
|
30
|
+
* the server admits cannot drift.
|
|
31
|
+
*
|
|
32
|
+
* A ROLE IS NOT A REACH. A site collaborator is a real member document
|
|
33
|
+
* carrying `role: 'editor'`, and that role holds `data.manage` — so a gate
|
|
34
|
+
* that read the permission alone would admit a contractor on one microsite
|
|
35
|
+
* to act over every site in the agency. `orgWide` is checked first and
|
|
36
|
+
* separately, and no permission grant restores it; this is the boundary the
|
|
37
|
+
* host variants get for free from the site role, spelled out for the variant
|
|
38
|
+
* that has no site.
|
|
39
|
+
*/
|
|
40
|
+
import { type AglynOrganization, type PluginApiRequest } from '@aglyn/aglyn/server';
|
|
41
|
+
/**
|
|
42
|
+
* Which level a CRM route runs at, read off its body once.
|
|
43
|
+
*
|
|
44
|
+
* `orgId` present is the ORGANIZATION variant, whatever else the body
|
|
45
|
+
* carries: the org-level console always names its org, and a `hostId`
|
|
46
|
+
* beside it is the record's own site or the site an email leaves from —
|
|
47
|
+
* never what the caller is authorized against. `hostId` alone is the site
|
|
48
|
+
* variant every route has always had.
|
|
49
|
+
*/
|
|
50
|
+
export interface CrmRouteScope {
|
|
51
|
+
level: 'site' | 'org';
|
|
52
|
+
hostId: string;
|
|
53
|
+
orgId: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function readCrmRouteScope(body: Record<string, unknown> | null | undefined): CrmRouteScope | null;
|
|
56
|
+
/**
|
|
57
|
+
* Whether `uid` holds `data.manage` in `orgId`, as the permission catalog
|
|
58
|
+
* resolves it: the member's role, a custom role and per-member overrides
|
|
59
|
+
* (AGL-2843).
|
|
60
|
+
*
|
|
61
|
+
* Asked of `memberHasOrgPermission`, never of the `permissions` map
|
|
62
|
+
* `resolveOrgPermissions` returns. That map is the legacy six keys plus the
|
|
63
|
+
* keys plugins register (`ResolvedOrgPermissionSet`), and `data.manage` is a
|
|
64
|
+
* catalog key, so it is never in it: a gate that read it there would refuse
|
|
65
|
+
* everyone but staff. A lookup that fails answers `false`.
|
|
66
|
+
*/
|
|
67
|
+
export declare function holdsDataManage(orgId: string | null | undefined, uid: string): Promise<boolean>;
|
|
68
|
+
export interface OrgCaller {
|
|
69
|
+
ok: true;
|
|
70
|
+
uid: string;
|
|
71
|
+
/** The token's address, or null for an account with none. */
|
|
72
|
+
email: string | null;
|
|
73
|
+
/** The token's display name, `''` when it carries none. */
|
|
74
|
+
name: string;
|
|
75
|
+
staff: boolean;
|
|
76
|
+
orgId: string;
|
|
77
|
+
org: Partial<AglynOrganization>;
|
|
78
|
+
}
|
|
79
|
+
export interface OrgCallerRefusal {
|
|
80
|
+
ok: false;
|
|
81
|
+
status: number;
|
|
82
|
+
error: string;
|
|
83
|
+
}
|
|
84
|
+
/** What the caller must hold over the whole organization. */
|
|
85
|
+
export type OrgCallerRequirement =
|
|
86
|
+
/** `data.manage`, org-wide — the key the CRM is gated on. Staff pass. */
|
|
87
|
+
'data.manage'
|
|
88
|
+
/** A workspace owner or admin, org-wide. Staff acting alone do NOT pass. */
|
|
89
|
+
| 'manage-org';
|
|
90
|
+
/**
|
|
91
|
+
* A verified session, org-wide reach in `orgId`, and the requirement — in
|
|
92
|
+
* that order, the cheapest refusal first — then the org document, which
|
|
93
|
+
* every org variant needs for the plan and the consent groups.
|
|
94
|
+
*
|
|
95
|
+
* Staff are admitted to the `data.manage` requirement as every CRM route
|
|
96
|
+
* admits them, and refused the `manage-org` one as the erasure route
|
|
97
|
+
* refuses them: the workspace is the controller of its people's data and
|
|
98
|
+
* the instruction to erase has to come from it.
|
|
99
|
+
*/
|
|
100
|
+
export declare function authorizeOrgCaller(req: PluginApiRequest, orgId: string, options: {
|
|
101
|
+
needs: OrgCallerRequirement;
|
|
102
|
+
refusal: string;
|
|
103
|
+
}): Promise<OrgCaller | OrgCallerRefusal>;
|
|
104
|
+
/**
|
|
105
|
+
* Every site of the organization, by document id — the host set an
|
|
106
|
+
* org-level act sweeps when it needs one (AGL-2634). One equality on
|
|
107
|
+
* `hosts.orgId`, the query the erasure route already ran, which needs no
|
|
108
|
+
* composite index.
|
|
109
|
+
*/
|
|
110
|
+
export declare function orgHostIds(firestore: FirebaseFirestore.Firestore, orgId: string): Promise<string[]>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* WHO IS CALLING AT THE ORGANIZATION LEVEL (AGL-2634).
|
|
18
|
+
*
|
|
19
|
+
* The CRM's host-routed actions — a stage move, a merge, an erasure, an
|
|
20
|
+
* email — were written for the site hub: each resolves the org FROM the
|
|
21
|
+
* site, checks the caller's role ON that site, and logs into that site's
|
|
22
|
+
* feed. The organization-level hub (AGL-2630) mounts the same surfaces with
|
|
23
|
+
* no site, and for a while every one of those actions ran "as the record's
|
|
24
|
+
* own capturing site", which left a record nobody's site had captured with
|
|
25
|
+
* nothing to run as. The org variant of each route authorizes HERE instead:
|
|
26
|
+
* by the org the body names, for an org-wide member, with the permission
|
|
27
|
+
* the whole CRM is gated on — the same three facts the org hub's own page
|
|
28
|
+
* gate checks (`resolveOrgCrmAccess`), so what the console offers and what
|
|
29
|
+
* the server admits cannot drift.
|
|
30
|
+
*
|
|
31
|
+
* A ROLE IS NOT A REACH. A site collaborator is a real member document
|
|
32
|
+
* carrying `role: 'editor'`, and that role holds `data.manage` — so a gate
|
|
33
|
+
* that read the permission alone would admit a contractor on one microsite
|
|
34
|
+
* to act over every site in the agency. `orgWide` is checked first and
|
|
35
|
+
* separately, and no permission grant restores it; this is the boundary the
|
|
36
|
+
* host variants get for free from the site role, spelled out for the variant
|
|
37
|
+
* that has no site.
|
|
38
|
+
*/ import { canManageOrg } from "@aglyn/aglyn/server";
|
|
39
|
+
import { firebaseAdmin, getOrgDoc, memberHasOrgPermission, resolveOrgMembership } from "@aglyn/tenant-data-admin";
|
|
40
|
+
import { isRefusedIdToken } from "@aglyn/tenant-data-admin/server/id-token-refusal";
|
|
41
|
+
import { resolveOrgPermissions } from "@aglyn/tenant-runtime/org-permissions";
|
|
42
|
+
export function readCrmRouteScope(body) {
|
|
43
|
+
var _ref, _ref1;
|
|
44
|
+
const hostId = String((_ref = body == null ? void 0 : body['hostId']) != null ? _ref : '').trim().slice(0, 128);
|
|
45
|
+
const orgId = String((_ref1 = body == null ? void 0 : body['orgId']) != null ? _ref1 : '').trim().slice(0, 128);
|
|
46
|
+
if (orgId) return {
|
|
47
|
+
level: 'org',
|
|
48
|
+
hostId,
|
|
49
|
+
orgId
|
|
50
|
+
};
|
|
51
|
+
if (hostId) return {
|
|
52
|
+
level: 'site',
|
|
53
|
+
hostId,
|
|
54
|
+
orgId: ''
|
|
55
|
+
};
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Whether `uid` holds `data.manage` in `orgId`, as the permission catalog
|
|
60
|
+
* resolves it: the member's role, a custom role and per-member overrides
|
|
61
|
+
* (AGL-2843).
|
|
62
|
+
*
|
|
63
|
+
* Asked of `memberHasOrgPermission`, never of the `permissions` map
|
|
64
|
+
* `resolveOrgPermissions` returns. That map is the legacy six keys plus the
|
|
65
|
+
* keys plugins register (`ResolvedOrgPermissionSet`), and `data.manage` is a
|
|
66
|
+
* catalog key, so it is never in it: a gate that read it there would refuse
|
|
67
|
+
* everyone but staff. A lookup that fails answers `false`.
|
|
68
|
+
*/ export async function holdsDataManage(orgId, uid) {
|
|
69
|
+
if (!orgId) return false;
|
|
70
|
+
try {
|
|
71
|
+
var _ref;
|
|
72
|
+
const membership = await resolveOrgMembership(uid, orgId);
|
|
73
|
+
const member = (_ref = membership == null ? void 0 : membership.member) != null ? _ref : null;
|
|
74
|
+
if (!member) return false;
|
|
75
|
+
return await memberHasOrgPermission(orgId, member, 'data.manage') === true;
|
|
76
|
+
} catch (unused) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A verified session, org-wide reach in `orgId`, and the requirement — in
|
|
82
|
+
* that order, the cheapest refusal first — then the org document, which
|
|
83
|
+
* every org variant needs for the plan and the consent groups.
|
|
84
|
+
*
|
|
85
|
+
* Staff are admitted to the `data.manage` requirement as every CRM route
|
|
86
|
+
* admits them, and refused the `manage-org` one as the erasure route
|
|
87
|
+
* refuses them: the workspace is the controller of its people's data and
|
|
88
|
+
* the instruction to erase has to come from it.
|
|
89
|
+
*/ export async function authorizeOrgCaller(req, orgId, options) {
|
|
90
|
+
var _req_headers_authorization, _ref, _decoded_email, _decoded_name;
|
|
91
|
+
const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
|
|
92
|
+
const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
|
|
93
|
+
if (!idToken) return {
|
|
94
|
+
ok: false,
|
|
95
|
+
status: 401,
|
|
96
|
+
error: 'Unauthenticated'
|
|
97
|
+
};
|
|
98
|
+
let decoded;
|
|
99
|
+
try {
|
|
100
|
+
decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
// A refused credential is the caller's 401. A failure to check one is
|
|
103
|
+
// ours and keeps a 5xx, so an outage pages instead of reading as a bad
|
|
104
|
+
// token (AGL-2852).
|
|
105
|
+
if (isRefusedIdToken(error)) return {
|
|
106
|
+
ok: false,
|
|
107
|
+
status: 401,
|
|
108
|
+
error: 'Unauthenticated'
|
|
109
|
+
};
|
|
110
|
+
console.error('[crm] the organization caller could not be verified', error);
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
status: 500,
|
|
114
|
+
error: 'The sign-in could not be checked. Try again.'
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const staff = decoded.staff === true;
|
|
118
|
+
const membership = await resolveOrgPermissions(decoded.uid, {
|
|
119
|
+
orgId
|
|
120
|
+
}).catch(()=>null);
|
|
121
|
+
const orgWide = Boolean(membership == null ? void 0 : membership.orgWide) && (membership == null ? void 0 : membership.orgId) === orgId;
|
|
122
|
+
const admitted = options.needs === 'data.manage' ? staff || orgWide && await holdsDataManage(orgId, decoded.uid) : orgWide && canManageOrg((_ref = membership == null ? void 0 : membership.role) != null ? _ref : null);
|
|
123
|
+
if (!admitted) return {
|
|
124
|
+
ok: false,
|
|
125
|
+
status: 403,
|
|
126
|
+
error: options.refusal
|
|
127
|
+
};
|
|
128
|
+
const org = await getOrgDoc(orgId).catch(()=>null);
|
|
129
|
+
if (!org) return {
|
|
130
|
+
ok: false,
|
|
131
|
+
status: 404,
|
|
132
|
+
error: 'Unknown organization'
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
ok: true,
|
|
136
|
+
uid: decoded.uid,
|
|
137
|
+
email: (_decoded_email = decoded.email) != null ? _decoded_email : null,
|
|
138
|
+
name: String((_decoded_name = decoded.name) != null ? _decoded_name : '').trim().slice(0, 120),
|
|
139
|
+
staff,
|
|
140
|
+
orgId,
|
|
141
|
+
org
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Every site of the organization, by document id — the host set an
|
|
146
|
+
* org-level act sweeps when it needs one (AGL-2634). One equality on
|
|
147
|
+
* `hosts.orgId`, the query the erasure route already ran, which needs no
|
|
148
|
+
* composite index.
|
|
149
|
+
*/ export async function orgHostIds(firestore, orgId) {
|
|
150
|
+
const hosts = await firestore.collection('hosts').where('orgId', '==', orgId).get();
|
|
151
|
+
return hosts.docs.map((doc)=>String(doc.id));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=org-caller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/org-caller.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 * WHO IS CALLING AT THE ORGANIZATION LEVEL (AGL-2634).\n *\n * The CRM's host-routed actions — a stage move, a merge, an erasure, an\n * email — were written for the site hub: each resolves the org FROM the\n * site, checks the caller's role ON that site, and logs into that site's\n * feed. The organization-level hub (AGL-2630) mounts the same surfaces with\n * no site, and for a while every one of those actions ran \"as the record's\n * own capturing site\", which left a record nobody's site had captured with\n * nothing to run as. The org variant of each route authorizes HERE instead:\n * by the org the body names, for an org-wide member, with the permission\n * the whole CRM is gated on — the same three facts the org hub's own page\n * gate checks (`resolveOrgCrmAccess`), so what the console offers and what\n * the server admits cannot drift.\n *\n * A ROLE IS NOT A REACH. A site collaborator is a real member document\n * carrying `role: 'editor'`, and that role holds `data.manage` — so a gate\n * that read the permission alone would admit a contractor on one microsite\n * to act over every site in the agency. `orgWide` is checked first and\n * separately, and no permission grant restores it; this is the boundary the\n * host variants get for free from the site role, spelled out for the variant\n * that has no site.\n */\n\nimport { type AglynOrganization, canManageOrg, type PluginApiRequest } from '@aglyn/aglyn/server'\nimport {\n firebaseAdmin,\n getOrgDoc,\n memberHasOrgPermission,\n resolveOrgMembership,\n} from '@aglyn/tenant-data-admin'\nimport { isRefusedIdToken } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\n\n/**\n * Which level a CRM route runs at, read off its body once.\n *\n * `orgId` present is the ORGANIZATION variant, whatever else the body\n * carries: the org-level console always names its org, and a `hostId`\n * beside it is the record's own site or the site an email leaves from —\n * never what the caller is authorized against. `hostId` alone is the site\n * variant every route has always had.\n */\nexport interface CrmRouteScope {\n level: 'site' | 'org'\n hostId: string\n orgId: string\n}\n\nexport function readCrmRouteScope(\n body: Record<string, unknown> | null | undefined,\n): CrmRouteScope | null {\n const hostId = String(body?.['hostId'] ?? '')\n .trim()\n .slice(0, 128)\n const orgId = String(body?.['orgId'] ?? '')\n .trim()\n .slice(0, 128)\n if (orgId) return { level: 'org', hostId, orgId }\n if (hostId) return { level: 'site', hostId, orgId: '' }\n return null\n}\n\n/**\n * Whether `uid` holds `data.manage` in `orgId`, as the permission catalog\n * resolves it: the member's role, a custom role and per-member overrides\n * (AGL-2843).\n *\n * Asked of `memberHasOrgPermission`, never of the `permissions` map\n * `resolveOrgPermissions` returns. That map is the legacy six keys plus the\n * keys plugins register (`ResolvedOrgPermissionSet`), and `data.manage` is a\n * catalog key, so it is never in it: a gate that read it there would refuse\n * everyone but staff. A lookup that fails answers `false`.\n */\nexport async function holdsDataManage(\n orgId: string | null | undefined,\n uid: string,\n): Promise<boolean> {\n if (!orgId) return false\n try {\n const membership = await resolveOrgMembership(uid, orgId)\n const member = membership?.member ?? null\n if (!member) return false\n return (await memberHasOrgPermission(orgId, member, 'data.manage')) === true\n } catch {\n return false\n }\n}\n\nexport interface OrgCaller {\n ok: true\n uid: string\n /** The token's address, or null for an account with none. */\n email: string | null\n /** The token's display name, `''` when it carries none. */\n name: string\n staff: boolean\n orgId: string\n org: Partial<AglynOrganization>\n}\n\nexport interface OrgCallerRefusal {\n ok: false\n status: number\n error: string\n}\n\n/** What the caller must hold over the whole organization. */\nexport type OrgCallerRequirement =\n /** `data.manage`, org-wide — the key the CRM is gated on. Staff pass. */\n | 'data.manage'\n /** A workspace owner or admin, org-wide. Staff acting alone do NOT pass. */\n | 'manage-org'\n\n/**\n * A verified session, org-wide reach in `orgId`, and the requirement — in\n * that order, the cheapest refusal first — then the org document, which\n * every org variant needs for the plan and the consent groups.\n *\n * Staff are admitted to the `data.manage` requirement as every CRM route\n * admits them, and refused the `manage-org` one as the erasure route\n * refuses them: the workspace is the controller of its people's data and\n * the instruction to erase has to come from it.\n */\nexport async function authorizeOrgCaller(\n req: PluginApiRequest,\n orgId: string,\n options: { needs: OrgCallerRequirement; refusal: string },\n): Promise<OrgCaller | OrgCallerRefusal> {\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return { ok: false, status: 401, error: 'Unauthenticated' }\n let decoded: { uid: string; email?: string; name?: string; staff?: unknown }\n try {\n decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n } catch (error) {\n // A refused credential is the caller's 401. A failure to check one is\n // ours and keeps a 5xx, so an outage pages instead of reading as a bad\n // token (AGL-2852).\n if (isRefusedIdToken(error)) return { ok: false, status: 401, error: 'Unauthenticated' }\n console.error('[crm] the organization caller could not be verified', error)\n return { ok: false, status: 500, error: 'The sign-in could not be checked. Try again.' }\n }\n const staff = decoded.staff === true\n const membership = await resolveOrgPermissions(decoded.uid, { orgId }).catch(\n () => null,\n )\n const orgWide = Boolean(membership?.orgWide) && membership?.orgId === orgId\n const admitted =\n options.needs === 'data.manage'\n ? staff || (orgWide && (await holdsDataManage(orgId, decoded.uid)))\n : orgWide && canManageOrg(membership?.role ?? null)\n if (!admitted) return { ok: false, status: 403, error: options.refusal }\n const org = await getOrgDoc(orgId).catch(() => null)\n if (!org) return { ok: false, status: 404, error: 'Unknown organization' }\n return {\n ok: true,\n uid: decoded.uid,\n email: decoded.email ?? null,\n name: String(decoded.name ?? '')\n .trim()\n .slice(0, 120),\n staff,\n orgId,\n org,\n }\n}\n\n/**\n * Every site of the organization, by document id — the host set an\n * org-level act sweeps when it needs one (AGL-2634). One equality on\n * `hosts.orgId`, the query the erasure route already ran, which needs no\n * composite index.\n */\nexport async function orgHostIds(\n firestore: FirebaseFirestore.Firestore,\n orgId: string,\n): Promise<string[]> {\n const hosts = await firestore.collection('hosts').where('orgId', '==', orgId).get()\n return hosts.docs.map((doc: { id: string }) => String(doc.id))\n}\n"],"names":["canManageOrg","firebaseAdmin","getOrgDoc","memberHasOrgPermission","resolveOrgMembership","isRefusedIdToken","resolveOrgPermissions","readCrmRouteScope","body","hostId","String","trim","slice","orgId","level","holdsDataManage","uid","membership","member","authorizeOrgCaller","req","options","decoded","authorization","headers","idToken","startsWith","length","undefined","ok","status","error","app","auth","verifyIdToken","console","staff","catch","orgWide","Boolean","admitted","needs","role","refusal","org","email","name","orgHostIds","firestore","hosts","collection","where","get","docs","map","doc","id"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;CAsBC,GAED,SAAiCA,YAAY,QAA+B,sBAAqB;AACjG,SACEC,aAAa,EACbC,SAAS,EACTC,sBAAsB,EACtBC,oBAAoB,QACf,2BAA0B;AACjC,SAASC,gBAAgB,QAAQ,mDAAkD;AACnF,SAASC,qBAAqB,QAAQ,wCAAuC;AAiB7E,OAAO,SAASC,kBACdC,IAAgD;;IAEhD,MAAMC,SAASC,eAAOF,wBAAAA,IAAM,CAAC,SAAS,mBAAI,IACvCG,IAAI,GACJC,KAAK,CAAC,GAAG;IACZ,MAAMC,QAAQH,gBAAOF,wBAAAA,IAAM,CAAC,QAAQ,oBAAI,IACrCG,IAAI,GACJC,KAAK,CAAC,GAAG;IACZ,IAAIC,OAAO,OAAO;QAAEC,OAAO;QAAOL;QAAQI;IAAM;IAChD,IAAIJ,QAAQ,OAAO;QAAEK,OAAO;QAAQL;QAAQI,OAAO;IAAG;IACtD,OAAO;AACT;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAeE,gBACpBF,KAAgC,EAChCG,GAAW;IAEX,IAAI,CAACH,OAAO,OAAO;IACnB,IAAI;;QACF,MAAMI,aAAa,MAAMb,qBAAqBY,KAAKH;QACnD,MAAMK,iBAASD,8BAAAA,WAAYC,MAAM,mBAAI;QACrC,IAAI,CAACA,QAAQ,OAAO;QACpB,OAAO,AAAC,MAAMf,uBAAuBU,OAAOK,QAAQ,mBAAoB;IAC1E,EAAE,eAAM;QACN,OAAO;IACT;AACF;AA2BA;;;;;;;;;CASC,GACD,OAAO,eAAeC,mBACpBC,GAAqB,EACrBP,KAAa,EACbQ,OAAyD;QAE5BD,kCA+BpBE,gBACMA;IAhCf,MAAMC,gBAAgBb,QAAOU,6BAAAA,IAAII,OAAO,CAACD,aAAa,YAAzBH,6BAA6B;IAC1D,MAAMK,UAAUF,cAAcG,UAAU,CAAC,aACrCH,cAAcX,KAAK,CAAC,UAAUe,MAAM,IACpCC;IACJ,IAAI,CAACH,SAAS,OAAO;QAAEI,IAAI;QAAOC,QAAQ;QAAKC,OAAO;IAAkB;IACxE,IAAIT;IACJ,IAAI;QACFA,UAAU,MAAMrB,cAAc+B,GAAG,GAAGC,IAAI,GAAGC,aAAa,CAACT;IAC3D,EAAE,OAAOM,OAAO;QACd,sEAAsE;QACtE,uEAAuE;QACvE,oBAAoB;QACpB,IAAI1B,iBAAiB0B,QAAQ,OAAO;YAAEF,IAAI;YAAOC,QAAQ;YAAKC,OAAO;QAAkB;QACvFI,QAAQJ,KAAK,CAAC,uDAAuDA;QACrE,OAAO;YAAEF,IAAI;YAAOC,QAAQ;YAAKC,OAAO;QAA+C;IACzF;IACA,MAAMK,QAAQd,QAAQc,KAAK,KAAK;IAChC,MAAMnB,aAAa,MAAMX,sBAAsBgB,QAAQN,GAAG,EAAE;QAAEH;IAAM,GAAGwB,KAAK,CAC1E,IAAM;IAER,MAAMC,UAAUC,QAAQtB,8BAAAA,WAAYqB,OAAO,KAAKrB,CAAAA,8BAAAA,WAAYJ,KAAK,MAAKA;IACtE,MAAM2B,WACJnB,QAAQoB,KAAK,KAAK,gBACdL,SAAUE,WAAY,MAAMvB,gBAAgBF,OAAOS,QAAQN,GAAG,IAC9DsB,WAAWtC,qBAAaiB,8BAAAA,WAAYyB,IAAI,mBAAI;IAClD,IAAI,CAACF,UAAU,OAAO;QAAEX,IAAI;QAAOC,QAAQ;QAAKC,OAAOV,QAAQsB,OAAO;IAAC;IACvE,MAAMC,MAAM,MAAM1C,UAAUW,OAAOwB,KAAK,CAAC,IAAM;IAC/C,IAAI,CAACO,KAAK,OAAO;QAAEf,IAAI;QAAOC,QAAQ;QAAKC,OAAO;IAAuB;IACzE,OAAO;QACLF,IAAI;QACJb,KAAKM,QAAQN,GAAG;QAChB6B,KAAK,GAAEvB,iBAAAA,QAAQuB,KAAK,YAAbvB,iBAAiB;QACxBwB,MAAMpC,QAAOY,gBAAAA,QAAQwB,IAAI,YAAZxB,gBAAgB,IAC1BX,IAAI,GACJC,KAAK,CAAC,GAAG;QACZwB;QACAvB;QACA+B;IACF;AACF;AAEA;;;;;CAKC,GACD,OAAO,eAAeG,WACpBC,SAAsC,EACtCnC,KAAa;IAEb,MAAMoC,QAAQ,MAAMD,UAAUE,UAAU,CAAC,SAASC,KAAK,CAAC,SAAS,MAAMtC,OAAOuC,GAAG;IACjF,OAAOH,MAAMI,IAAI,CAACC,GAAG,CAAC,CAACC,MAAwB7C,OAAO6C,IAAIC,EAAE;AAC9D"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* THE RECIPES, INSTALLED FROM THE ORGANIZATION (AGL-2639).
|
|
19
|
+
*
|
|
20
|
+
* On a site, a recipe opens the Actions editor prefilled and writes nothing
|
|
21
|
+
* until the person saves — the editor is the writer, and the site's own
|
|
22
|
+
* role is the authority. The organization-level CRM hub has no editor: it
|
|
23
|
+
* mounts over every site with no site of its own, and an org running five
|
|
24
|
+
* sites installed "Welcome a new lead" five times by hand. These two routes
|
|
25
|
+
* are the org hub's door to a site's automations.
|
|
26
|
+
*
|
|
27
|
+
* `crm/recipe-install` writes the action a recipe builds into ONE site's
|
|
28
|
+
* `hosts/{hostId}/actions`, as the site editor would have saved it — the
|
|
29
|
+
* same builder, the same validator, the same stored shape — and refuses a
|
|
30
|
+
* second install of the same recipe on the same site by the stamp the
|
|
31
|
+
* builder puts on it. A server route rather than a client write for three
|
|
32
|
+
* reasons the browser cannot supply: the document is created by a server
|
|
33
|
+
* on every plan (the rules close `actions` to client creates, and the
|
|
34
|
+
* per-site cap is counted here); the stamp is the server's word, not the
|
|
35
|
+
* caller's; and the site is checked to be one the caller may manage —
|
|
36
|
+
* at the org level, a site OF THE ORG the caller holds org-wide reach in,
|
|
37
|
+
* so a host id typed into the body cannot reach a site under another
|
|
38
|
+
* workspace.
|
|
39
|
+
*
|
|
40
|
+
* `crm/recipe-status` reads the stamps back for every site of the org, so
|
|
41
|
+
* the card can say which sites carry which recipe. An action with no stamp
|
|
42
|
+
* at all — saved before the stamp existed — is counted, not ignored: it
|
|
43
|
+
* may well be a recipe, and the reader is told the site "may already have
|
|
44
|
+
* it" rather than shown a clean slate.
|
|
45
|
+
*
|
|
46
|
+
* Both routes serve the site variant too (`hostId` alone, authorized by the
|
|
47
|
+
* host role that creates actions today), so a site-level caller gets the
|
|
48
|
+
* same answers; the org variant (`orgId` in the body) is the one the org
|
|
49
|
+
* hub uses.
|
|
50
|
+
*/
|
|
51
|
+
import { type PluginApiHandler } from '@aglyn/aglyn/server';
|
|
52
|
+
export declare const CRM_RECIPE_INSTALL_ROUTE: "crm/recipe-install";
|
|
53
|
+
export declare const CRM_RECIPE_STATUS_ROUTE: "crm/recipe-status";
|
|
54
|
+
/**
|
|
55
|
+
* `POST /api/crm/recipe-install`
|
|
56
|
+
*
|
|
57
|
+
* The body is a CrmRecipeInstallRequest: the site, the recipe, the form a
|
|
58
|
+
* recipe that needs one is keyed on, and the org at the organization level.
|
|
59
|
+
* Answers 201 with a CrmRecipeInstallResult once the action is written, and
|
|
60
|
+
* 409 when a live action on the site already carries the stamp — naming it,
|
|
61
|
+
* so the caller can say "already installed" rather than install it twice.
|
|
62
|
+
*
|
|
63
|
+
* The write is one transaction: the stamps are read, the duplicate and the
|
|
64
|
+
* per-site cap are judged, and the document is created — all inside it, so
|
|
65
|
+
* two installs racing for the same site cannot both land.
|
|
66
|
+
*/
|
|
67
|
+
export declare const crmRecipeInstallHandler: PluginApiHandler;
|
|
68
|
+
/**
|
|
69
|
+
* `POST /api/crm/recipe-status`
|
|
70
|
+
*
|
|
71
|
+
* The body names the organization (`orgId`) for every one of its sites, or
|
|
72
|
+
* one site (`hostId`) for that site alone. Answers `ok: true` and `sites`,
|
|
73
|
+
* one CrmRecipeSiteStatus per site swept.
|
|
74
|
+
*/
|
|
75
|
+
export declare const crmRecipeStatusHandler: PluginApiHandler;
|