@xuda.io/account_module 1.2.2271 → 1.2.2273
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/index.mjs +14 -7
- package/index.mjs.premerge.bak +4725 -0
- package/package.json +1 -1
- package/scripts/run_backfill.mjs +1 -5
- package/scripts/run_backfill.mjs.premerge.bak +51 -0
- package/scripts/run_migrate_account.mjs +1 -5
- package/scripts/run_migrate_account.mjs.premerge.bak +54 -0
- package/scripts/run_migrate_all.mjs +1 -5
- package/scripts/run_migrate_all.mjs.premerge.bak +109 -0
package/package.json
CHANGED
package/scripts/run_backfill.mjs
CHANGED
|
@@ -15,11 +15,7 @@
|
|
|
15
15
|
import path from 'path';
|
|
16
16
|
|
|
17
17
|
if (!global._conf) {
|
|
18
|
-
global._conf = (
|
|
19
|
-
await import(path.join(process.env.XUDA_HOME, process.env.XUDA_CONFIG), {
|
|
20
|
-
with: { type: 'json' },
|
|
21
|
-
})
|
|
22
|
-
).default;
|
|
18
|
+
global._conf = (await import(path.join(process.env.XUDA_HOME, "common", "load_conf.mjs"))).loadConf();
|
|
23
19
|
}
|
|
24
20
|
|
|
25
21
|
const argv = process.argv.slice(2);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// One-shot backfill runner.
|
|
2
|
+
//
|
|
3
|
+
// Usage (from /var/xuda on dev/prod, with XUDA_HOME and XUDA_CONFIG set
|
|
4
|
+
// the same way the cpi services have them):
|
|
5
|
+
//
|
|
6
|
+
// node cpi/account_module/scripts/run_backfill.mjs # full backfill
|
|
7
|
+
// node cpi/account_module/scripts/run_backfill.mjs --dry-run # compute, don't save
|
|
8
|
+
// node cpi/account_module/scripts/run_backfill.mjs --limit=5 # first 5 docs only
|
|
9
|
+
// node cpi/account_module/scripts/run_backfill.mjs --app-id=vps-abc123 # one doc
|
|
10
|
+
//
|
|
11
|
+
// Idempotent — safe to re-run if the first pass errored partway.
|
|
12
|
+
// Each doc with a populated app_cost.last_billed_ts is silently
|
|
13
|
+
// skipped on subsequent runs.
|
|
14
|
+
|
|
15
|
+
import path from 'path';
|
|
16
|
+
|
|
17
|
+
if (!global._conf) {
|
|
18
|
+
global._conf = (
|
|
19
|
+
await import(path.join(process.env.XUDA_HOME, process.env.XUDA_CONFIG), {
|
|
20
|
+
with: { type: 'json' },
|
|
21
|
+
})
|
|
22
|
+
).default;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const argv = process.argv.slice(2);
|
|
26
|
+
const opts = {};
|
|
27
|
+
for (const a of argv) {
|
|
28
|
+
if (a === '--dry-run') opts.dry_run = true;
|
|
29
|
+
else if (a.startsWith('--limit=')) opts.limit = parseInt(a.split('=')[1], 10);
|
|
30
|
+
else if (a.startsWith('--app-id=')) opts.app_id = a.split('=')[1];
|
|
31
|
+
else console.warn(`Unknown arg: ${a}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const module_path = path.join(process.env.XUDA_HOME, 'cpi') + (!_conf.is_debug ? '/node_modules/@xuda.io' : '');
|
|
35
|
+
const { backfill_app_costs } = await import(`${module_path}/account_module/index.mjs`);
|
|
36
|
+
|
|
37
|
+
console.log('Running backfill with opts:', opts);
|
|
38
|
+
const summary = await backfill_app_costs(opts);
|
|
39
|
+
|
|
40
|
+
const { per_doc, ...headline } = summary;
|
|
41
|
+
console.log('\n=== Backfill summary ===');
|
|
42
|
+
console.log(JSON.stringify(headline, null, 2));
|
|
43
|
+
|
|
44
|
+
if (per_doc?.length) {
|
|
45
|
+
const fs = await import('fs');
|
|
46
|
+
const out = path.join(process.env.XUDA_HOME || process.cwd(), `backfill_app_cost_log_${Date.now()}.json`);
|
|
47
|
+
fs.writeFileSync(out, JSON.stringify(per_doc, null, 2));
|
|
48
|
+
console.log(`\nPer-doc log written to: ${out}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
process.exit(summary.errors?.length ? 1 : 0);
|
|
@@ -13,11 +13,7 @@
|
|
|
13
13
|
import path from 'path';
|
|
14
14
|
|
|
15
15
|
if (!global._conf) {
|
|
16
|
-
global._conf = (
|
|
17
|
-
await import(path.join(process.env.XUDA_HOME, process.env.XUDA_CONFIG), {
|
|
18
|
-
with: { type: 'json' },
|
|
19
|
-
})
|
|
20
|
-
).default;
|
|
16
|
+
global._conf = (await import(path.join(process.env.XUDA_HOME, "common", "load_conf.mjs"))).loadConf();
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
const argv = process.argv.slice(2);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Migrate a single account from legacy 3-subscription model to the
|
|
2
|
+
// consolidated 1-subscription-with-3-items model.
|
|
3
|
+
//
|
|
4
|
+
// Usage (from /var/xuda on dev/prod, with XUDA_HOME and XUDA_CONFIG set):
|
|
5
|
+
//
|
|
6
|
+
// node cpi/account_module/scripts/run_migrate_account.mjs --uid=<UID>
|
|
7
|
+
// node cpi/account_module/scripts/run_migrate_account.mjs --uid=<UID> --dry-run
|
|
8
|
+
//
|
|
9
|
+
// Idempotent — running on an already-consolidated account is a no-op.
|
|
10
|
+
// Use --dry-run first to preview which subs would be cancelled and
|
|
11
|
+
// when the new sub would start billing.
|
|
12
|
+
|
|
13
|
+
import path from 'path';
|
|
14
|
+
|
|
15
|
+
if (!global._conf) {
|
|
16
|
+
global._conf = (
|
|
17
|
+
await import(path.join(process.env.XUDA_HOME, process.env.XUDA_CONFIG), {
|
|
18
|
+
with: { type: 'json' },
|
|
19
|
+
})
|
|
20
|
+
).default;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const argv = process.argv.slice(2);
|
|
24
|
+
const opts = {};
|
|
25
|
+
for (const a of argv) {
|
|
26
|
+
if (a === '--dry-run') opts.dry_run = true;
|
|
27
|
+
else if (a.startsWith('--uid=')) opts.uid = a.split('=')[1];
|
|
28
|
+
else console.warn(`Unknown arg: ${a}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!opts.uid) {
|
|
32
|
+
console.error('--uid=<UID> is required');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const module_path = path.join(process.env.XUDA_HOME, 'cpi') + (!_conf.is_debug ? '/node_modules/@xuda.io' : '');
|
|
37
|
+
const { migrate_account_to_consolidated } = await import(`${module_path}/stripe_module/index.mjs`);
|
|
38
|
+
|
|
39
|
+
console.log(`Migrating account ${opts.uid}${opts.dry_run ? ' (DRY RUN)' : ''}...`);
|
|
40
|
+
const result = await migrate_account_to_consolidated(opts);
|
|
41
|
+
console.log('\n=== Result ===');
|
|
42
|
+
console.log(JSON.stringify(result, null, 2));
|
|
43
|
+
|
|
44
|
+
if (result.code === 1) {
|
|
45
|
+
if (opts.dry_run) {
|
|
46
|
+
console.log('\nDry run complete. Re-run without --dry-run to actually migrate.');
|
|
47
|
+
} else {
|
|
48
|
+
console.log(`\n✓ Account ${opts.uid} migrated. New sub will start billing at unix ts ${result.data?.starts_billing_at_unix_ts}.`);
|
|
49
|
+
}
|
|
50
|
+
process.exit(0);
|
|
51
|
+
} else {
|
|
52
|
+
console.error(`\n✗ Migration failed: ${result.data}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
@@ -19,11 +19,7 @@
|
|
|
19
19
|
import path from 'path';
|
|
20
20
|
|
|
21
21
|
if (!global._conf) {
|
|
22
|
-
global._conf = (
|
|
23
|
-
await import(path.join(process.env.XUDA_HOME, process.env.XUDA_CONFIG), {
|
|
24
|
-
with: { type: 'json' },
|
|
25
|
-
})
|
|
26
|
-
).default;
|
|
22
|
+
global._conf = (await import(path.join(process.env.XUDA_HOME, "common", "load_conf.mjs"))).loadConf();
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
const argv = process.argv.slice(2);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Bulk migrate EVERY account from the legacy 3-subscription model to
|
|
2
|
+
// the consolidated 1-subscription-with-3-items model.
|
|
3
|
+
//
|
|
4
|
+
// Idempotent — accounts already on the consolidated model return
|
|
5
|
+
// "already consolidated" and are skipped silently. Per-account errors
|
|
6
|
+
// don't stop the loop; the summary at the end lists what failed.
|
|
7
|
+
//
|
|
8
|
+
// Usage (from /var/xuda on dev/prod, with XUDA_HOME and XUDA_CONFIG set):
|
|
9
|
+
//
|
|
10
|
+
// node cpi/account_module/scripts/run_migrate_all.mjs --dry-run
|
|
11
|
+
// node cpi/account_module/scripts/run_migrate_all.mjs
|
|
12
|
+
// node cpi/account_module/scripts/run_migrate_all.mjs --limit=5 # canary first N
|
|
13
|
+
//
|
|
14
|
+
// On dev this is fine to run at any time. On prod, prefer the
|
|
15
|
+
// per-account script (run_migrate_account.mjs) or the webhook-driven
|
|
16
|
+
// gradual migration so each customer migrates at their natural
|
|
17
|
+
// cycle boundary.
|
|
18
|
+
|
|
19
|
+
import path from 'path';
|
|
20
|
+
|
|
21
|
+
if (!global._conf) {
|
|
22
|
+
global._conf = (
|
|
23
|
+
await import(path.join(process.env.XUDA_HOME, process.env.XUDA_CONFIG), {
|
|
24
|
+
with: { type: 'json' },
|
|
25
|
+
})
|
|
26
|
+
).default;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const argv = process.argv.slice(2);
|
|
30
|
+
const opts = { dry_run: false, limit: null };
|
|
31
|
+
for (const a of argv) {
|
|
32
|
+
if (a === '--dry-run') opts.dry_run = true;
|
|
33
|
+
else if (a.startsWith('--limit=')) opts.limit = parseInt(a.split('=')[1], 10);
|
|
34
|
+
else console.warn(`Unknown arg: ${a}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const module_path = path.join(process.env.XUDA_HOME, 'cpi') + (!_conf.is_debug ? '/node_modules/@xuda.io' : '');
|
|
38
|
+
const db_module = await import(`${module_path}/db_module/index.mjs`);
|
|
39
|
+
const { migrate_account_to_consolidated } = await import(`${module_path}/stripe_module/index.mjs`);
|
|
40
|
+
|
|
41
|
+
console.log(`Bulk migration ${opts.dry_run ? '(DRY RUN)' : ''}${opts.limit ? ` limit=${opts.limit}` : ''}...`);
|
|
42
|
+
|
|
43
|
+
const accounts_ret = await db_module.get_couch_view_raw('xuda_accounts', 'all_accounts', {});
|
|
44
|
+
const accounts = (accounts_ret?.rows || []).map((r) => r.value).filter((a) => a?._id);
|
|
45
|
+
|
|
46
|
+
console.log(`Found ${accounts.length} accounts.\n`);
|
|
47
|
+
|
|
48
|
+
const summary = {
|
|
49
|
+
scanned: 0,
|
|
50
|
+
migrated: 0,
|
|
51
|
+
already: 0,
|
|
52
|
+
skipped_no_stripe: 0,
|
|
53
|
+
failed: 0,
|
|
54
|
+
errors: [],
|
|
55
|
+
per_account: [],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const todo = opts.limit ? accounts.slice(0, opts.limit) : accounts;
|
|
59
|
+
|
|
60
|
+
for (const acct of todo) {
|
|
61
|
+
summary.scanned++;
|
|
62
|
+
|
|
63
|
+
// Skip accounts that don't have any Stripe presence yet (e.g.
|
|
64
|
+
// half-created test accounts) — there's nothing to migrate.
|
|
65
|
+
if (!acct.stripe_customer_id) {
|
|
66
|
+
summary.skipped_no_stripe++;
|
|
67
|
+
summary.per_account.push({ uid: acct._id, action: 'skipped', reason: 'no stripe_customer_id' });
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const result = await migrate_account_to_consolidated({ uid: acct._id, dry_run: opts.dry_run });
|
|
73
|
+
if (result.code === 1) {
|
|
74
|
+
const data = result.data || '';
|
|
75
|
+
const isAlready = typeof data === 'string' && (data.includes('already') || data.includes('reconciled'));
|
|
76
|
+
if (isAlready) {
|
|
77
|
+
summary.already++;
|
|
78
|
+
summary.per_account.push({ uid: acct._id, action: 'already', reason: data });
|
|
79
|
+
} else {
|
|
80
|
+
summary.migrated++;
|
|
81
|
+
summary.per_account.push({ uid: acct._id, action: opts.dry_run ? 'dry-run-ok' : 'migrated', reason: typeof data === 'object' ? `new_sub=${data.new_sub_id} starts=${data.starts_billing_at_unix_ts}` : data });
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
summary.failed++;
|
|
85
|
+
summary.errors.push({ uid: acct._id, reason: result.data });
|
|
86
|
+
summary.per_account.push({ uid: acct._id, action: 'failed', reason: result.data });
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
summary.failed++;
|
|
90
|
+
summary.errors.push({ uid: acct._id, reason: err.message });
|
|
91
|
+
summary.per_account.push({ uid: acct._id, action: 'errored', reason: err.message });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Tiny gap between accounts so we don't hammer Stripe with bursts.
|
|
95
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const { per_account, ...headline } = summary;
|
|
99
|
+
console.log('\n=== Bulk migration summary ===');
|
|
100
|
+
console.log(JSON.stringify(headline, null, 2));
|
|
101
|
+
|
|
102
|
+
if (per_account.length) {
|
|
103
|
+
const fs = await import('fs');
|
|
104
|
+
const out = path.join(process.env.XUDA_HOME || process.cwd(), `bulk_migrate_log_${Date.now()}.json`);
|
|
105
|
+
fs.writeFileSync(out, JSON.stringify(per_account, null, 2));
|
|
106
|
+
console.log(`\nPer-account log written to: ${out}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
process.exit(summary.failed ? 1 : 0);
|