@nebulr-group/bridge-cli 0.5.0 → 0.5.1-beta.0
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/README.md +11 -1
- package/dist/commands/setup.command.d.ts +60 -0
- package/dist/commands/setup.command.d.ts.map +1 -1
- package/dist/commands/setup.command.js +129 -28
- package/dist/commands/setup.command.js.map +1 -1
- package/dist/commands/stripe.command.d.ts +36 -0
- package/dist/commands/stripe.command.d.ts.map +1 -1
- package/dist/commands/stripe.command.js +70 -14
- package/dist/commands/stripe.command.js.map +1 -1
- package/dist/prompts/billing/master.md +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -319,10 +319,20 @@ bridge event list --type USER_CREATED --since 24h --limit 50
|
|
|
319
319
|
|
|
320
320
|
```bash
|
|
321
321
|
bridge setup sso --provider google --client-id <id> --client-secret <secret>
|
|
322
|
+
bridge setup sso --provider azure --client-id <id> --client-secret <secret> --tenant-id <entra-directory-id>
|
|
322
323
|
bridge setup payments --stripe-key sk_test_xxx
|
|
323
|
-
bridge setup communication --
|
|
324
|
+
bridge setup communication --from-address noreply@acme.com --from-name "Acme"
|
|
324
325
|
```
|
|
325
326
|
|
|
327
|
+
`setup sso` supports google, github, linkedin, facebook and azure (azure also
|
|
328
|
+
needs the Entra ID Directory (tenant) ID). SAML and OIDC are not supported by
|
|
329
|
+
the Bridge API; set them up in the Bridge admin UI. The command prints the
|
|
330
|
+
callback URL to register in the provider's console.
|
|
331
|
+
|
|
332
|
+
`setup communication` sets only the sender name and address. Bridge sends all
|
|
333
|
+
email through its own provider, so there is no provider or API key to
|
|
334
|
+
configure. A new sender address is verified by email before Bridge uses it.
|
|
335
|
+
|
|
326
336
|
### Info (context for AI agents)
|
|
327
337
|
|
|
328
338
|
```bash
|
|
@@ -1,3 +1,63 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
export declare const SSO_PROVIDERS: {
|
|
3
|
+
readonly google: {
|
|
4
|
+
readonly clientId: "googleClientId";
|
|
5
|
+
readonly clientSecret: "googleClientSecret";
|
|
6
|
+
readonly enable: "googleSsoEnabled";
|
|
7
|
+
};
|
|
8
|
+
readonly github: {
|
|
9
|
+
readonly clientId: "githubClientId";
|
|
10
|
+
readonly clientSecret: "githubClientSecret";
|
|
11
|
+
readonly enable: "githubSsoEnabled";
|
|
12
|
+
};
|
|
13
|
+
readonly linkedin: {
|
|
14
|
+
readonly clientId: "linkedinClientId";
|
|
15
|
+
readonly clientSecret: "linkedinClientSecret";
|
|
16
|
+
readonly enable: "linkedinSsoEnabled";
|
|
17
|
+
};
|
|
18
|
+
readonly facebook: {
|
|
19
|
+
readonly clientId: "facebookClientId";
|
|
20
|
+
readonly clientSecret: "facebookClientSecret";
|
|
21
|
+
readonly enable: "facebookSsoEnabled";
|
|
22
|
+
};
|
|
23
|
+
readonly azure: {
|
|
24
|
+
readonly clientId: "microsoftAzureADClientId";
|
|
25
|
+
readonly clientSecret: "microsoftAzureADClientSecret";
|
|
26
|
+
readonly tenantId: "microsoftAzureADTenantId";
|
|
27
|
+
readonly enable: "azureAdSsoEnabled";
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export type SupportedSsoProvider = keyof typeof SSO_PROVIDERS;
|
|
31
|
+
/** Carries a machine-readable `code`, which `outputError` reports. */
|
|
32
|
+
export declare class SetupError extends Error {
|
|
33
|
+
readonly code: string;
|
|
34
|
+
constructor(message: string, code: string);
|
|
35
|
+
}
|
|
36
|
+
export interface SetupSsoOptions {
|
|
37
|
+
provider: string;
|
|
38
|
+
clientId?: string;
|
|
39
|
+
clientSecret?: string;
|
|
40
|
+
tenantId?: string;
|
|
41
|
+
metadataUrl?: string;
|
|
42
|
+
discoveryUrl?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function setupSso(opts: SetupSsoOptions): Promise<{
|
|
45
|
+
provider: "google" | "github" | "linkedin" | "facebook" | "azure";
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
callbackUrl: string;
|
|
48
|
+
app: import("@nebulr-group/bridge-auth-core").AppResponse;
|
|
49
|
+
}>;
|
|
50
|
+
export interface SetupCommunicationOptions {
|
|
51
|
+
fromAddress?: string;
|
|
52
|
+
fromName?: string;
|
|
53
|
+
provider?: string;
|
|
54
|
+
apiKey?: string;
|
|
55
|
+
}
|
|
56
|
+
export declare function setupCommunication(opts: SetupCommunicationOptions): Promise<{
|
|
57
|
+
configured: boolean;
|
|
58
|
+
emailSenderEmail: string | null;
|
|
59
|
+
emailSenderName: string | null;
|
|
60
|
+
app: import("@nebulr-group/bridge-auth-core").AppResponse;
|
|
61
|
+
}>;
|
|
2
62
|
export declare function registerSetupCommands(program: Command): void;
|
|
3
63
|
//# sourceMappingURL=setup.command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.command.d.ts","sourceRoot":"","sources":["../../src/commands/setup.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.command.d.ts","sourceRoot":"","sources":["../../src/commands/setup.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAuB5C,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWhB,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,aAAa,CAAC;AAS9D,sEAAsE;AACtE,qBAAa,UAAW,SAAQ,KAAK;IACN,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAtC,OAAO,EAAE,MAAM,EAAW,IAAI,EAAE,MAAM;CAInD;AASD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,eAAe;;;;;GA4CnD;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,yBAAyB;;;;;GA+BvE;AAKD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyC5D"}
|
|
@@ -1,25 +1,133 @@
|
|
|
1
|
+
import { Option } from 'commander';
|
|
1
2
|
import { getManagementClient } from '../config.js';
|
|
2
3
|
import { outputSuccess, outputError } from '../output.js';
|
|
4
|
+
/*
|
|
5
|
+
* TBP-663 — `setup sso` and `setup communication` talk to the app service
|
|
6
|
+
* (`app.updateCredentials` / `app.update` / `app.get`) directly instead of
|
|
7
|
+
* `workflows.setupSSO` / `workflows.setupCommunication`.
|
|
8
|
+
*
|
|
9
|
+
* Why not the workflows: in auth-core <= 0.7.0-beta.0 they send fields the
|
|
10
|
+
* API rejects under forbidNonWhitelisted — azure as `azureClientId`, saml/oidc
|
|
11
|
+
* as `samlMetadataUrl` / `oidcDiscoveryUrl`, email as `sendgridApiKey` — so
|
|
12
|
+
* both commands 400'd. auth-core main (#34) fixed them but changed their
|
|
13
|
+
* signatures and throws on the options this CLI used to pass. The app service
|
|
14
|
+
* has the same shape in both, so calling it with the server's own field names
|
|
15
|
+
* behaves identically whichever auth-core is installed.
|
|
16
|
+
*
|
|
17
|
+
* Field names are the server's: bridge-api
|
|
18
|
+
* microservices/account/nebulr-api/app/dto/update-credentials-request.dto.ts
|
|
19
|
+
* and update-app-request.dto.ts. Same table as auth-core's `setupSSO` and the
|
|
20
|
+
* MCP `setup_sso` tool. `setup-command.test.ts` pins them to those DTOs.
|
|
21
|
+
*/
|
|
22
|
+
export const SSO_PROVIDERS = {
|
|
23
|
+
google: { clientId: 'googleClientId', clientSecret: 'googleClientSecret', enable: 'googleSsoEnabled' },
|
|
24
|
+
github: { clientId: 'githubClientId', clientSecret: 'githubClientSecret', enable: 'githubSsoEnabled' },
|
|
25
|
+
linkedin: { clientId: 'linkedinClientId', clientSecret: 'linkedinClientSecret', enable: 'linkedinSsoEnabled' },
|
|
26
|
+
facebook: { clientId: 'facebookClientId', clientSecret: 'facebookClientSecret', enable: 'facebookSsoEnabled' },
|
|
27
|
+
azure: {
|
|
28
|
+
clientId: 'microsoftAzureADClientId',
|
|
29
|
+
clientSecret: 'microsoftAzureADClientSecret',
|
|
30
|
+
tenantId: 'microsoftAzureADTenantId',
|
|
31
|
+
enable: 'azureAdSsoEnabled',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
const SUPPORTED = Object.keys(SSO_PROVIDERS).join(', ');
|
|
35
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
36
|
+
/** Carries a machine-readable `code`, which `outputError` reports. */
|
|
37
|
+
export class SetupError extends Error {
|
|
38
|
+
code;
|
|
39
|
+
constructor(message, code) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.name = 'SetupError';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const NOT_SAVED = ' Nothing was saved.';
|
|
46
|
+
const invalid = (message) => new SetupError(message + NOT_SAVED, 'INVALID_ARGUMENT');
|
|
47
|
+
function isSupported(provider) {
|
|
48
|
+
return Object.prototype.hasOwnProperty.call(SSO_PROVIDERS, provider);
|
|
49
|
+
}
|
|
50
|
+
export async function setupSso(opts) {
|
|
51
|
+
const provider = String(opts.provider).toLowerCase();
|
|
52
|
+
if (!isSupported(provider)) {
|
|
53
|
+
throw new SetupError(provider === 'saml' || provider === 'oidc'
|
|
54
|
+
? `SSO provider "${provider}" is not supported by the Bridge API: it has no fields for SAML or OIDC ` +
|
|
55
|
+
`connections. Set them up in the Bridge admin UI. Supported here: ${SUPPORTED}.${NOT_SAVED}`
|
|
56
|
+
: `Unknown SSO provider "${provider}". Supported: ${SUPPORTED}.${NOT_SAVED}`, 'SSO_PROVIDER_NOT_SUPPORTED');
|
|
57
|
+
}
|
|
58
|
+
for (const [flag, value] of [['--metadata-url', opts.metadataUrl], ['--discovery-url', opts.discoveryUrl]]) {
|
|
59
|
+
if (value !== undefined) {
|
|
60
|
+
throw new SetupError(`${flag} was removed: the Bridge API has no field for it (SAML and OIDC are set up in the Bridge admin UI).${NOT_SAVED}`, 'SSO_PROVIDER_NOT_SUPPORTED');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!opts.clientId || !opts.clientSecret) {
|
|
64
|
+
throw invalid('--client-id and --client-secret are required (from the provider console).');
|
|
65
|
+
}
|
|
66
|
+
const fields = SSO_PROVIDERS[provider];
|
|
67
|
+
const credentials = {
|
|
68
|
+
[fields.clientId]: opts.clientId,
|
|
69
|
+
[fields.clientSecret]: opts.clientSecret,
|
|
70
|
+
};
|
|
71
|
+
if ('tenantId' in fields) {
|
|
72
|
+
if (!opts.tenantId) {
|
|
73
|
+
throw invalid('azure needs --tenant-id: the Directory (tenant) ID from the Entra ID app registration.');
|
|
74
|
+
}
|
|
75
|
+
credentials[fields.tenantId] = opts.tenantId;
|
|
76
|
+
}
|
|
77
|
+
else if (opts.tenantId !== undefined) {
|
|
78
|
+
throw invalid(`--tenant-id applies to azure only, not "${provider}".`);
|
|
79
|
+
}
|
|
80
|
+
const mgmt = getManagementClient();
|
|
81
|
+
await mgmt.app.updateCredentials(credentials);
|
|
82
|
+
const enable = { [fields.enable]: true };
|
|
83
|
+
await mgmt.app.update(enable);
|
|
84
|
+
const app = await mgmt.app.get();
|
|
85
|
+
return { provider, enabled: true, callbackUrl: app.defaultCallbackUri, app };
|
|
86
|
+
}
|
|
87
|
+
export async function setupCommunication(opts) {
|
|
88
|
+
if (opts.provider !== undefined || opts.apiKey !== undefined) {
|
|
89
|
+
throw new SetupError('--provider and --api-key were removed: Bridge sends all email through its own provider, so there is ' +
|
|
90
|
+
'no provider or API key to configure (the Bridge API has never accepted one). ' +
|
|
91
|
+
`Pass only --from-address and/or --from-name.${NOT_SAVED}`, 'EMAIL_PROVIDER_NOT_SUPPORTED');
|
|
92
|
+
}
|
|
93
|
+
const { fromAddress, fromName } = opts;
|
|
94
|
+
if (fromAddress === undefined && fromName === undefined) {
|
|
95
|
+
throw invalid('Nothing to set: pass --from-address, --from-name, or both.');
|
|
96
|
+
}
|
|
97
|
+
if (fromAddress !== undefined && !EMAIL.test(fromAddress)) {
|
|
98
|
+
throw invalid('--from-address must be an email address, e.g. "no-reply@example.com".');
|
|
99
|
+
}
|
|
100
|
+
if (fromName !== undefined && fromName.trim() === '') {
|
|
101
|
+
throw invalid('--from-name must not be empty.');
|
|
102
|
+
}
|
|
103
|
+
const update = {};
|
|
104
|
+
if (fromAddress !== undefined)
|
|
105
|
+
update.emailSenderEmail = fromAddress;
|
|
106
|
+
if (fromName !== undefined)
|
|
107
|
+
update.emailSenderName = fromName;
|
|
108
|
+
const app = await getManagementClient().app.update(update);
|
|
109
|
+
return {
|
|
110
|
+
configured: true,
|
|
111
|
+
emailSenderEmail: app.emailSenderEmail ?? null,
|
|
112
|
+
emailSenderName: app.emailSenderName ?? null,
|
|
113
|
+
app,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** Accepted only to refuse it with an explanation instead of commander's bare "unknown option". */
|
|
117
|
+
const removed = (flags, description) => new Option(flags, description).hideHelp();
|
|
3
118
|
export function registerSetupCommands(program) {
|
|
4
119
|
const setup = program.command('setup').description('Run setup workflows (multi-step operations)');
|
|
5
120
|
setup.command('sso')
|
|
6
|
-
.description('Enable an SSO provider and get callback URL')
|
|
7
|
-
.requiredOption('--provider <provider>',
|
|
8
|
-
.
|
|
9
|
-
.
|
|
10
|
-
.option('--
|
|
11
|
-
.
|
|
121
|
+
.description('Enable an SSO login provider and get the callback URL to register with it')
|
|
122
|
+
.requiredOption('--provider <provider>', `SSO provider: ${SUPPORTED} (SAML/OIDC are set up in the Bridge admin UI)`)
|
|
123
|
+
.requiredOption('--client-id <id>', 'OAuth client ID from the provider console')
|
|
124
|
+
.requiredOption('--client-secret <secret>', 'OAuth client secret from the provider console')
|
|
125
|
+
.option('--tenant-id <id>', 'azure only, required: Entra ID Directory (tenant) ID (not a Bridge tenant)')
|
|
126
|
+
.addOption(removed('--metadata-url <url>', 'removed: SAML is not supported by the Bridge API'))
|
|
127
|
+
.addOption(removed('--discovery-url <url>', 'removed: OIDC is not supported by the Bridge API'))
|
|
12
128
|
.action(async (opts) => {
|
|
13
129
|
try {
|
|
14
|
-
outputSuccess(await
|
|
15
|
-
provider: opts.provider,
|
|
16
|
-
config: {
|
|
17
|
-
clientId: opts.clientId,
|
|
18
|
-
clientSecret: opts.clientSecret,
|
|
19
|
-
metadataUrl: opts.metadataUrl,
|
|
20
|
-
discoveryUrl: opts.discoveryUrl,
|
|
21
|
-
},
|
|
22
|
-
}));
|
|
130
|
+
outputSuccess(await setupSso(opts));
|
|
23
131
|
}
|
|
24
132
|
catch (err) {
|
|
25
133
|
outputError(err);
|
|
@@ -41,21 +149,14 @@ export function registerSetupCommands(program) {
|
|
|
41
149
|
}
|
|
42
150
|
});
|
|
43
151
|
setup.command('communication')
|
|
44
|
-
.description('
|
|
45
|
-
.
|
|
46
|
-
.
|
|
47
|
-
.
|
|
48
|
-
.
|
|
152
|
+
.description("Set the sender name and address of your app's emails (sent through Bridge's own email provider)")
|
|
153
|
+
.option('--from-address <email>', 'Sender email address. A new address is verified by email before Bridge uses it')
|
|
154
|
+
.option('--from-name <name>', 'Sender display name')
|
|
155
|
+
.addOption(removed('--provider <provider>', 'removed: email goes through Bridge\'s own provider'))
|
|
156
|
+
.addOption(removed('--api-key <key>', 'removed: email goes through Bridge\'s own provider'))
|
|
49
157
|
.action(async (opts) => {
|
|
50
158
|
try {
|
|
51
|
-
outputSuccess(await
|
|
52
|
-
provider: opts.provider,
|
|
53
|
-
config: {
|
|
54
|
-
apiKey: opts.apiKey,
|
|
55
|
-
fromAddress: opts.fromAddress,
|
|
56
|
-
fromName: opts.fromName,
|
|
57
|
-
},
|
|
58
|
-
}));
|
|
159
|
+
outputSuccess(await setupCommunication(opts));
|
|
59
160
|
}
|
|
60
161
|
catch (err) {
|
|
61
162
|
outputError(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.command.js","sourceRoot":"","sources":["../../src/commands/setup.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup.command.js","sourceRoot":"","sources":["../../src/commands/setup.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,39 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
export interface ServerCredentialsState {
|
|
3
|
+
stripeCredentialsAdded: boolean;
|
|
4
|
+
/** TBP-658. Optional: older account APIs do not send it. */
|
|
5
|
+
stripeWebhookConfigured?: boolean;
|
|
6
|
+
azureMarketplaceCredentialsAdded: boolean;
|
|
7
|
+
azureAdSsoCredentialsAdded: boolean;
|
|
8
|
+
linkedinSsoCredentialsAdded: boolean;
|
|
9
|
+
googleSsoCredentialsAdded: boolean;
|
|
10
|
+
appleSsoCredentialsAdded: boolean;
|
|
11
|
+
githubSsoCredentialsAdded: boolean;
|
|
12
|
+
facebookSsoCredentialsAdded: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Webhook health fields on the app response (TBP-658). All optional: older APIs omit them. */
|
|
15
|
+
export interface ServerAppStripeState {
|
|
16
|
+
stripeSetupStatus?: 'pending' | 'completed' | 'failed' | null;
|
|
17
|
+
stripeSetupError?: string | null;
|
|
18
|
+
stripeLastWebhookAt?: string | null;
|
|
19
|
+
stripeLastWebhookRejectedAt?: string | null;
|
|
20
|
+
stripeLastWebhookRejectionReason?: string | null;
|
|
21
|
+
}
|
|
22
|
+
/** `true` only when the server confirms stored Stripe keys; never undefined. */
|
|
23
|
+
export declare function stripeConnectedFrom(state: unknown): boolean;
|
|
24
|
+
/** `null` when the API does not report it (older account API), so absence is not read as broken. */
|
|
25
|
+
export declare function stripeWebhookConfiguredFrom(state: unknown): boolean | null;
|
|
26
|
+
export interface StripeStatus {
|
|
27
|
+
connected: boolean;
|
|
28
|
+
webhookConfigured: boolean | null;
|
|
29
|
+
webhookHealthy: boolean | null;
|
|
30
|
+
lastWebhookAt: string | null;
|
|
31
|
+
lastWebhookRejectedAt: string | null;
|
|
32
|
+
lastWebhookRejectionReason: string | null;
|
|
33
|
+
setupStatus: string | null;
|
|
34
|
+
setupError: string | null;
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function stripeStatusFrom(state: unknown, app: unknown): StripeStatus;
|
|
2
38
|
export declare function registerStripeCommands(program: Command): void;
|
|
3
39
|
//# sourceMappingURL=stripe.command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stripe.command.d.ts","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"stripe.command.d.ts","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,MAAM,WAAW,sBAAsB;IACrC,sBAAsB,EAAE,OAAO,CAAC;IAChC,4DAA4D;IAC5D,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,gCAAgC,EAAE,OAAO,CAAC;IAC1C,0BAA0B,EAAE,OAAO,CAAC;IACpC,2BAA2B,EAAE,OAAO,CAAC;IACrC,yBAAyB,EAAE,OAAO,CAAC;IACnC,wBAAwB,EAAE,OAAO,CAAC;IAClC,yBAAyB,EAAE,OAAO,CAAC;IACnC,2BAA2B,EAAE,OAAO,CAAC;CACtC;AAED,+FAA+F;AAC/F,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC9D,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,2BAA2B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,gCAAgC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClD;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE3D;AAED,oGAAoG;AACpG,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAG1E;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB;AAyBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,YAAY,CAuB3E;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqC7D"}
|
|
@@ -1,18 +1,71 @@
|
|
|
1
1
|
import { getManagementClient } from '../config.js';
|
|
2
2
|
import { outputSuccess, outputError } from '../output.js';
|
|
3
|
+
/** `true` only when the server confirms stored Stripe keys; never undefined. */
|
|
4
|
+
export function stripeConnectedFrom(state) {
|
|
5
|
+
return state?.stripeCredentialsAdded === true;
|
|
6
|
+
}
|
|
7
|
+
/** `null` when the API does not report it (older account API), so absence is not read as broken. */
|
|
8
|
+
export function stripeWebhookConfiguredFrom(state) {
|
|
9
|
+
const value = state?.stripeWebhookConfigured;
|
|
10
|
+
return typeof value === 'boolean' ? value : null;
|
|
11
|
+
}
|
|
12
|
+
const CONNECT_HINT = 'run: bridge stripe connect --secret-key sk_... --publishable-key pk_...';
|
|
13
|
+
const REPAIR_HINT = 'Checkout still works, but subscription changes, cancellations and payment failures are not ' +
|
|
14
|
+
'reaching Bridge. Repair: re-run `bridge setup payments` with the same keys, or use "Repair ' +
|
|
15
|
+
'webhook" in the Bridge admin payment settings.';
|
|
16
|
+
/*
|
|
17
|
+
* Mirrors bridge-api's `AppService._stripeWebhookUnhealthy` plus the two
|
|
18
|
+
* explicit failure signals. `null` = the API gave us nothing to judge by.
|
|
19
|
+
*/
|
|
20
|
+
function webhookHealthy(configured, app) {
|
|
21
|
+
if (configured === false || app.stripeSetupStatus === 'failed')
|
|
22
|
+
return false;
|
|
23
|
+
const rejectedAt = app.stripeLastWebhookRejectedAt;
|
|
24
|
+
if (rejectedAt) {
|
|
25
|
+
const lastAt = app.stripeLastWebhookAt;
|
|
26
|
+
if (!lastAt || new Date(rejectedAt).getTime() > new Date(lastAt).getTime())
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return configured;
|
|
30
|
+
}
|
|
31
|
+
export function stripeStatusFrom(state, app) {
|
|
32
|
+
const a = (app ?? {});
|
|
33
|
+
const connected = stripeConnectedFrom(state);
|
|
34
|
+
const webhookConfigured = stripeWebhookConfiguredFrom(state);
|
|
35
|
+
const healthy = connected ? webhookHealthy(webhookConfigured, a) : null;
|
|
36
|
+
let message;
|
|
37
|
+
if (!connected)
|
|
38
|
+
message = `Stripe is not connected — ${CONNECT_HINT}`;
|
|
39
|
+
else if (healthy === false)
|
|
40
|
+
message = `Stripe is connected, but its webhook is not working. ${REPAIR_HINT}`;
|
|
41
|
+
else if (healthy === null)
|
|
42
|
+
message = 'Stripe is connected (webhook health not reported by this API)';
|
|
43
|
+
else
|
|
44
|
+
message = 'Stripe is connected and its webhook is configured';
|
|
45
|
+
return {
|
|
46
|
+
connected,
|
|
47
|
+
webhookConfigured,
|
|
48
|
+
webhookHealthy: healthy,
|
|
49
|
+
lastWebhookAt: a.stripeLastWebhookAt ?? null,
|
|
50
|
+
lastWebhookRejectedAt: a.stripeLastWebhookRejectedAt ?? null,
|
|
51
|
+
lastWebhookRejectionReason: a.stripeLastWebhookRejectionReason ?? null,
|
|
52
|
+
setupStatus: a.stripeSetupStatus ?? null,
|
|
53
|
+
setupError: a.stripeSetupError ?? null,
|
|
54
|
+
message,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
3
57
|
export function registerStripeCommands(program) {
|
|
4
58
|
const stripe = program.command('stripe').description('Manage Stripe integration');
|
|
5
59
|
stripe.command('status')
|
|
6
|
-
.description('Show whether Stripe credentials are configured')
|
|
60
|
+
.description('Show whether Stripe credentials are configured and whether its webhook works')
|
|
7
61
|
.action(async () => {
|
|
8
62
|
try {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
});
|
|
63
|
+
const client = getManagementClient();
|
|
64
|
+
const [state, app] = await Promise.all([
|
|
65
|
+
client.app.getCredentialsState(),
|
|
66
|
+
client.app.get(),
|
|
67
|
+
]);
|
|
68
|
+
outputSuccess(stripeStatusFrom(state, app));
|
|
16
69
|
}
|
|
17
70
|
catch (err) {
|
|
18
71
|
outputError(err);
|
|
@@ -28,12 +81,15 @@ export function registerStripeCommands(program) {
|
|
|
28
81
|
stripeSecretKey: opts.secretKey,
|
|
29
82
|
stripePublicKey: opts.publishableKey,
|
|
30
83
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
84
|
+
const connected = stripeConnectedFrom(state);
|
|
85
|
+
const webhookConfigured = stripeWebhookConfiguredFrom(state);
|
|
86
|
+
let message = connected
|
|
87
|
+
? 'Stripe connected successfully'
|
|
88
|
+
: 'Credentials saved but Stripe did not confirm — check your keys and try again';
|
|
89
|
+
if (connected && webhookConfigured === false) {
|
|
90
|
+
message += '. Warning: the Stripe webhook is not configured — run `bridge stripe status` for details';
|
|
91
|
+
}
|
|
92
|
+
outputSuccess({ connected, webhookConfigured, message });
|
|
37
93
|
}
|
|
38
94
|
catch (err) {
|
|
39
95
|
outputError(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stripe.command.js","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"stripe.command.js","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -127,6 +127,9 @@ If any plan will have a price, connect Stripe **before** setting prices (so each
|
|
|
127
127
|
bridge stripe status
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
If it reports `webhookHealthy: false`, checkout still works but subscription changes, cancellations
|
|
131
|
+
and payment failures will not sync. Tell the developer and follow the repair hint in `message`.
|
|
132
|
+
|
|
130
133
|
If not connected, ask the developer for their Stripe keys (from `dashboard.stripe.com/apikeys`):
|
|
131
134
|
|
|
132
135
|
```bash
|