@molecule/api-bonds-default-express 1.0.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/LICENSE +115 -0
- package/dist/billing.d.ts +54 -0
- package/dist/billing.d.ts.map +1 -0
- package/dist/billing.js +228 -0
- package/dist/browser-guard.d.ts +2 -0
- package/dist/browser-guard.d.ts.map +1 -0
- package/dist/browser-guard.js +18 -0
- package/dist/handlers.d.ts +91 -0
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +128 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +102 -0
- package/dist/middleware.d.ts +23 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +41 -0
- package/dist/migrate.d.ts +17 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +16 -0
- package/dist/resources.d.ts +18 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +18 -0
- package/dist/routes.d.ts +124 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +166 -0
- package/dist/schemas.d.ts +23 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +18 -0
- package/dist/setup.d.ts +132 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +507 -0
- package/package.json +377 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@molecule/api-bonds-default-express` — default API bond wirings and
|
|
3
|
+
* shared route/handler plumbing for Express-based apps.
|
|
4
|
+
*
|
|
5
|
+
* Two halves:
|
|
6
|
+
*
|
|
7
|
+
* 1. **`setup<Name>()` bond wirings** (40+): one function per default
|
|
8
|
+
* provider (`setupConfigEnv`, `setupDatabasePostgresql`,
|
|
9
|
+
* `setupJwtJsonwebtoken`, `setupEmailsMailgun`, `setupUploadsS3`,
|
|
10
|
+
* `setupRealtimeSocketio`, `setupAiAnthropic`, …) so per-app
|
|
11
|
+
* `api/src/bonds/<name>.ts` files are 1-line re-exports and
|
|
12
|
+
* `bonds/index.ts` just calls them in order.
|
|
13
|
+
* 2. **Shared Express plumbing**: `createBillingRouter` (the fleet's
|
|
14
|
+
* Stripe billing endpoints), the `mountDefaultUserAuthRoutes` /
|
|
15
|
+
* `mountDefaultDeviceRoutes` / other `mountDefault*Routes` helpers,
|
|
16
|
+
* handler guards (`requireAuth`, `requireUser`, `requireOwnership`,
|
|
17
|
+
* `getUserId`, `validationError`, `internalError`), zod param schemas,
|
|
18
|
+
* `trackAuthEvent`, and a `createMigrator` re-export.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* // api/src/bonds/index.ts — wire defaults at startup, then validate:
|
|
23
|
+
* import { validateBonds } from '@molecule/api-bond'
|
|
24
|
+
* import {
|
|
25
|
+
* setupConfigEnv,
|
|
26
|
+
* setupDatabasePostgresql,
|
|
27
|
+
* setupEmailsMailgun,
|
|
28
|
+
* setupJwtJsonwebtoken,
|
|
29
|
+
* setupSecretsEnv,
|
|
30
|
+
* } from '@molecule/api-bonds-default-express'
|
|
31
|
+
*
|
|
32
|
+
* async function setupBonds(): Promise<void> {
|
|
33
|
+
* setupConfigEnv()
|
|
34
|
+
* setupSecretsEnv()
|
|
35
|
+
* setupDatabasePostgresql()
|
|
36
|
+
* setupJwtJsonwebtoken()
|
|
37
|
+
* setupEmailsMailgun()
|
|
38
|
+
* validateBonds()
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // api/src/routes/billing.ts — the fleet-standard billing endpoints
|
|
45
|
+
* // (POST /checkout, POST /cancel, GET /status, GET /tiers), mounted by
|
|
46
|
+
* // the app router at /billing (the real file default-exports the router):
|
|
47
|
+
* import { createBillingRouter } from '@molecule/api-bonds-default-express'
|
|
48
|
+
*
|
|
49
|
+
* // Your app owns these (typically in api/src/tiers.ts):
|
|
50
|
+
* interface AppLimits { seats: number }
|
|
51
|
+
* const getPricingTiers = () => [] // your tiers, each with a stripePriceId + limits
|
|
52
|
+
* const appPlanKeys = { free: 'free', pro: 'pro' }
|
|
53
|
+
*
|
|
54
|
+
* const billingRouter = createBillingRouter<AppLimits>({
|
|
55
|
+
* getPricingTiers,
|
|
56
|
+
* planKeys: appPlanKeys,
|
|
57
|
+
* })
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @module
|
|
61
|
+
* @remarks
|
|
62
|
+
* - **Development falls back to zero-credential providers; production never
|
|
63
|
+
* does.** When `NODE_ENV !== 'production'` and a provider's required env
|
|
64
|
+
* is missing, the setup wires the capture/local sibling instead and logs
|
|
65
|
+
* the swap: mailgun→emails-capture (`MAILGUN_API_KEY`/`MAILGUN_DOMAIN`),
|
|
66
|
+
* uploads-s3→uploads-filesystem (`AWS_*`), search-meilisearch→
|
|
67
|
+
* search-postgres (`MEILISEARCH_URL`), web-push→push-capture
|
|
68
|
+
* (`VAPID_*`), geolocation-mapbox→nominatim (`MAPBOX_ACCESS_TOKEN`),
|
|
69
|
+
* cache-redis→cache-memory (`REDIS_URL`). In production the credentialed
|
|
70
|
+
* provider is wired regardless — missing env surfaces as loud,
|
|
71
|
+
* actionable 503s and boot-report entries, never a silent provider swap.
|
|
72
|
+
* So "emails don't arrive in dev" usually means they were CAPTURED (read
|
|
73
|
+
* them via the activity/capture tooling), not lost.
|
|
74
|
+
* - **Realtime setups (`setupRealtimeSocketio`, `setupRealtimeWs`,
|
|
75
|
+
* `setupRealtimeSse`) all defer-attach.** Each dynamic-imports its
|
|
76
|
+
* provider's `createProvider({ deferAttach: true })`, calls
|
|
77
|
+
* `setProvider()`, then `registerServerCreatedHook((server) =>
|
|
78
|
+
* provider.attachHttpServer?.(server))` from
|
|
79
|
+
* `@molecule/api-server-default-express` — so the realtime transport
|
|
80
|
+
* shares the API's HTTP server/port once it exists, instead of a
|
|
81
|
+
* standalone port a containerized sandbox / proxied deploy may not
|
|
82
|
+
* expose. Add new realtime bonds by mirroring this pattern exactly.
|
|
83
|
+
* - `createBillingRouter` registers the app's Stripe plan catalogue with
|
|
84
|
+
* `@molecule/api-resource-payment` at construction AND re-registers per
|
|
85
|
+
* checkout (price-id env vars may resolve after startup); webhook
|
|
86
|
+
* handling stays with `@molecule/api-resource-user`'s
|
|
87
|
+
* `handlePaymentNotification`. A paid price whose `planKeys` entry is
|
|
88
|
+
* missing is skipped WITH a warning — that plan could never be granted.
|
|
89
|
+
* - Only wire the setups whose packages your app actually installed —
|
|
90
|
+
* each one imports its provider package (several lazily via dynamic
|
|
91
|
+
* import), so calling a setup for an uninstalled bond fails at that
|
|
92
|
+
* import.
|
|
93
|
+
*/
|
|
94
|
+
export * from './billing.js';
|
|
95
|
+
export * from './browser-guard.js';
|
|
96
|
+
export * from './handlers.js';
|
|
97
|
+
export * from './middleware.js';
|
|
98
|
+
export * from './migrate.js';
|
|
99
|
+
export * from './resources.js';
|
|
100
|
+
export * from './routes.js';
|
|
101
|
+
export * from './schemas.js';
|
|
102
|
+
export * from './setup.js';
|
|
103
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4FG;AAEH,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA;AAClC,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@molecule/api-bonds-default-express` — default API bond wirings and
|
|
3
|
+
* shared route/handler plumbing for Express-based apps.
|
|
4
|
+
*
|
|
5
|
+
* Two halves:
|
|
6
|
+
*
|
|
7
|
+
* 1. **`setup<Name>()` bond wirings** (40+): one function per default
|
|
8
|
+
* provider (`setupConfigEnv`, `setupDatabasePostgresql`,
|
|
9
|
+
* `setupJwtJsonwebtoken`, `setupEmailsMailgun`, `setupUploadsS3`,
|
|
10
|
+
* `setupRealtimeSocketio`, `setupAiAnthropic`, …) so per-app
|
|
11
|
+
* `api/src/bonds/<name>.ts` files are 1-line re-exports and
|
|
12
|
+
* `bonds/index.ts` just calls them in order.
|
|
13
|
+
* 2. **Shared Express plumbing**: `createBillingRouter` (the fleet's
|
|
14
|
+
* Stripe billing endpoints), the `mountDefaultUserAuthRoutes` /
|
|
15
|
+
* `mountDefaultDeviceRoutes` / other `mountDefault*Routes` helpers,
|
|
16
|
+
* handler guards (`requireAuth`, `requireUser`, `requireOwnership`,
|
|
17
|
+
* `getUserId`, `validationError`, `internalError`), zod param schemas,
|
|
18
|
+
* `trackAuthEvent`, and a `createMigrator` re-export.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* // api/src/bonds/index.ts — wire defaults at startup, then validate:
|
|
23
|
+
* import { validateBonds } from '@molecule/api-bond'
|
|
24
|
+
* import {
|
|
25
|
+
* setupConfigEnv,
|
|
26
|
+
* setupDatabasePostgresql,
|
|
27
|
+
* setupEmailsMailgun,
|
|
28
|
+
* setupJwtJsonwebtoken,
|
|
29
|
+
* setupSecretsEnv,
|
|
30
|
+
* } from '@molecule/api-bonds-default-express'
|
|
31
|
+
*
|
|
32
|
+
* async function setupBonds(): Promise<void> {
|
|
33
|
+
* setupConfigEnv()
|
|
34
|
+
* setupSecretsEnv()
|
|
35
|
+
* setupDatabasePostgresql()
|
|
36
|
+
* setupJwtJsonwebtoken()
|
|
37
|
+
* setupEmailsMailgun()
|
|
38
|
+
* validateBonds()
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // api/src/routes/billing.ts — the fleet-standard billing endpoints
|
|
45
|
+
* // (POST /checkout, POST /cancel, GET /status, GET /tiers), mounted by
|
|
46
|
+
* // the app router at /billing (the real file default-exports the router):
|
|
47
|
+
* import { createBillingRouter } from '@molecule/api-bonds-default-express'
|
|
48
|
+
*
|
|
49
|
+
* // Your app owns these (typically in api/src/tiers.ts):
|
|
50
|
+
* interface AppLimits { seats: number }
|
|
51
|
+
* const getPricingTiers = () => [] // your tiers, each with a stripePriceId + limits
|
|
52
|
+
* const appPlanKeys = { free: 'free', pro: 'pro' }
|
|
53
|
+
*
|
|
54
|
+
* const billingRouter = createBillingRouter<AppLimits>({
|
|
55
|
+
* getPricingTiers,
|
|
56
|
+
* planKeys: appPlanKeys,
|
|
57
|
+
* })
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @module
|
|
61
|
+
* @remarks
|
|
62
|
+
* - **Development falls back to zero-credential providers; production never
|
|
63
|
+
* does.** When `NODE_ENV !== 'production'` and a provider's required env
|
|
64
|
+
* is missing, the setup wires the capture/local sibling instead and logs
|
|
65
|
+
* the swap: mailgun→emails-capture (`MAILGUN_API_KEY`/`MAILGUN_DOMAIN`),
|
|
66
|
+
* uploads-s3→uploads-filesystem (`AWS_*`), search-meilisearch→
|
|
67
|
+
* search-postgres (`MEILISEARCH_URL`), web-push→push-capture
|
|
68
|
+
* (`VAPID_*`), geolocation-mapbox→nominatim (`MAPBOX_ACCESS_TOKEN`),
|
|
69
|
+
* cache-redis→cache-memory (`REDIS_URL`). In production the credentialed
|
|
70
|
+
* provider is wired regardless — missing env surfaces as loud,
|
|
71
|
+
* actionable 503s and boot-report entries, never a silent provider swap.
|
|
72
|
+
* So "emails don't arrive in dev" usually means they were CAPTURED (read
|
|
73
|
+
* them via the activity/capture tooling), not lost.
|
|
74
|
+
* - **Realtime setups (`setupRealtimeSocketio`, `setupRealtimeWs`,
|
|
75
|
+
* `setupRealtimeSse`) all defer-attach.** Each dynamic-imports its
|
|
76
|
+
* provider's `createProvider({ deferAttach: true })`, calls
|
|
77
|
+
* `setProvider()`, then `registerServerCreatedHook((server) =>
|
|
78
|
+
* provider.attachHttpServer?.(server))` from
|
|
79
|
+
* `@molecule/api-server-default-express` — so the realtime transport
|
|
80
|
+
* shares the API's HTTP server/port once it exists, instead of a
|
|
81
|
+
* standalone port a containerized sandbox / proxied deploy may not
|
|
82
|
+
* expose. Add new realtime bonds by mirroring this pattern exactly.
|
|
83
|
+
* - `createBillingRouter` registers the app's Stripe plan catalogue with
|
|
84
|
+
* `@molecule/api-resource-payment` at construction AND re-registers per
|
|
85
|
+
* checkout (price-id env vars may resolve after startup); webhook
|
|
86
|
+
* handling stays with `@molecule/api-resource-user`'s
|
|
87
|
+
* `handlePaymentNotification`. A paid price whose `planKeys` entry is
|
|
88
|
+
* missing is skipped WITH a warning — that plan could never be granted.
|
|
89
|
+
* - Only wire the setups whose packages your app actually installed —
|
|
90
|
+
* each one imports its provider package (several lazily via dynamic
|
|
91
|
+
* import), so calling a setup for an uninstalled bond fails at that
|
|
92
|
+
* import.
|
|
93
|
+
*/
|
|
94
|
+
export * from './billing.js';
|
|
95
|
+
export * from './browser-guard.js';
|
|
96
|
+
export * from './handlers.js';
|
|
97
|
+
export * from './middleware.js';
|
|
98
|
+
export * from './migrate.js';
|
|
99
|
+
export * from './resources.js';
|
|
100
|
+
export * from './routes.js';
|
|
101
|
+
export * from './schemas.js';
|
|
102
|
+
export * from './setup.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Express middleware helpers used by the molecule fleet's
|
|
3
|
+
* `api/src/middleware/<name>.ts` files.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import type { RequestHandler } from 'express';
|
|
8
|
+
/**
|
|
9
|
+
* Emits an analytics event AND a log entry for an auth-related mutation
|
|
10
|
+
* (signup, login, password reset, plan change, etc.). Logs at info on
|
|
11
|
+
* success and warn on auth failure (4xx) so security signal is captured.
|
|
12
|
+
*
|
|
13
|
+
* Replaces the per-app `api/src/middleware/auth-analytics.ts` shipped
|
|
14
|
+
* by 10 fleet apps.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { trackAuthEvent } from '@molecule/api-bonds-default-express'
|
|
19
|
+
* router.post('/users/log-in', trackAuthEvent('user.login'), User.logIn)
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function trackAuthEvent(eventName: string): RequestHandler;
|
|
23
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAyB,cAAc,EAAY,MAAM,SAAS,CAAA;AAK9E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAqBhE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Express middleware helpers used by the molecule fleet's
|
|
3
|
+
* `api/src/middleware/<name>.ts` files.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { track } from '@molecule/api-analytics';
|
|
8
|
+
import { logger } from '@molecule/api-logger';
|
|
9
|
+
/**
|
|
10
|
+
* Emits an analytics event AND a log entry for an auth-related mutation
|
|
11
|
+
* (signup, login, password reset, plan change, etc.). Logs at info on
|
|
12
|
+
* success and warn on auth failure (4xx) so security signal is captured.
|
|
13
|
+
*
|
|
14
|
+
* Replaces the per-app `api/src/middleware/auth-analytics.ts` shipped
|
|
15
|
+
* by 10 fleet apps.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { trackAuthEvent } from '@molecule/api-bonds-default-express'
|
|
20
|
+
* router.post('/users/log-in', trackAuthEvent('user.login'), User.logIn)
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function trackAuthEvent(eventName) {
|
|
24
|
+
return (req, res, next) => {
|
|
25
|
+
res.on('finish', () => {
|
|
26
|
+
const id = typeof req.params.id === 'string' ? req.params.id : req.params.id?.[0];
|
|
27
|
+
const ok = res.statusCode >= 200 && res.statusCode < 400;
|
|
28
|
+
if (ok) {
|
|
29
|
+
void track({
|
|
30
|
+
name: eventName,
|
|
31
|
+
properties: { method: req.method, userId: id },
|
|
32
|
+
});
|
|
33
|
+
logger.info(`[auth] ${eventName} method=${req.method} userId=${id ?? '-'} status=${res.statusCode}`);
|
|
34
|
+
}
|
|
35
|
+
else if (res.statusCode >= 400 && res.statusCode < 500) {
|
|
36
|
+
logger.warn(`[auth] ${eventName} failed method=${req.method} userId=${id ?? '-'} status=${res.statusCode}`);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
next();
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres migration runner factory — re-exported from the PostgreSQL
|
|
3
|
+
* bond.
|
|
4
|
+
*
|
|
5
|
+
* The `createMigrator` implementation lives in
|
|
6
|
+
* `@molecule/api-database-postgresql` because it is Postgres-specific
|
|
7
|
+
* bootstrapping plumbing (`CREATE DATABASE`, the `pg_database` catalog
|
|
8
|
+
* probe, raw `pg.Client` connections). This bond-setup package must not
|
|
9
|
+
* import the concrete `pg` driver directly — it reaches Postgres only
|
|
10
|
+
* through the `@molecule/api-database-postgresql` bond it already
|
|
11
|
+
* peer-depends on. App `scripts/migrate.ts` files keep importing
|
|
12
|
+
* `createMigrator` from here unchanged.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
export { createMigrator } from '@molecule/api-database-postgresql';
|
|
17
|
+
//# sourceMappingURL=migrate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA"}
|
package/dist/migrate.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres migration runner factory — re-exported from the PostgreSQL
|
|
3
|
+
* bond.
|
|
4
|
+
*
|
|
5
|
+
* The `createMigrator` implementation lives in
|
|
6
|
+
* `@molecule/api-database-postgresql` because it is Postgres-specific
|
|
7
|
+
* bootstrapping plumbing (`CREATE DATABASE`, the `pg_database` catalog
|
|
8
|
+
* probe, raw `pg.Client` connections). This bond-setup package must not
|
|
9
|
+
* import the concrete `pg` driver directly — it reaches Postgres only
|
|
10
|
+
* through the `@molecule/api-database-postgresql` bond it already
|
|
11
|
+
* peer-depends on. App `scripts/migrate.ts` files keep importing
|
|
12
|
+
* `createMigrator` from here unchanged.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
export { createMigrator } from '@molecule/api-database-postgresql';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-wired default request handler maps for the user + device
|
|
3
|
+
* resources. Apps that don't need to customize their resource
|
|
4
|
+
* wiring can re-export these directly from
|
|
5
|
+
* `App/Resource/<Name>/index.ts`, eliminating the byte-identical
|
|
6
|
+
* `createRequestHandlerMap(createRequestHandler)` calls that every
|
|
7
|
+
* flagship app shipped.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { deviceService } from '@molecule/api-resource-device';
|
|
12
|
+
import { authorization } from '@molecule/api-resource-user';
|
|
13
|
+
/** Pre-wired request handler map for `@molecule/api-resource-user`. */
|
|
14
|
+
export declare const userRequestHandlerMap: import("@molecule/api-resource-user").UserRequestHandlerMap;
|
|
15
|
+
/** Pre-wired request handler map for `@molecule/api-resource-device`. */
|
|
16
|
+
export declare const deviceRequestHandlerMap: import("@molecule/api-resource-device").DeviceRequestHandlerMap;
|
|
17
|
+
export { deviceService, authorization as userAuthorization };
|
|
18
|
+
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAEL,aAAa,EACd,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,aAAa,EAEd,MAAM,6BAA6B,CAAA;AAEpC,uEAAuE;AACvE,eAAO,MAAM,qBAAqB,6DAAoD,CAAA;AAEtF,yEAAyE;AACzE,eAAO,MAAM,uBAAuB,iEAAsD,CAAA;AAE1F,OAAO,EAAE,aAAa,EAAE,aAAa,IAAI,iBAAiB,EAAE,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-wired default request handler maps for the user + device
|
|
3
|
+
* resources. Apps that don't need to customize their resource
|
|
4
|
+
* wiring can re-export these directly from
|
|
5
|
+
* `App/Resource/<Name>/index.ts`, eliminating the byte-identical
|
|
6
|
+
* `createRequestHandlerMap(createRequestHandler)` calls that every
|
|
7
|
+
* flagship app shipped.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { createRequestHandler } from '@molecule/api-resource';
|
|
12
|
+
import { createRequestHandlerMap as createDeviceRequestHandlerMap, deviceService, } from '@molecule/api-resource-device';
|
|
13
|
+
import { authorization, createRequestHandlerMap as createUserRequestHandlerMap, } from '@molecule/api-resource-user';
|
|
14
|
+
/** Pre-wired request handler map for `@molecule/api-resource-user`. */
|
|
15
|
+
export const userRequestHandlerMap = createUserRequestHandlerMap(createRequestHandler);
|
|
16
|
+
/** Pre-wired request handler map for `@molecule/api-resource-device`. */
|
|
17
|
+
export const deviceRequestHandlerMap = createDeviceRequestHandlerMap(createRequestHandler);
|
|
18
|
+
export { deviceService, authorization as userAuthorization };
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { Router } from 'express';
|
|
2
|
+
import type { DeviceRequestHandlerMap } from '@molecule/api-resource-device';
|
|
3
|
+
import type { UserRequestHandlerMap } from '@molecule/api-resource-user';
|
|
4
|
+
/**
|
|
5
|
+
* Composable route-mount helpers for the molecule fleet's default
|
|
6
|
+
* `api/src/App/router.ts`. Apps assemble their router by calling the
|
|
7
|
+
* mount* functions they need — `mountDefaultDeviceRoutes`,
|
|
8
|
+
* `mountDefaultUserAuthRoutes`, etc. — instead of writing 80 lines of
|
|
9
|
+
* `router.METHOD(path, ...handlers)` calls.
|
|
10
|
+
*
|
|
11
|
+
* Each helper takes the router + the corresponding resource handler
|
|
12
|
+
* map (e.g. `mountDefaultDeviceRoutes(router, Device)`). Helpers are
|
|
13
|
+
* additive and can be combined freely.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
type DeviceMap = DeviceRequestHandlerMap;
|
|
18
|
+
type UserMap = UserRequestHandlerMap;
|
|
19
|
+
/**
|
|
20
|
+
* Mounts the standard device routes:
|
|
21
|
+
*
|
|
22
|
+
* - `GET /devices/push/public-key` (public — the VAPID public key browsers
|
|
23
|
+
* need for `pushManager.subscribe({ applicationServerKey })`; bond-gated
|
|
24
|
+
* 404/503 when no push provider is bonded/configured)
|
|
25
|
+
* - `GET /devices` (auth+query)
|
|
26
|
+
* - `GET /devices/:id` (authUser+read)
|
|
27
|
+
* - `PATCH /devices/:id` (authUser+update)
|
|
28
|
+
* - `DELETE /devices/:id` (authUser+del)
|
|
29
|
+
*/
|
|
30
|
+
export declare function mountDefaultDeviceRoutes(router: Router, device: DeviceMap): void;
|
|
31
|
+
/**
|
|
32
|
+
* Mounts the public auth endpoints:
|
|
33
|
+
*
|
|
34
|
+
* - `POST /users` (create)
|
|
35
|
+
* - `POST /users/log-in` (rateLimitAuth + logIn)
|
|
36
|
+
* - `POST /users/forgot-password` (rateLimitAuth + forgotPassword)
|
|
37
|
+
*
|
|
38
|
+
* The credential-bearing routes are fronted by `user.rateLimitAuth` — the
|
|
39
|
+
* default IP+account brute-force throttle from `@molecule/api-resource-user` —
|
|
40
|
+
* so generated apps are not left with unthrottled password / TOTP-via-login
|
|
41
|
+
* guessing. The limiter degrades open (logs a warning) when no rate-limit
|
|
42
|
+
* provider is bonded, so apps that opt out still boot.
|
|
43
|
+
*/
|
|
44
|
+
export declare function mountDefaultUserAuthRoutes(router: Router, user: UserMap): void;
|
|
45
|
+
/**
|
|
46
|
+
* Optional OAuth routes — BOTH halves of the flow:
|
|
47
|
+
*
|
|
48
|
+
* - `GET /users/oauth/:provider` (rateLimitAuth + oauthAuthorize) —
|
|
49
|
+
* initiation: sets the CSRF `oauth_state` + PKCE `oauth_verifier` httpOnly
|
|
50
|
+
* cookies and 302-redirects to the bonded provider's authorization URL.
|
|
51
|
+
* Without this half the state cookie `logInOAuth` validates is never set,
|
|
52
|
+
* so every callback fails 403 (this is exactly how the generated-app fleet
|
|
53
|
+
* shipped an exchange endpoint with no way to start the dance). The GET
|
|
54
|
+
* carries the same `rateLimitAuth` throttle as the POST: it has no body, so
|
|
55
|
+
* only the generous per-IP bucket applies — an abuse ceiling on cookie-mint/
|
|
56
|
+
* redirect flooding that a legitimate login (one GET + one POST) never
|
|
57
|
+
* approaches. A trip is a 429 JSON on a top-level navigation, which is
|
|
58
|
+
* acceptable for that ceiling.
|
|
59
|
+
* - `POST /users/log-in/oauth` (rateLimitAuth + logInOAuth) — callback
|
|
60
|
+
* exchange: verifies state + code with the bonded provider and logs the
|
|
61
|
+
* user in.
|
|
62
|
+
*
|
|
63
|
+
* Only mount when the app wires an oauth bond. Handlers check the bond
|
|
64
|
+
* registry at request time, so an unbonded provider yields a clean 404.
|
|
65
|
+
*/
|
|
66
|
+
export declare function mountDefaultUserOAuthLoginRoute(router: Router, user: UserMap): void;
|
|
67
|
+
/**
|
|
68
|
+
* Optional reset-password route: `POST /users/reset-password` (rateLimitAuth +
|
|
69
|
+
* resetPassword). Only mount when the app uses the pkg's resetPassword handler
|
|
70
|
+
* rather than a custom local handler.
|
|
71
|
+
*/
|
|
72
|
+
export declare function mountDefaultUserResetPasswordRoute(router: Router, user: UserMap): void;
|
|
73
|
+
/**
|
|
74
|
+
* Mounts the authed-self user CRUD routes:
|
|
75
|
+
*
|
|
76
|
+
* - `GET /users/me` (auth+readSelf) — session restore; MUST precede `/users/:id`
|
|
77
|
+
* - `GET /users/:id` (authSelf+read)
|
|
78
|
+
* - `PATCH /users/:id` (authSelf+update)
|
|
79
|
+
* - `DELETE /users/:id` (authSelf+del)
|
|
80
|
+
*/
|
|
81
|
+
export declare function mountDefaultUserCrudRoutes(router: Router, user: UserMap): void;
|
|
82
|
+
/**
|
|
83
|
+
* Mounts password + 2FA security routes:
|
|
84
|
+
*
|
|
85
|
+
* - `PATCH /users/:id/password` (authSelf+updatePassword)
|
|
86
|
+
* - `POST /users/:id/verify-two-factor` (authSelf + rateLimitTwoFactor + verifyTwoFactor)
|
|
87
|
+
*
|
|
88
|
+
* The 2FA verification route carries a stricter limiter (`user.rateLimitTwoFactor`)
|
|
89
|
+
* that temp-locks the second factor per account after consecutive misses.
|
|
90
|
+
*/
|
|
91
|
+
export declare function mountDefaultUserSecurityRoutes(router: Router, user: UserMap): void;
|
|
92
|
+
/**
|
|
93
|
+
* Mounts plan/billing routes:
|
|
94
|
+
*
|
|
95
|
+
* - `PATCH /users/:id/plan` (authSelf+updatePlan)
|
|
96
|
+
* - `POST /users/payment-notification/:provider` (requireWebhookAuthenticity+handlePaymentNotification)
|
|
97
|
+
*
|
|
98
|
+
* The notification route is public (providers POST to it), so it is gated by
|
|
99
|
+
* `requireWebhookAuthenticity`: signature-verifying webhook providers (Stripe)
|
|
100
|
+
* pass through, while unsigned server-to-server providers (Apple/Google) require
|
|
101
|
+
* a shared secret — the endpoint is not open by default.
|
|
102
|
+
*/
|
|
103
|
+
export declare function mountDefaultUserBillingRoutes(router: Router, user: UserMap): void;
|
|
104
|
+
/**
|
|
105
|
+
* Optional payment-verification routes for apps that support
|
|
106
|
+
* client-driven payment confirmation (Apple/Google receipt verify).
|
|
107
|
+
*
|
|
108
|
+
* Both verbs require `authSelf` ([M3-1]): the handler mutates and returns the
|
|
109
|
+
* `:id` user, so an unauthenticated / cross-user call must not reach it. The
|
|
110
|
+
* permissive global `verifyMiddleware()` never blocks, so per-route `authSelf`
|
|
111
|
+
* is the gate. `authSelf` does NOT break the Stripe Checkout `success_url`
|
|
112
|
+
* callback — that is a top-level browser navigation which carries the
|
|
113
|
+
* `sameSite:'lax'` session cookie — and in-handler customer/checkout-session
|
|
114
|
+
* binding remains as defense-in-depth. This mirrors the hardened declarative
|
|
115
|
+
* route table (`resources/user/src/routes.ts`) and molecule-dev's live router;
|
|
116
|
+
* the fix had not been propagated to this mounter, which the generated-app
|
|
117
|
+
* fleet uses.
|
|
118
|
+
*
|
|
119
|
+
* - `GET /users/:id/verify-payment/:provider` (authSelf+verifyPayment)
|
|
120
|
+
* - `POST /users/:id/verify-payment/:provider` (authSelf+verifyPayment)
|
|
121
|
+
*/
|
|
122
|
+
export declare function mountDefaultUserVerifyPaymentRoutes(router: Router, user: UserMap): void;
|
|
123
|
+
export {};
|
|
124
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAA;AAC5E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AAExE;;;;;;;;;;;;GAYG;AAEH,KAAK,SAAS,GAAG,uBAAuB,CAAA;AAExC,KAAK,OAAO,GAAG,qBAAqB,CAAA;AAEpC;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAWhF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAU9E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAMnF;AAED;;;;GAIG;AACH,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAGtF;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAW9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAWlF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAQjF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAIvF"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mounts the standard device routes:
|
|
3
|
+
*
|
|
4
|
+
* - `GET /devices/push/public-key` (public — the VAPID public key browsers
|
|
5
|
+
* need for `pushManager.subscribe({ applicationServerKey })`; bond-gated
|
|
6
|
+
* 404/503 when no push provider is bonded/configured)
|
|
7
|
+
* - `GET /devices` (auth+query)
|
|
8
|
+
* - `GET /devices/:id` (authUser+read)
|
|
9
|
+
* - `PATCH /devices/:id` (authUser+update)
|
|
10
|
+
* - `DELETE /devices/:id` (authUser+del)
|
|
11
|
+
*/
|
|
12
|
+
export function mountDefaultDeviceRoutes(router, device) {
|
|
13
|
+
// Mounted before /devices/:id so the literal "push" segment can never be
|
|
14
|
+
// captured as an :id. Guarded like user.readSelf/oauthAuthorize so older
|
|
15
|
+
// handler maps (and test fixtures) without the handler still mount.
|
|
16
|
+
if (device.pushPublicKey) {
|
|
17
|
+
router.get('/devices/push/public-key', device.pushPublicKey);
|
|
18
|
+
}
|
|
19
|
+
router.get('/devices', device.auth, device.query);
|
|
20
|
+
router.get('/devices/:id', device.authUser, device.read);
|
|
21
|
+
router.patch('/devices/:id', device.authUser, device.update);
|
|
22
|
+
router.delete('/devices/:id', device.authUser, device.del);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Mounts the public auth endpoints:
|
|
26
|
+
*
|
|
27
|
+
* - `POST /users` (create)
|
|
28
|
+
* - `POST /users/log-in` (rateLimitAuth + logIn)
|
|
29
|
+
* - `POST /users/forgot-password` (rateLimitAuth + forgotPassword)
|
|
30
|
+
*
|
|
31
|
+
* The credential-bearing routes are fronted by `user.rateLimitAuth` — the
|
|
32
|
+
* default IP+account brute-force throttle from `@molecule/api-resource-user` —
|
|
33
|
+
* so generated apps are not left with unthrottled password / TOTP-via-login
|
|
34
|
+
* guessing. The limiter degrades open (logs a warning) when no rate-limit
|
|
35
|
+
* provider is bonded, so apps that opt out still boot.
|
|
36
|
+
*/
|
|
37
|
+
export function mountDefaultUserAuthRoutes(router, user) {
|
|
38
|
+
router.post('/users', user.create);
|
|
39
|
+
router.post('/users/log-in', user.rateLimitAuth, user.logIn);
|
|
40
|
+
// Logout — cookie-authed device revocation + credential-cookie clearing.
|
|
41
|
+
// [M1-1] required so logout clears the httpOnly cookie (else the cookie-based
|
|
42
|
+
// session restore re-logs-in on the next load).
|
|
43
|
+
if (user.logout) {
|
|
44
|
+
router.post('/users/logout', user.auth, user.logout);
|
|
45
|
+
}
|
|
46
|
+
router.post('/users/forgot-password', user.rateLimitAuth, user.forgotPassword);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Optional OAuth routes — BOTH halves of the flow:
|
|
50
|
+
*
|
|
51
|
+
* - `GET /users/oauth/:provider` (rateLimitAuth + oauthAuthorize) —
|
|
52
|
+
* initiation: sets the CSRF `oauth_state` + PKCE `oauth_verifier` httpOnly
|
|
53
|
+
* cookies and 302-redirects to the bonded provider's authorization URL.
|
|
54
|
+
* Without this half the state cookie `logInOAuth` validates is never set,
|
|
55
|
+
* so every callback fails 403 (this is exactly how the generated-app fleet
|
|
56
|
+
* shipped an exchange endpoint with no way to start the dance). The GET
|
|
57
|
+
* carries the same `rateLimitAuth` throttle as the POST: it has no body, so
|
|
58
|
+
* only the generous per-IP bucket applies — an abuse ceiling on cookie-mint/
|
|
59
|
+
* redirect flooding that a legitimate login (one GET + one POST) never
|
|
60
|
+
* approaches. A trip is a 429 JSON on a top-level navigation, which is
|
|
61
|
+
* acceptable for that ceiling.
|
|
62
|
+
* - `POST /users/log-in/oauth` (rateLimitAuth + logInOAuth) — callback
|
|
63
|
+
* exchange: verifies state + code with the bonded provider and logs the
|
|
64
|
+
* user in.
|
|
65
|
+
*
|
|
66
|
+
* Only mount when the app wires an oauth bond. Handlers check the bond
|
|
67
|
+
* registry at request time, so an unbonded provider yields a clean 404.
|
|
68
|
+
*/
|
|
69
|
+
export function mountDefaultUserOAuthLoginRoute(router, user) {
|
|
70
|
+
if (user.oauthAuthorize) {
|
|
71
|
+
router.get('/users/oauth/:provider', user.rateLimitAuth, user.oauthAuthorize);
|
|
72
|
+
}
|
|
73
|
+
if (!user.logInOAuth)
|
|
74
|
+
return;
|
|
75
|
+
router.post('/users/log-in/oauth', user.rateLimitAuth, user.logInOAuth);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Optional reset-password route: `POST /users/reset-password` (rateLimitAuth +
|
|
79
|
+
* resetPassword). Only mount when the app uses the pkg's resetPassword handler
|
|
80
|
+
* rather than a custom local handler.
|
|
81
|
+
*/
|
|
82
|
+
export function mountDefaultUserResetPasswordRoute(router, user) {
|
|
83
|
+
if (!user.resetPassword)
|
|
84
|
+
return;
|
|
85
|
+
router.post('/users/reset-password', user.rateLimitAuth, user.resetPassword);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Mounts the authed-self user CRUD routes:
|
|
89
|
+
*
|
|
90
|
+
* - `GET /users/me` (auth+readSelf) — session restore; MUST precede `/users/:id`
|
|
91
|
+
* - `GET /users/:id` (authSelf+read)
|
|
92
|
+
* - `PATCH /users/:id` (authSelf+update)
|
|
93
|
+
* - `DELETE /users/:id` (authSelf+del)
|
|
94
|
+
*/
|
|
95
|
+
export function mountDefaultUserCrudRoutes(router, user) {
|
|
96
|
+
// Current user — cookie-authed session restore. Registered before `/users/:id`
|
|
97
|
+
// so Express doesn't capture `me` as an `:id`. [M1-1] the app's auth client
|
|
98
|
+
// calls this on init to restore the session from the httpOnly cookie after a
|
|
99
|
+
// full page load (the in-memory bearer token does not survive a reload).
|
|
100
|
+
if (user.readSelf) {
|
|
101
|
+
router.get('/users/me', user.auth, user.readSelf);
|
|
102
|
+
}
|
|
103
|
+
router.get('/users/:id', user.authSelf, user.read);
|
|
104
|
+
router.patch('/users/:id', user.authSelf, user.update);
|
|
105
|
+
router.delete('/users/:id', user.authSelf, user.del);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Mounts password + 2FA security routes:
|
|
109
|
+
*
|
|
110
|
+
* - `PATCH /users/:id/password` (authSelf+updatePassword)
|
|
111
|
+
* - `POST /users/:id/verify-two-factor` (authSelf + rateLimitTwoFactor + verifyTwoFactor)
|
|
112
|
+
*
|
|
113
|
+
* The 2FA verification route carries a stricter limiter (`user.rateLimitTwoFactor`)
|
|
114
|
+
* that temp-locks the second factor per account after consecutive misses.
|
|
115
|
+
*/
|
|
116
|
+
export function mountDefaultUserSecurityRoutes(router, user) {
|
|
117
|
+
router.patch('/users/:id/password', user.authSelf, user.updatePassword);
|
|
118
|
+
// POST alias for clients that use the auth-client `changePassword` flow,
|
|
119
|
+
// which historically dispatches POST. Same handler; both verbs accepted.
|
|
120
|
+
router.post('/users/:id/password', user.authSelf, user.updatePassword);
|
|
121
|
+
router.post('/users/:id/verify-two-factor', user.authSelf, user.rateLimitTwoFactor, user.verifyTwoFactor);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Mounts plan/billing routes:
|
|
125
|
+
*
|
|
126
|
+
* - `PATCH /users/:id/plan` (authSelf+updatePlan)
|
|
127
|
+
* - `POST /users/payment-notification/:provider` (requireWebhookAuthenticity+handlePaymentNotification)
|
|
128
|
+
*
|
|
129
|
+
* The notification route is public (providers POST to it), so it is gated by
|
|
130
|
+
* `requireWebhookAuthenticity`: signature-verifying webhook providers (Stripe)
|
|
131
|
+
* pass through, while unsigned server-to-server providers (Apple/Google) require
|
|
132
|
+
* a shared secret — the endpoint is not open by default.
|
|
133
|
+
*/
|
|
134
|
+
export function mountDefaultUserBillingRoutes(router, user) {
|
|
135
|
+
router.patch('/users/:id/plan', user.authSelf, user.updatePlan);
|
|
136
|
+
if (user.handlePaymentNotification) {
|
|
137
|
+
const notificationHandlers = user.requireWebhookAuthenticity
|
|
138
|
+
? [user.requireWebhookAuthenticity, user.handlePaymentNotification]
|
|
139
|
+
: [user.handlePaymentNotification];
|
|
140
|
+
router.post('/users/payment-notification/:provider', ...notificationHandlers);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Optional payment-verification routes for apps that support
|
|
145
|
+
* client-driven payment confirmation (Apple/Google receipt verify).
|
|
146
|
+
*
|
|
147
|
+
* Both verbs require `authSelf` ([M3-1]): the handler mutates and returns the
|
|
148
|
+
* `:id` user, so an unauthenticated / cross-user call must not reach it. The
|
|
149
|
+
* permissive global `verifyMiddleware()` never blocks, so per-route `authSelf`
|
|
150
|
+
* is the gate. `authSelf` does NOT break the Stripe Checkout `success_url`
|
|
151
|
+
* callback — that is a top-level browser navigation which carries the
|
|
152
|
+
* `sameSite:'lax'` session cookie — and in-handler customer/checkout-session
|
|
153
|
+
* binding remains as defense-in-depth. This mirrors the hardened declarative
|
|
154
|
+
* route table (`resources/user/src/routes.ts`) and molecule-dev's live router;
|
|
155
|
+
* the fix had not been propagated to this mounter, which the generated-app
|
|
156
|
+
* fleet uses.
|
|
157
|
+
*
|
|
158
|
+
* - `GET /users/:id/verify-payment/:provider` (authSelf+verifyPayment)
|
|
159
|
+
* - `POST /users/:id/verify-payment/:provider` (authSelf+verifyPayment)
|
|
160
|
+
*/
|
|
161
|
+
export function mountDefaultUserVerifyPaymentRoutes(router, user) {
|
|
162
|
+
if (!user.verifyPayment)
|
|
163
|
+
return;
|
|
164
|
+
router.get('/users/:id/verify-payment/:provider', user.authSelf, user.verifyPayment);
|
|
165
|
+
router.post('/users/:id/verify-payment/:provider', user.authSelf, user.verifyPayment);
|
|
166
|
+
}
|