backend-manager 5.9.4 → 5.9.6
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/CLAUDE.md +6 -0
- package/package.json +8 -1
- package/src/cli/commands/install.js +4 -3
- package/src/cli/commands/setup-tests/backend-manager.js +2 -1
- package/src/cli/commands/setup-tests/firebase-admin.js +2 -1
- package/src/cli/commands/setup-tests/firebase-functions.js +2 -1
- package/src/cli/utils/safe-install.js +13 -0
- package/src/manager/routes/payments/cancel/post.js +22 -2
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/metadata.json +14 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/newsletter.html +486 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/newsletter.md +41 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/newsletter.mjml +97 -0
- package/{test/email/fixtures/clean.json → src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/structure.json} +16 -4
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-11T03-28-07/summary.md +1 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/metadata.json +14 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/newsletter.html +486 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/newsletter.md +41 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/newsletter.mjml +97 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/structure.json +42 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-00-28/summary.md +1 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/metadata.json +14 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/newsletter.html +486 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/newsletter.md +41 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/newsletter.mjml +97 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/structure.json +42 -0
- package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-06-18T01-01-34/summary.md +1 -0
- package/src/test/fixtures/firebase-project/.temp/test-mode.json +6 -0
- package/src/test/fixtures/firebase-project/database-debug.log +12 -0
- package/src/test/fixtures/firebase-project/firestore-debug.log +136 -0
- package/src/test/fixtures/firebase-project/pubsub-debug.log +17 -0
- package/src/test/test-accounts.js +9 -0
- package/.codeclimate.yml +0 -32
- package/.nvmrc +0 -1
- package/CHANGELOG.md +0 -1432
- package/PROGRESS.md +0 -86
- package/TODO-2.md +0 -121
- package/TODO-CHARGEBLAST.md +0 -32
- package/TODO-WEBHOOK-KEY-LEGACY-REMOVAL.md +0 -15
- package/TODO-WEBHOOK-KEY-UPGRADE.md +0 -138
- package/TODO-email-auth.md +0 -14
- package/TODO.md +0 -187
- package/plans/mcp2.md +0 -247
- package/scripts/test-helper-providers.js +0 -162
- package/scripts/update-disposable-domains.js +0 -44
- package/templates/_.env +0 -42
- package/templates/_.gitignore +0 -60
- package/templates/backend-manager-config.json +0 -249
- package/templates/database.rules.json +0 -83
- package/templates/firebase.json +0 -66
- package/templates/firestore.indexes.json +0 -4
- package/templates/firestore.rules +0 -112
- package/templates/public/404.html +0 -26
- package/templates/public/index.html +0 -24
- package/templates/remoteconfig.template.json +0 -1
- package/templates/storage-lifecycle-config-1-day.json +0 -9
- package/templates/storage-lifecycle-config-30-days.json +0 -9
- package/templates/storage.rules +0 -8
- package/test/_init/accounts-validation.js +0 -58
- package/test/_legacy/ai/index.js +0 -231
- package/test/_legacy/ai/test.jpg +0 -0
- package/test/_legacy/ai/test.pdf +0 -0
- package/test/_legacy/index.js +0 -31
- package/test/_legacy/payment-resolver/chargebee/orders/refunded.json +0 -0
- package/test/_legacy/payment-resolver/chargebee/orders/unpaid.json +0 -98
- package/test/_legacy/payment-resolver/chargebee/subscriptions/active.json +0 -578
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-in-trial.json +0 -38
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-skipped-to-active.json +0 -135
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-active-to-cancelled.json +0 -42
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-active.json +0 -44
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-cancelled.json +0 -39
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-refund.json +0 -143
- package/test/_legacy/payment-resolver/chargebee/subscriptions/trial-to-suspended.json +0 -48
- package/test/_legacy/payment-resolver/coinbase/orders/regular.json +0 -204
- package/test/_legacy/payment-resolver/coinbase/subscriptions/cancelled.json +0 -204
- package/test/_legacy/payment-resolver/coinbase/subscriptions/paid-2.json +0 -125
- package/test/_legacy/payment-resolver/index.js +0 -1558
- package/test/_legacy/payment-resolver/paypal/orders/refunded.json +0 -0
- package/test/_legacy/payment-resolver/paypal/orders/regular.json +0 -120
- package/test/_legacy/payment-resolver/paypal/subscriptions/active-refund-previous-stmnt.json +0 -323
- package/test/_legacy/payment-resolver/paypal/subscriptions/active.json +0 -192
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-in-trial.json +0 -125
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-payment-not-complete.json +0 -76
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-payment-overdue-2.json +0 -114
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-payment-overdue.json +0 -114
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-active-to-cancelled.json +0 -111
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-active.json +0 -132
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-cancelled.json +0 -107
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-expired.json +0 -91
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-refund.json +0 -147
- package/test/_legacy/payment-resolver/paypal/subscriptions/trial-to-suspended.json +0 -120
- package/test/_legacy/payment-resolver/stripe/orders/refunded.json +0 -0
- package/test/_legacy/payment-resolver/stripe/orders/regular.json +0 -91
- package/test/_legacy/payment-resolver/stripe/subscriptions/active.json +0 -421
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-in-trial.json +0 -349
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-active-failed-authorization.json +0 -430
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-active.json +0 -319
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-cancelled.json +0 -319
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-refund.json +0 -414
- package/test/_legacy/payment-resolver/stripe/subscriptions/trial-to-suspended.json +0 -319
- package/test/_legacy/payment-resolver/stripe/subscriptions/unsure.json +0 -339
- package/test/_legacy/test-new +0 -1178
- package/test/_legacy/test.md +0 -89
- package/test/_legacy/usage.js +0 -175
- package/test/_legacy/user.js +0 -31
- package/test/ai/tools-live.js +0 -170
- package/test/boot/emulator-boots.js +0 -37
- package/test/content/blog-generate.js +0 -160
- package/test/email/campaign-send.js +0 -45
- package/test/email/consent-lifecycle.js +0 -257
- package/test/email/feedback-and-plain-send.js +0 -52
- package/test/email/fixtures/editorial.json +0 -30
- package/test/email/fixtures/field-report.json +0 -53
- package/test/email/marketing-lifecycle.js +0 -132
- package/test/email/newsletter-generate.js +0 -819
- package/test/email/newsletter-templates.js +0 -491
- package/test/email/templates.js +0 -207
- package/test/email/transactional-send.js +0 -34
- package/test/email/transactional.js +0 -560
- package/test/email/validation.js +0 -710
- package/test/events/auth-delete-race.js +0 -201
- package/test/events/payments/journey-payments-cancel-endpoint.js +0 -100
- package/test/events/payments/journey-payments-cancel.js +0 -182
- package/test/events/payments/journey-payments-discount.js +0 -89
- package/test/events/payments/journey-payments-failure.js +0 -146
- package/test/events/payments/journey-payments-legacy-product.js +0 -149
- package/test/events/payments/journey-payments-one-time-failure.js +0 -111
- package/test/events/payments/journey-payments-one-time.js +0 -136
- package/test/events/payments/journey-payments-plan-change.js +0 -144
- package/test/events/payments/journey-payments-refund-webhook.js +0 -180
- package/test/events/payments/journey-payments-suspend.js +0 -181
- package/test/events/payments/journey-payments-trial-cancel.js +0 -130
- package/test/events/payments/journey-payments-trial.js +0 -171
- package/test/events/payments/journey-payments-uid-resolution.js +0 -132
- package/test/events/payments/journey-payments-upgrade.js +0 -135
- package/test/fixtures/chargebee/invoice-one-time.json +0 -27
- package/test/fixtures/chargebee/subscription-active.json +0 -44
- package/test/fixtures/chargebee/subscription-cancelled.json +0 -42
- package/test/fixtures/chargebee/subscription-in-trial.json +0 -41
- package/test/fixtures/chargebee/subscription-legacy-plan.json +0 -41
- package/test/fixtures/chargebee/subscription-non-renewing.json +0 -41
- package/test/fixtures/chargebee/subscription-paused.json +0 -42
- package/test/fixtures/chargebee/webhook-payment-failed.json +0 -51
- package/test/fixtures/chargebee/webhook-subscription-created.json +0 -47
- package/test/fixtures/paypal/order-approved.json +0 -62
- package/test/fixtures/paypal/order-completed.json +0 -110
- package/test/fixtures/paypal/subscription-active.json +0 -76
- package/test/fixtures/paypal/subscription-cancelled.json +0 -50
- package/test/fixtures/paypal/subscription-suspended.json +0 -65
- package/test/fixtures/stripe/checkout-session-completed.json +0 -130
- package/test/fixtures/stripe/invoice-payment-failed.json +0 -148
- package/test/fixtures/stripe/invoice-subscription-payment-failed.json +0 -28
- package/test/fixtures/stripe/subscription-active.json +0 -161
- package/test/fixtures/stripe/subscription-canceled.json +0 -161
- package/test/fixtures/stripe/subscription-trialing.json +0 -161
- package/test/functions/admin/database-read.js +0 -134
- package/test/functions/admin/database-write.js +0 -159
- package/test/functions/admin/edit-post.js +0 -366
- package/test/functions/admin/firestore-query.js +0 -207
- package/test/functions/admin/firestore-read.js +0 -129
- package/test/functions/admin/firestore-write.js +0 -105
- package/test/functions/admin/get-stats.js +0 -115
- package/test/functions/admin/send-email.js +0 -119
- package/test/functions/admin/send-notification.js +0 -208
- package/test/functions/admin/write-repo-content.js +0 -223
- package/test/functions/general/add-marketing-contact.js +0 -335
- package/test/functions/general/fetch-post.js +0 -54
- package/test/functions/general/generate-uuid.js +0 -114
- package/test/functions/test/authenticate.js +0 -64
- package/test/functions/user/create-custom-token.js +0 -73
- package/test/functions/user/delete.js +0 -135
- package/test/functions/user/get-active-sessions.js +0 -111
- package/test/functions/user/get-subscription-info.js +0 -99
- package/test/functions/user/regenerate-api-keys.js +0 -156
- package/test/functions/user/resolve.js +0 -52
- package/test/functions/user/sign-out-all-sessions.js +0 -94
- package/test/functions/user/sign-up.js +0 -252
- package/test/functions/user/submit-feedback.js +0 -104
- package/test/functions/user/validate-settings.js +0 -82
- package/test/helpers/ai-request-payload.js +0 -300
- package/test/helpers/ai-test-provider.js +0 -202
- package/test/helpers/ai-tools-format.js +0 -383
- package/test/helpers/content/blog-auto-publisher.js +0 -484
- package/test/helpers/content/feed-parser.js +0 -528
- package/test/helpers/content/ghostii-blocks.js +0 -134
- package/test/helpers/content/ghostii-feed-integration.js +0 -404
- package/test/helpers/content/ghostii-write-article.js +0 -243
- package/test/helpers/environment.js +0 -230
- package/test/helpers/infer-contact.js +0 -113
- package/test/helpers/merge-line-files.js +0 -271
- package/test/helpers/payment/chargebee/parse-webhook.js +0 -413
- package/test/helpers/payment/chargebee/to-unified-one-time.js +0 -147
- package/test/helpers/payment/chargebee/to-unified-subscription.js +0 -648
- package/test/helpers/payment/paypal/parse-webhook.js +0 -539
- package/test/helpers/payment/paypal/to-unified-one-time.js +0 -382
- package/test/helpers/payment/paypal/to-unified-subscription.js +0 -820
- package/test/helpers/payment/stripe/parse-webhook.js +0 -447
- package/test/helpers/payment/stripe/to-unified-one-time.js +0 -306
- package/test/helpers/payment/stripe/to-unified-subscription.js +0 -708
- package/test/helpers/sanitize.js +0 -222
- package/test/helpers/slugify.js +0 -394
- package/test/helpers/storage.js +0 -194
- package/test/helpers/user.js +0 -755
- package/test/helpers/webhook-forward.js +0 -418
- package/test/mcp/discovery.js +0 -53
- package/test/mcp/oauth.js +0 -161
- package/test/mcp/protocol.js +0 -268
- package/test/mcp/roles.js +0 -188
- package/test/mcp/utils.js +0 -245
- package/test/routes/admin/create-post.js +0 -364
- package/test/routes/admin/database.js +0 -131
- package/test/routes/admin/deduplicate-image-alts.js +0 -190
- package/test/routes/admin/email.js +0 -114
- package/test/routes/admin/firestore-query.js +0 -204
- package/test/routes/admin/firestore.js +0 -127
- package/test/routes/admin/infer-contact.js +0 -218
- package/test/routes/admin/notification.js +0 -198
- package/test/routes/admin/post-resize-image.js +0 -184
- package/test/routes/admin/post.js +0 -368
- package/test/routes/admin/repo-content.js +0 -223
- package/test/routes/admin/stats.js +0 -112
- package/test/routes/content/post.js +0 -54
- package/test/routes/general/uuid.js +0 -115
- package/test/routes/marketing/campaign.js +0 -125
- package/test/routes/marketing/contact.js +0 -370
- package/test/routes/marketing/email-preferences.js +0 -292
- package/test/routes/marketing/push-send.js +0 -32
- package/test/routes/marketing/webhook-forward.js +0 -61
- package/test/routes/marketing/webhook.js +0 -639
- package/test/routes/payments/cancel.js +0 -140
- package/test/routes/payments/discount.js +0 -80
- package/test/routes/payments/dispute-alert.js +0 -320
- package/test/routes/payments/intent.js +0 -336
- package/test/routes/payments/portal.js +0 -92
- package/test/routes/payments/refund.js +0 -179
- package/test/routes/payments/trial-eligibility.js +0 -71
- package/test/routes/payments/webhook.js +0 -107
- package/test/routes/test/authenticate.js +0 -59
- package/test/routes/test/schema.js +0 -554
- package/test/routes/test/usage.js +0 -342
- package/test/routes/user/api-keys.js +0 -156
- package/test/routes/user/delete.js +0 -136
- package/test/routes/user/feedback.js +0 -104
- package/test/routes/user/sessions.js +0 -199
- package/test/routes/user/settings-validate.js +0 -82
- package/test/routes/user/signup.js +0 -542
- package/test/routes/user/subscription.js +0 -99
- package/test/routes/user/token.js +0 -73
- package/test/routes/user/user.js +0 -157
- package/test/rules/notifications.js +0 -410
- package/test/rules/payments-carts.js +0 -371
- package/test/rules/user.js +0 -396
package/CLAUDE.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> **Note for contributors and Claude:** This file is the architectural overview — identity, top-level conventions, and a map to deep references. The **meat** (per-subsystem APIs, behavior tables, recipes) lives in `docs/<topic>.md`. When extending or adding content, write it in the matching `docs/*.md` file and cross-link from here — do NOT inline it. If a topic doesn't have a doc yet, create one. Goal: keep this file under 250 lines.
|
|
4
4
|
|
|
5
|
+
> **Mirrored structure:** BEM, UJM, BXM, and EM CLAUDE.md files mirror each other — shared sections (Supply-Chain Security, Development Workflow, File Conventions, etc.) appear in the **same order at the same position** across all four. When adding a section that applies to multiple frameworks, insert it in the same spot in all of them.
|
|
6
|
+
|
|
5
7
|
## Identity
|
|
6
8
|
|
|
7
9
|
Backend Manager (BEM) is a comprehensive framework for building modern Firebase Cloud Functions backends. Sister project to Electron Manager (EM), Browser Extension Manager (BXM), and Ultimate Jekyll Manager (UJM). Provides a single `Manager.init(exports, {...})` bootstrap that wires built-in functions (`bm_api`, auth events, cron jobs), helper classes (Assistant, User, Analytics, Usage, Middleware, Settings, Utilities, Metadata), payment processor integrations (Stripe / PayPal), Firestore-trigger pipelines, marketing campaign automation, an MCP server, and a CLI for emulator/deploy/logs/auth/Firestore operations.
|
|
@@ -94,6 +96,10 @@ See [docs/cli-firestore-auth.md](docs/cli-firestore-auth.md) and [docs/cli-logs.
|
|
|
94
96
|
- **If the user reports an error**, check the emulator/test output for the root cause before guessing.
|
|
95
97
|
- **Live-test UI changes via CDP.** When working on admin dashboards or browser-facing endpoints, use the `chrome-devtools` MCP tools (screenshots, click, evaluate JS, console logs) to verify the change works in the running browser. See `~/.claude/mcp-server/servers/chrome-devtools/CLAUDE.md`.
|
|
96
98
|
|
|
99
|
+
## Supply-Chain Security
|
|
100
|
+
|
|
101
|
+
All `npm install` calls in CLI commands (`npx mgr i`, `npx mgr setup`, setup-tests) route through the `safeInstall()` helper (`src/cli/utils/safe-install.js`). It prefixes `sfw` (Socket Firewall) when installed — blocking confirmed malware at the network level before packages reach disk. Falls back to plain npm if sfw isn't available. CI workflows install sfw globally and run `sfw npm install`/`sfw npm ci`. Installs will **fail if sfw detects confirmed malware** in any package in the dependency tree; non-critical CVEs and quality warnings pass through.
|
|
102
|
+
|
|
97
103
|
## File Conventions
|
|
98
104
|
|
|
99
105
|
- **CommonJS** throughout. `prepare-package` copies `src/` → `dist/` 1:1 (no transforms).
|
package/package.json
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "5.9.
|
|
3
|
+
"version": "5.9.6",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"src/",
|
|
8
|
+
"dist/",
|
|
9
|
+
"bin/",
|
|
10
|
+
"docs/",
|
|
11
|
+
"CLAUDE.md"
|
|
12
|
+
],
|
|
6
13
|
"bin": {
|
|
7
14
|
"bm": "./bin/backend-manager",
|
|
8
15
|
"bem": "./bin/backend-manager",
|
|
@@ -5,6 +5,7 @@ const powertools = require('node-powertools');
|
|
|
5
5
|
const Npm = require('npm-api');
|
|
6
6
|
const jetpack = require('fs-jetpack');
|
|
7
7
|
const wonderfulVersion = require('wonderful-version');
|
|
8
|
+
const { safeInstall } = require('../utils/safe-install');
|
|
8
9
|
|
|
9
10
|
class InstallCommand extends BaseCommand {
|
|
10
11
|
async execute(type) {
|
|
@@ -110,12 +111,12 @@ class InstallCommand extends BaseCommand {
|
|
|
110
111
|
const command = name;
|
|
111
112
|
this.log('Running ', command);
|
|
112
113
|
|
|
113
|
-
return await
|
|
114
|
+
return await safeInstall(command, { log: true })
|
|
114
115
|
.catch((e) => {
|
|
115
116
|
throw e;
|
|
116
117
|
});
|
|
117
118
|
}
|
|
118
|
-
|
|
119
|
+
|
|
119
120
|
if (name.indexOf('file:') > -1) {
|
|
120
121
|
v = '';
|
|
121
122
|
} else if (!version) {
|
|
@@ -133,7 +134,7 @@ class InstallCommand extends BaseCommand {
|
|
|
133
134
|
const command = `npm i ${name}${v}${t}`;
|
|
134
135
|
this.log('Running ', command);
|
|
135
136
|
|
|
136
|
-
return await
|
|
137
|
+
return await safeInstall(command, { log: true })
|
|
137
138
|
.catch((e) => {
|
|
138
139
|
throw e;
|
|
139
140
|
});
|
|
@@ -4,6 +4,7 @@ const wonderfulVersion = require('wonderful-version');
|
|
|
4
4
|
const powertools = require('node-powertools');
|
|
5
5
|
const Npm = require('npm-api');
|
|
6
6
|
const helpers = require('./helpers');
|
|
7
|
+
const { safeInstall } = require('../../utils/safe-install');
|
|
7
8
|
|
|
8
9
|
class BackendManagerTest extends BaseTest {
|
|
9
10
|
getName() {
|
|
@@ -72,7 +73,7 @@ class BackendManagerTest extends BaseTest {
|
|
|
72
73
|
console.log('Running ', command);
|
|
73
74
|
|
|
74
75
|
// Execute
|
|
75
|
-
await
|
|
76
|
+
await safeInstall(command);
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -3,6 +3,7 @@ const chalk = require('chalk').default;
|
|
|
3
3
|
const wonderfulVersion = require('wonderful-version');
|
|
4
4
|
const powertools = require('node-powertools');
|
|
5
5
|
const helpers = require('./helpers');
|
|
6
|
+
const { safeInstall } = require('../../utils/safe-install');
|
|
6
7
|
|
|
7
8
|
class FirebaseAdminTest extends BaseTest {
|
|
8
9
|
getName() {
|
|
@@ -64,7 +65,7 @@ class FirebaseAdminTest extends BaseTest {
|
|
|
64
65
|
console.log('Running ', command);
|
|
65
66
|
|
|
66
67
|
// Execute
|
|
67
|
-
await
|
|
68
|
+
await safeInstall(command);
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -3,6 +3,7 @@ const chalk = require('chalk').default;
|
|
|
3
3
|
const wonderfulVersion = require('wonderful-version');
|
|
4
4
|
const powertools = require('node-powertools');
|
|
5
5
|
const helpers = require('./helpers');
|
|
6
|
+
const { safeInstall } = require('../../utils/safe-install');
|
|
6
7
|
|
|
7
8
|
class FirebaseFunctionsTest extends BaseTest {
|
|
8
9
|
getName() {
|
|
@@ -64,7 +65,7 @@ class FirebaseFunctionsTest extends BaseTest {
|
|
|
64
65
|
console.log('Running ', command);
|
|
65
66
|
|
|
66
67
|
// Execute
|
|
67
|
-
await
|
|
68
|
+
await safeInstall(command);
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { execute } = require('node-powertools');
|
|
2
|
+
|
|
3
|
+
let _hasSfw;
|
|
4
|
+
async function safeInstall(command, options) {
|
|
5
|
+
if (_hasSfw === undefined) {
|
|
6
|
+
_hasSfw = await execute('sfw --version', { log: false }).then(() => true).catch(() => false);
|
|
7
|
+
}
|
|
8
|
+
const isInstall = /^npm\s+(install|i)\b/.test(command);
|
|
9
|
+
const prefix = (_hasSfw && isInstall) ? 'sfw ' : '';
|
|
10
|
+
return execute(`${prefix}${command}`, options || { log: true });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = { safeInstall };
|
|
@@ -26,8 +26,8 @@ module.exports = async ({ assistant, user, settings }) => {
|
|
|
26
26
|
|
|
27
27
|
const subscription = user.subscription;
|
|
28
28
|
|
|
29
|
-
// Require an active
|
|
30
|
-
if (!subscription || subscription.status !== 'active' || subscription.product?.id === 'basic') {
|
|
29
|
+
// Require an active or suspended paid subscription
|
|
30
|
+
if (!subscription || (subscription.status !== 'active' && subscription.status !== 'suspended') || subscription.product?.id === 'basic') {
|
|
31
31
|
assistant.log(`Cancel rejected: uid=${uid}, status=${subscription?.status}, product=${subscription?.product?.id}`);
|
|
32
32
|
return assistant.respond('No active paid subscription found', { code: 400 });
|
|
33
33
|
}
|
|
@@ -69,6 +69,26 @@ module.exports = async ({ assistant, user, settings }) => {
|
|
|
69
69
|
try {
|
|
70
70
|
await processorModule.cancelAtPeriodEnd({ resourceId, uid, subscription, assistant });
|
|
71
71
|
} catch (e) {
|
|
72
|
+
// If the subscription is suspended and the processor rejects (subscription already dead/gone),
|
|
73
|
+
// directly reset the user's subscription to cancelled so they can re-subscribe
|
|
74
|
+
if (subscription.status === 'suspended') {
|
|
75
|
+
assistant.log(`Processor cancel failed for suspended subscription (${e.message}), resetting directly`);
|
|
76
|
+
const admin = assistant.Manager.libraries.admin;
|
|
77
|
+
const now = powertools.timestamp(new Date(), { output: 'string' });
|
|
78
|
+
const nowUNIX = powertools.timestamp(now, { output: 'unix' });
|
|
79
|
+
|
|
80
|
+
await admin.firestore().doc(`users/${uid}`).set({
|
|
81
|
+
subscription: {
|
|
82
|
+
status: 'cancelled',
|
|
83
|
+
product: { id: 'basic', name: 'Basic' },
|
|
84
|
+
cancellation: { pending: false, date: { timestamp: now, timestampUNIX: nowUNIX } },
|
|
85
|
+
},
|
|
86
|
+
}, { merge: true });
|
|
87
|
+
|
|
88
|
+
assistant.log(`Directly cancelled suspended subscription for uid=${uid}`);
|
|
89
|
+
return assistant.respond({ success: true });
|
|
90
|
+
}
|
|
91
|
+
|
|
72
92
|
assistant.log(`Failed to cancel subscription via ${processor}: ${e.message}`);
|
|
73
93
|
return assistant.respond(`Failed to cancel subscription: ${e.message}`, { code: 500, sentry: true });
|
|
74
94
|
}
|