@wopr-network/platform-core 1.0.0 → 1.0.2
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/coverage/coverage-summary.json +119 -0
- package/dist/admin/index.d.ts +1 -1
- package/dist/auth/better-auth.d.ts +45 -0
- package/dist/auth/better-auth.js +70 -47
- package/dist/auth/index.d.ts +12 -0
- package/dist/auth/index.js +7 -0
- package/dist/billing/drizzle-webhook-seen-repository.d.ts +1 -1
- package/dist/billing/index.d.ts +4 -4
- package/dist/billing/index.js +4 -4
- package/dist/billing/payram/webhook.test.js +1 -1
- package/dist/billing/stripe/index.d.ts +5 -5
- package/dist/billing/stripe/index.js +2 -2
- package/dist/billing/stripe/stripe-payment-processor.js +1 -1
- package/dist/credits/auto-topup-charge.d.ts +2 -2
- package/dist/credits/auto-topup-charge.test.js +1 -1
- package/dist/credits/auto-topup-schedule.d.ts +1 -1
- package/dist/credits/auto-topup-schedule.test.js +1 -1
- package/dist/credits/auto-topup-settings-repository.test.js +1 -1
- package/dist/credits/auto-topup-usage.d.ts +1 -1
- package/dist/credits/auto-topup-usage.test.js +1 -1
- package/dist/credits/index.d.ts +1 -1
- package/dist/credits/index.js +1 -1
- package/dist/db/schema/index.d.ts +1 -1
- package/dist/db/schema/index.js +1 -1
- package/dist/email/index.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -3
- package/dist/metering/aggregator.test.js +1 -1
- package/dist/metering/emitter.test.js +1 -1
- package/dist/metering/load-test.bench.js +1 -1
- package/dist/metering/metering.test.js +1 -1
- package/dist/metering/reconciliation-cron.test.js +2 -2
- package/dist/metering/reconciliation-repository.test.js +1 -1
- package/dist/middleware/index.d.ts +3 -3
- package/dist/middleware/index.js +2 -2
- package/dist/security/credential-vault/index.d.ts +2 -2
- package/dist/security/index.d.ts +7 -7
- package/dist/security/index.js +7 -7
- package/dist/security/redirect-allowlist.js +10 -8
- package/dist/security/tenant-keys/index.d.ts +6 -6
- package/dist/security/tenant-keys/index.js +3 -3
- package/dist/tenancy/index.d.ts +3 -3
- package/dist/tenancy/org-service.d.ts +1 -1
- package/dist/tenancy/org-service.test.js +1 -1
- package/dist/trpc/index.d.ts +1 -1
- package/dist/trpc/index.js +1 -1
- package/dist/trpc/init.test.js +3 -5
- package/package.json +2 -1
- package/src/admin/index.ts +1 -1
- package/src/auth/better-auth.ts +129 -48
- package/src/auth/index.ts +31 -0
- package/src/billing/drizzle-webhook-seen-repository.ts +1 -1
- package/src/billing/index.ts +11 -13
- package/src/billing/payram/webhook.test.ts +1 -1
- package/src/billing/stripe/index.ts +17 -5
- package/src/billing/stripe/stripe-payment-processor.test.ts +2 -3
- package/src/billing/stripe/stripe-payment-processor.ts +1 -1
- package/src/credits/auto-topup-charge.test.ts +2 -2
- package/src/credits/auto-topup-charge.ts +2 -2
- package/src/credits/auto-topup-schedule.test.ts +1 -1
- package/src/credits/auto-topup-schedule.ts +1 -1
- package/src/credits/auto-topup-settings-repository.test.ts +1 -1
- package/src/credits/auto-topup-usage.test.ts +1 -1
- package/src/credits/auto-topup-usage.ts +1 -1
- package/src/credits/index.ts +1 -1
- package/src/db/schema/index.ts +1 -1
- package/src/email/index.ts +3 -3
- package/src/index.ts +13 -17
- package/src/metering/aggregator.test.ts +1 -1
- package/src/metering/emitter.test.ts +1 -1
- package/src/metering/load-test.bench.ts +1 -1
- package/src/metering/metering.test.ts +1 -1
- package/src/metering/reconciliation-cron.test.ts +2 -2
- package/src/metering/reconciliation-repository.test.ts +2 -2
- package/src/middleware/index.ts +5 -5
- package/src/middleware/rate-limit.test.ts +1 -1
- package/src/middleware/rate-limit.ts +1 -1
- package/src/security/credential-vault/index.ts +2 -2
- package/src/security/index.ts +43 -38
- package/src/security/redirect-allowlist.ts +11 -8
- package/src/security/tenant-keys/index.ts +10 -6
- package/src/tenancy/index.ts +3 -3
- package/src/tenancy/org-service.test.ts +1 -1
- package/src/tenancy/org-service.ts +1 -1
- package/src/trpc/index.ts +5 -5
- package/src/trpc/init.test.ts +8 -10
- package/vitest.config.ts +4 -0
package/src/auth/better-auth.ts
CHANGED
|
@@ -11,25 +11,84 @@
|
|
|
11
11
|
import { type BetterAuthOptions, betterAuth } from "better-auth";
|
|
12
12
|
import { twoFactor } from "better-auth/plugins";
|
|
13
13
|
import type { Pool } from "pg";
|
|
14
|
-
import type { PlatformDb } from "../db/index.js";
|
|
15
14
|
import { RoleStore } from "../admin/role-store.js";
|
|
16
15
|
import { logger } from "../config/logger.js";
|
|
17
16
|
import { initTwoFactorSchema } from "../db/auth-user-repository.js";
|
|
17
|
+
import type { PlatformDb } from "../db/index.js";
|
|
18
18
|
import { getEmailClient } from "../email/client.js";
|
|
19
19
|
import { passwordResetEmailTemplate, verifyEmailTemplate } from "../email/templates.js";
|
|
20
20
|
import { generateVerificationToken, initVerificationSchema, PgEmailVerifier } from "../email/verification.js";
|
|
21
21
|
import { createUserCreator, type IUserCreator } from "./user-creator.js";
|
|
22
22
|
|
|
23
|
+
/** OAuth provider credentials. */
|
|
24
|
+
export interface OAuthProvider {
|
|
25
|
+
clientId: string;
|
|
26
|
+
clientSecret: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Rate limit rule for a specific auth endpoint. */
|
|
30
|
+
export interface AuthRateLimitRule {
|
|
31
|
+
window: number;
|
|
32
|
+
max: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
/** Configuration for initializing Better Auth in platform-core. */
|
|
24
36
|
export interface BetterAuthConfig {
|
|
25
37
|
pool: Pool;
|
|
26
38
|
db: PlatformDb;
|
|
39
|
+
|
|
40
|
+
// --- Required ---
|
|
41
|
+
/** HMAC secret for session tokens. Falls back to BETTER_AUTH_SECRET env var. */
|
|
42
|
+
secret?: string;
|
|
43
|
+
/** Base URL for OAuth callbacks. Falls back to BETTER_AUTH_URL env var. */
|
|
44
|
+
baseURL?: string;
|
|
45
|
+
|
|
46
|
+
// --- Auth features ---
|
|
47
|
+
/** Route prefix. Default: "/api/auth" */
|
|
48
|
+
basePath?: string;
|
|
49
|
+
/** Email+password config. Default: enabled with 12-char min. */
|
|
50
|
+
emailAndPassword?: { enabled: boolean; minPasswordLength?: number };
|
|
51
|
+
/** OAuth providers. Default: reads GITHUB/DISCORD/GOOGLE env vars. */
|
|
52
|
+
socialProviders?: {
|
|
53
|
+
github?: OAuthProvider;
|
|
54
|
+
discord?: OAuthProvider;
|
|
55
|
+
google?: OAuthProvider;
|
|
56
|
+
};
|
|
57
|
+
/** Trusted providers for account linking. Default: ["github", "google"] */
|
|
58
|
+
trustedProviders?: string[];
|
|
59
|
+
/** Enable 2FA plugin. Default: true */
|
|
60
|
+
twoFactor?: boolean;
|
|
61
|
+
|
|
62
|
+
// --- Session & cookies ---
|
|
63
|
+
/** Cookie cache max age in seconds. Default: 300 (5 min) */
|
|
64
|
+
sessionCacheMaxAge?: number;
|
|
65
|
+
/** Cookie prefix. Default: "better-auth" */
|
|
66
|
+
cookiePrefix?: string;
|
|
67
|
+
/** Cookie domain (e.g., ".wopr.bot"). Falls back to COOKIE_DOMAIN env var. */
|
|
68
|
+
cookieDomain?: string;
|
|
69
|
+
|
|
70
|
+
// --- Rate limiting ---
|
|
71
|
+
/** Global rate limit window in seconds. Default: 60 */
|
|
72
|
+
rateLimitWindow?: number;
|
|
73
|
+
/** Global rate limit max requests. Default: 100 */
|
|
74
|
+
rateLimitMax?: number;
|
|
75
|
+
/** Per-endpoint rate limit overrides. Default: sign-in/sign-up/reset limits. */
|
|
76
|
+
rateLimitRules?: Record<string, AuthRateLimitRule>;
|
|
77
|
+
|
|
78
|
+
// --- Origins ---
|
|
79
|
+
/** Trusted origins for CORS. Falls back to UI_ORIGIN env var. */
|
|
80
|
+
trustedOrigins?: string[];
|
|
81
|
+
|
|
82
|
+
// --- Lifecycle hooks ---
|
|
27
83
|
/** Called after a new user signs up (e.g., create personal tenant). */
|
|
28
84
|
onUserCreated?: (userId: string, userName: string, email: string) => Promise<void>;
|
|
29
85
|
}
|
|
30
86
|
|
|
31
|
-
const
|
|
32
|
-
|
|
87
|
+
const DEFAULT_RATE_LIMIT_RULES: Record<string, AuthRateLimitRule> = {
|
|
88
|
+
"/sign-in/email": { window: 900, max: 5 },
|
|
89
|
+
"/sign-up/email": { window: 3600, max: 10 },
|
|
90
|
+
"/request-password-reset": { window: 3600, max: 3 },
|
|
91
|
+
};
|
|
33
92
|
|
|
34
93
|
let _config: BetterAuthConfig | null = null;
|
|
35
94
|
let _userCreator: IUserCreator | null = null;
|
|
@@ -47,32 +106,59 @@ async function getUserCreator(): Promise<IUserCreator> {
|
|
|
47
106
|
return _userCreatorPromise;
|
|
48
107
|
}
|
|
49
108
|
|
|
50
|
-
|
|
109
|
+
/** Resolve OAuth providers from config or env vars. */
|
|
110
|
+
function resolveSocialProviders(cfg: BetterAuthConfig): BetterAuthOptions["socialProviders"] {
|
|
111
|
+
if (cfg.socialProviders) return cfg.socialProviders;
|
|
112
|
+
return {
|
|
113
|
+
...(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET
|
|
114
|
+
? { github: { clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET } }
|
|
115
|
+
: {}),
|
|
116
|
+
...(process.env.DISCORD_CLIENT_ID && process.env.DISCORD_CLIENT_SECRET
|
|
117
|
+
? { discord: { clientId: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET } }
|
|
118
|
+
: {}),
|
|
119
|
+
...(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET
|
|
120
|
+
? { google: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET } }
|
|
121
|
+
: {}),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function authOptions(cfg: BetterAuthConfig): BetterAuthOptions {
|
|
126
|
+
const pool = cfg.pool;
|
|
127
|
+
const secret = cfg.secret || process.env.BETTER_AUTH_SECRET;
|
|
128
|
+
if (!secret) {
|
|
129
|
+
if (process.env.NODE_ENV === "production") {
|
|
130
|
+
throw new Error("BETTER_AUTH_SECRET is required in production");
|
|
131
|
+
}
|
|
132
|
+
logger.warn("BetterAuth secret not configured — sessions may be insecure");
|
|
133
|
+
}
|
|
134
|
+
const baseURL = cfg.baseURL || process.env.BETTER_AUTH_URL || "http://localhost:3100";
|
|
135
|
+
const basePath = cfg.basePath || "/api/auth";
|
|
136
|
+
const cookieDomain = cfg.cookieDomain || process.env.COOKIE_DOMAIN;
|
|
137
|
+
const trustedOrigins =
|
|
138
|
+
cfg.trustedOrigins ||
|
|
139
|
+
(process.env.UI_ORIGIN || "http://localhost:3001")
|
|
140
|
+
.split(",")
|
|
141
|
+
.map((o) => o.trim())
|
|
142
|
+
.filter(Boolean);
|
|
143
|
+
// Default minPasswordLength: 12 — caller must explicitly override, not accidentally omit
|
|
144
|
+
const emailAndPassword = cfg.emailAndPassword
|
|
145
|
+
? { minPasswordLength: 12, ...cfg.emailAndPassword }
|
|
146
|
+
: { enabled: true, minPasswordLength: 12 };
|
|
147
|
+
|
|
51
148
|
return {
|
|
52
149
|
database: pool,
|
|
53
|
-
secret:
|
|
54
|
-
baseURL
|
|
55
|
-
basePath
|
|
56
|
-
socialProviders:
|
|
57
|
-
...(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET
|
|
58
|
-
? { github: { clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET } }
|
|
59
|
-
: {}),
|
|
60
|
-
...(process.env.DISCORD_CLIENT_ID && process.env.DISCORD_CLIENT_SECRET
|
|
61
|
-
? { discord: { clientId: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET } }
|
|
62
|
-
: {}),
|
|
63
|
-
...(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET
|
|
64
|
-
? { google: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET } }
|
|
65
|
-
: {}),
|
|
66
|
-
},
|
|
150
|
+
secret: secret || "",
|
|
151
|
+
baseURL,
|
|
152
|
+
basePath,
|
|
153
|
+
socialProviders: resolveSocialProviders(cfg),
|
|
67
154
|
account: {
|
|
68
155
|
accountLinking: {
|
|
69
156
|
enabled: true,
|
|
70
|
-
trustedProviders: ["github", "google"],
|
|
157
|
+
trustedProviders: cfg.trustedProviders ?? ["github", "google"],
|
|
71
158
|
},
|
|
72
159
|
},
|
|
73
160
|
emailAndPassword: {
|
|
74
|
-
|
|
75
|
-
minPasswordLength: 12,
|
|
161
|
+
...emailAndPassword,
|
|
76
162
|
sendResetPassword: async ({ user, url }) => {
|
|
77
163
|
try {
|
|
78
164
|
const emailClient = getEmailClient();
|
|
@@ -99,12 +185,20 @@ function authOptions(pool: Pool): BetterAuthOptions {
|
|
|
99
185
|
logger.error("Failed to run user creator:", error);
|
|
100
186
|
}
|
|
101
187
|
|
|
188
|
+
if (cfg.onUserCreated) {
|
|
189
|
+
try {
|
|
190
|
+
await cfg.onUserCreated(user.id, user.name || user.email, user.email);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
logger.error("Failed to run onUserCreated callback:", error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
102
196
|
if (user.emailVerified) return;
|
|
103
197
|
|
|
104
198
|
try {
|
|
105
199
|
await initVerificationSchema(pool);
|
|
106
200
|
const { token } = await generateVerificationToken(pool, user.id);
|
|
107
|
-
const verifyUrl = `${
|
|
201
|
+
const verifyUrl = `${baseURL}${basePath}/verify?token=${token}`;
|
|
108
202
|
const emailClient = getEmailClient();
|
|
109
203
|
const template = verifyEmailTemplate(verifyUrl, user.email);
|
|
110
204
|
await emailClient.send({
|
|
@@ -116,45 +210,30 @@ function authOptions(pool: Pool): BetterAuthOptions {
|
|
|
116
210
|
} catch (error) {
|
|
117
211
|
logger.error("Failed to send verification email:", error);
|
|
118
212
|
}
|
|
119
|
-
|
|
120
|
-
// Delegate personal tenant creation to the consumer
|
|
121
|
-
if (_config?.onUserCreated) {
|
|
122
|
-
try {
|
|
123
|
-
await _config.onUserCreated(user.id, user.name || user.email, user.email);
|
|
124
|
-
} catch (error) {
|
|
125
|
-
logger.error("Failed to run onUserCreated callback:", error);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
213
|
},
|
|
129
214
|
},
|
|
130
215
|
},
|
|
131
216
|
},
|
|
132
217
|
session: {
|
|
133
|
-
cookieCache: { enabled: true, maxAge:
|
|
218
|
+
cookieCache: { enabled: true, maxAge: cfg.sessionCacheMaxAge ?? 300 },
|
|
134
219
|
},
|
|
135
220
|
advanced: {
|
|
136
|
-
cookiePrefix: "better-auth",
|
|
221
|
+
cookiePrefix: cfg.cookiePrefix || "better-auth",
|
|
137
222
|
cookies: {
|
|
138
223
|
session_token: {
|
|
139
|
-
attributes: {
|
|
140
|
-
domain: process.env.COOKIE_DOMAIN || ".wopr.bot",
|
|
141
|
-
},
|
|
224
|
+
attributes: cookieDomain ? { domain: cookieDomain } : {},
|
|
142
225
|
},
|
|
143
226
|
},
|
|
144
227
|
},
|
|
145
|
-
plugins: [twoFactor()],
|
|
228
|
+
plugins: cfg.twoFactor !== false ? [twoFactor()] : [],
|
|
146
229
|
rateLimit: {
|
|
147
230
|
enabled: true,
|
|
148
|
-
window: 60,
|
|
149
|
-
max: 100,
|
|
150
|
-
customRules: {
|
|
151
|
-
"/sign-in/email": { window: 900, max: 5 },
|
|
152
|
-
"/sign-up/email": { window: 3600, max: 10 },
|
|
153
|
-
"/request-password-reset": { window: 3600, max: 3 },
|
|
154
|
-
},
|
|
231
|
+
window: cfg.rateLimitWindow ?? 60,
|
|
232
|
+
max: cfg.rateLimitMax ?? 100,
|
|
233
|
+
customRules: { ...DEFAULT_RATE_LIMIT_RULES, ...cfg.rateLimitRules },
|
|
155
234
|
storage: "memory",
|
|
156
235
|
},
|
|
157
|
-
trustedOrigins
|
|
236
|
+
trustedOrigins,
|
|
158
237
|
};
|
|
159
238
|
}
|
|
160
239
|
|
|
@@ -174,9 +253,11 @@ export async function runAuthMigrations(): Promise<void> {
|
|
|
174
253
|
if (!_config) throw new Error("BetterAuth not initialized — call initBetterAuth() first");
|
|
175
254
|
type DbModule = { getMigrations: (opts: BetterAuthOptions) => Promise<{ runMigrations: () => Promise<void> }> };
|
|
176
255
|
const { getMigrations } = (await import("better-auth/db")) as unknown as DbModule;
|
|
177
|
-
const { runMigrations } = await getMigrations(authOptions(_config
|
|
256
|
+
const { runMigrations } = await getMigrations(authOptions(_config));
|
|
178
257
|
await runMigrations();
|
|
179
|
-
|
|
258
|
+
if (_config.twoFactor !== false) {
|
|
259
|
+
await initTwoFactorSchema(_config.pool);
|
|
260
|
+
}
|
|
180
261
|
}
|
|
181
262
|
|
|
182
263
|
let _auth: Auth | null = null;
|
|
@@ -188,7 +269,7 @@ let _auth: Auth | null = null;
|
|
|
188
269
|
export function getAuth(): Auth {
|
|
189
270
|
if (!_auth) {
|
|
190
271
|
if (!_config) throw new Error("BetterAuth not initialized — call initBetterAuth() first");
|
|
191
|
-
_auth = betterAuth(authOptions(_config
|
|
272
|
+
_auth = betterAuth(authOptions(_config));
|
|
192
273
|
}
|
|
193
274
|
return _auth;
|
|
194
275
|
}
|
package/src/auth/index.ts
CHANGED
|
@@ -518,3 +518,34 @@ export async function validateTenantAccess(
|
|
|
518
518
|
const member = await orgMemberRepo.findMember(requestedTenantId, userId);
|
|
519
519
|
return member !== null;
|
|
520
520
|
}
|
|
521
|
+
|
|
522
|
+
// ---------------------------------------------------------------------------
|
|
523
|
+
// Re-exports — Better Auth factory, middleware, and repositories
|
|
524
|
+
// ---------------------------------------------------------------------------
|
|
525
|
+
|
|
526
|
+
export type { IApiKeyRepository } from "./api-key-repository.js";
|
|
527
|
+
export { DrizzleApiKeyRepository } from "./api-key-repository.js";
|
|
528
|
+
export type {
|
|
529
|
+
Auth,
|
|
530
|
+
AuthRateLimitRule,
|
|
531
|
+
BetterAuthConfig,
|
|
532
|
+
OAuthProvider,
|
|
533
|
+
} from "./better-auth.js";
|
|
534
|
+
// Test utilities — do not call in production code
|
|
535
|
+
export {
|
|
536
|
+
getAuth,
|
|
537
|
+
getEmailVerifier,
|
|
538
|
+
initBetterAuth,
|
|
539
|
+
resetAuth,
|
|
540
|
+
resetUserCreator,
|
|
541
|
+
runAuthMigrations,
|
|
542
|
+
setAuth,
|
|
543
|
+
} from "./better-auth.js";
|
|
544
|
+
export type { ILoginHistoryRepository, LoginHistoryEntry } from "./login-history-repository.js";
|
|
545
|
+
export { BetterAuthLoginHistoryRepository } from "./login-history-repository.js";
|
|
546
|
+
export type { SessionAuthEnv } from "./middleware.js";
|
|
547
|
+
export { dualAuth, sessionAuth } from "./middleware.js";
|
|
548
|
+
export type { IUserCreator } from "./user-creator.js";
|
|
549
|
+
export { createUserCreator } from "./user-creator.js";
|
|
550
|
+
export type { IUserRoleRepository } from "./user-role-repository.js";
|
|
551
|
+
export { DrizzleUserRoleRepository } from "./user-role-repository.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { and, eq, lt } from "drizzle-orm";
|
|
2
|
+
import type { WebhookSeenEvent } from "../credits/repository-types.js";
|
|
2
3
|
import type { PlatformDb } from "../db/index.js";
|
|
3
4
|
import { webhookSeenEvents } from "../db/schema/index.js";
|
|
4
|
-
import type { WebhookSeenEvent } from "../credits/repository-types.js";
|
|
5
5
|
import type { IWebhookSeenRepository } from "./webhook-seen-repository.js";
|
|
6
6
|
|
|
7
7
|
export class DrizzleWebhookSeenRepository implements IWebhookSeenRepository {
|
package/src/billing/index.ts
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
|
+
export { DrizzleWebhookSeenRepository } from "./drizzle-webhook-seen-repository.js";
|
|
1
2
|
export type {
|
|
2
|
-
SavedPaymentMethod,
|
|
3
|
-
CheckoutOpts,
|
|
4
|
-
CheckoutSession,
|
|
5
3
|
ChargeOpts,
|
|
6
4
|
ChargeResult,
|
|
7
|
-
|
|
5
|
+
CheckoutOpts,
|
|
6
|
+
CheckoutSession,
|
|
7
|
+
Invoice,
|
|
8
|
+
IPaymentProcessor,
|
|
8
9
|
PortalOpts,
|
|
10
|
+
SavedPaymentMethod,
|
|
11
|
+
SetupResult,
|
|
9
12
|
WebhookResult,
|
|
10
|
-
IPaymentProcessor,
|
|
11
|
-
Invoice,
|
|
12
13
|
} from "./payment-processor.js";
|
|
13
14
|
export { PaymentMethodOwnershipError } from "./payment-processor.js";
|
|
14
|
-
export type { IWebhookSeenRepository } from "./webhook-seen-repository.js";
|
|
15
|
-
export { noOpReplayGuard } from "./webhook-seen-repository.js";
|
|
16
|
-
export { DrizzleWebhookSeenRepository } from "./drizzle-webhook-seen-repository.js";
|
|
17
|
-
|
|
18
|
-
// Stripe
|
|
19
|
-
export * from "./stripe/index.js";
|
|
20
|
-
|
|
21
15
|
// PayRam
|
|
22
16
|
export * from "./payram/index.js";
|
|
17
|
+
// Stripe
|
|
18
|
+
export * from "./stripe/index.js";
|
|
19
|
+
export type { IWebhookSeenRepository } from "./webhook-seen-repository.js";
|
|
20
|
+
export { noOpReplayGuard } from "./webhook-seen-repository.js";
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
import type { PGlite } from "@electric-sql/pglite";
|
|
9
9
|
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
10
|
+
import { CreditLedger } from "../../credits/credit-ledger.js";
|
|
10
11
|
import type { PlatformDb } from "../../db/index.js";
|
|
11
12
|
import { createTestDb, truncateAllTables } from "../../test/db.js";
|
|
12
|
-
import { CreditLedger } from "../../credits/credit-ledger.js";
|
|
13
13
|
import { DrizzleWebhookSeenRepository } from "../drizzle-webhook-seen-repository.js";
|
|
14
14
|
import { noOpReplayGuard } from "../webhook-seen-repository.js";
|
|
15
15
|
import { PayRamChargeRepository } from "./charge-store.js";
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
export { createCreditCheckoutSession, createVpsCheckoutSession } from "./checkout.js";
|
|
2
2
|
export { createStripeClient, loadStripeConfig } from "./client.js";
|
|
3
|
-
export type {
|
|
4
|
-
export {
|
|
5
|
-
|
|
3
|
+
export type { CreditPriceMap, CreditPricePoint } from "./credit-prices.js";
|
|
4
|
+
export {
|
|
5
|
+
CREDIT_PRICE_POINTS,
|
|
6
|
+
getConfiguredPriceIds,
|
|
7
|
+
getCreditAmountForPurchase,
|
|
8
|
+
loadCreditPriceMap,
|
|
9
|
+
lookupCreditPrice,
|
|
10
|
+
} from "./credit-prices.js";
|
|
6
11
|
export type { DetachPaymentMethodOpts } from "./payment-methods.js";
|
|
12
|
+
export { detachAllPaymentMethods, detachPaymentMethod } from "./payment-methods.js";
|
|
7
13
|
export { createPortalSession } from "./portal.js";
|
|
8
14
|
export type { SetupIntentOpts } from "./setup-intent.js";
|
|
9
15
|
export { createSetupIntent } from "./setup-intent.js";
|
|
10
|
-
export type {
|
|
16
|
+
export type { StripePaymentProcessorDeps, StripeWebhookHandlerResult } from "./stripe-payment-processor.js";
|
|
11
17
|
export { StripePaymentProcessor } from "./stripe-payment-processor.js";
|
|
12
18
|
export type { ITenantCustomerRepository } from "./tenant-store.js";
|
|
13
19
|
export { DrizzleTenantCustomerRepository, TenantCustomerRepository } from "./tenant-store.js";
|
|
14
|
-
export type {
|
|
20
|
+
export type {
|
|
21
|
+
CreditCheckoutOpts,
|
|
22
|
+
PortalSessionOpts,
|
|
23
|
+
StripeBillingConfig,
|
|
24
|
+
TenantCustomerRow,
|
|
25
|
+
VpsCheckoutOpts,
|
|
26
|
+
} from "./types.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type Stripe from "stripe";
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { Credit } from "../../credits/credit.js";
|
|
4
3
|
import type { IAutoTopupEventLogRepository } from "../../credits/auto-topup-event-log-repository.js";
|
|
4
|
+
import { Credit } from "../../credits/credit.js";
|
|
5
5
|
import type { CreditTransaction, ICreditLedger } from "../../credits/credit-ledger.js";
|
|
6
6
|
import { PaymentMethodOwnershipError } from "../payment-processor.js";
|
|
7
7
|
import type { CreditPriceMap } from "./credit-prices.js";
|
|
@@ -309,7 +309,6 @@ describe("StripePaymentProcessor", () => {
|
|
|
309
309
|
tenantRepo: mocks.tenantRepo,
|
|
310
310
|
webhookSecret: "whsec_test",
|
|
311
311
|
creditLedger: mocks.creditLedger,
|
|
312
|
-
|
|
313
312
|
});
|
|
314
313
|
|
|
315
314
|
await expect(
|
|
@@ -447,7 +446,7 @@ describe("StripePaymentProcessor", () => {
|
|
|
447
446
|
tenantRepo: mocks.tenantRepo,
|
|
448
447
|
webhookSecret: "whsec_test",
|
|
449
448
|
creditLedger: mocks.creditLedger,
|
|
450
|
-
|
|
449
|
+
|
|
451
450
|
priceMap,
|
|
452
451
|
});
|
|
453
452
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type Stripe from "stripe";
|
|
2
|
-
import { Credit } from "../../credits/credit.js";
|
|
3
2
|
import { chargeAutoTopup } from "../../credits/auto-topup-charge.js";
|
|
4
3
|
import type { IAutoTopupEventLogRepository } from "../../credits/auto-topup-event-log-repository.js";
|
|
4
|
+
import { Credit } from "../../credits/credit.js";
|
|
5
5
|
import type { ICreditLedger } from "../../credits/credit-ledger.js";
|
|
6
6
|
import {
|
|
7
7
|
type ChargeOpts,
|
|
@@ -5,11 +5,11 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vites
|
|
|
5
5
|
import type { PlatformDb } from "../db/index.js";
|
|
6
6
|
import { creditAutoTopup } from "../db/schema/credit-auto-topup.js";
|
|
7
7
|
import { createTestDb, truncateAllTables } from "../test/db.js";
|
|
8
|
-
import { Credit } from "./credit.js";
|
|
9
|
-
import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
|
|
10
8
|
import { type AutoTopupChargeDeps, chargeAutoTopup, MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
|
|
11
9
|
import { DrizzleAutoTopupEventLogRepository } from "./auto-topup-event-log-repository.js";
|
|
10
|
+
import { Credit } from "./credit.js";
|
|
12
11
|
import { CreditLedger } from "./credit-ledger.js";
|
|
12
|
+
import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
|
|
13
13
|
|
|
14
14
|
function mockStripe(overrides?: {
|
|
15
15
|
paymentIntentId?: string;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import Stripe from "stripe";
|
|
2
2
|
import { logger } from "../config/logger.js";
|
|
3
|
-
import type { Credit } from "./credit.js";
|
|
4
|
-
import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
|
|
5
3
|
import type { IAutoTopupEventLogRepository } from "./auto-topup-event-log-repository.js";
|
|
4
|
+
import type { Credit } from "./credit.js";
|
|
6
5
|
import type { ICreditLedger } from "./credit-ledger.js";
|
|
6
|
+
import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
|
|
7
7
|
|
|
8
8
|
/** After this many consecutive Stripe failures, the auto-topup mode is disabled. */
|
|
9
9
|
export const MAX_CONSECUTIVE_FAILURES = 3;
|
|
@@ -2,9 +2,9 @@ import type { PGlite } from "@electric-sql/pglite";
|
|
|
2
2
|
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import type { PlatformDb } from "../db/index.js";
|
|
4
4
|
import { beginTestTransaction, createTestDb, endTestTransaction, rollbackTestTransaction } from "../test/db.js";
|
|
5
|
-
import { Credit } from "./credit.js";
|
|
6
5
|
import { runScheduledTopups, type ScheduleTopupDeps } from "./auto-topup-schedule.js";
|
|
7
6
|
import { DrizzleAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
|
|
7
|
+
import { Credit } from "./credit.js";
|
|
8
8
|
|
|
9
9
|
describe("runScheduledTopups", () => {
|
|
10
10
|
let pool: PGlite;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { logger } from "../config/logger.js";
|
|
2
|
-
import type { Credit } from "./credit.js";
|
|
3
2
|
import type { AutoTopupChargeResult } from "./auto-topup-charge.js";
|
|
4
3
|
import { MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
|
|
5
4
|
import type { IAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
|
|
5
|
+
import type { Credit } from "./credit.js";
|
|
6
6
|
|
|
7
7
|
export interface ScheduleTopupDeps {
|
|
8
8
|
settingsRepo: IAutoTopupSettingsRepository;
|
|
@@ -2,8 +2,8 @@ import type { PGlite } from "@electric-sql/pglite";
|
|
|
2
2
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
3
3
|
import type { PlatformDb } from "../db/index.js";
|
|
4
4
|
import { beginTestTransaction, createTestDb, endTestTransaction, rollbackTestTransaction } from "../test/db.js";
|
|
5
|
-
import { Credit } from "./credit.js";
|
|
6
5
|
import { DrizzleAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
|
|
6
|
+
import { Credit } from "./credit.js";
|
|
7
7
|
|
|
8
8
|
describe("DrizzleAutoTopupSettingsRepository", () => {
|
|
9
9
|
let pool: PGlite;
|
|
@@ -2,9 +2,9 @@ import type { PGlite } from "@electric-sql/pglite";
|
|
|
2
2
|
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import type { PlatformDb } from "../db/index.js";
|
|
4
4
|
import { createTestDb, truncateAllTables } from "../test/db.js";
|
|
5
|
-
import { Credit } from "./credit.js";
|
|
6
5
|
import { DrizzleAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
|
|
7
6
|
import { maybeTriggerUsageTopup, type UsageTopupDeps } from "./auto-topup-usage.js";
|
|
7
|
+
import { Credit } from "./credit.js";
|
|
8
8
|
import { CreditLedger } from "./credit-ledger.js";
|
|
9
9
|
|
|
10
10
|
describe("maybeTriggerUsageTopup", () => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { logger } from "../config/logger.js";
|
|
2
|
-
import type { Credit } from "./credit.js";
|
|
3
2
|
import type { AutoTopupChargeResult } from "./auto-topup-charge.js";
|
|
4
3
|
import { MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
|
|
5
4
|
import type { IAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
|
|
5
|
+
import type { Credit } from "./credit.js";
|
|
6
6
|
import type { ICreditLedger } from "./credit-ledger.js";
|
|
7
7
|
|
|
8
8
|
export interface UsageTopupDeps {
|
package/src/credits/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export {
|
|
|
9
9
|
computeNextScheduleAt,
|
|
10
10
|
DrizzleAutoTopupSettingsRepository,
|
|
11
11
|
} from "./auto-topup-settings-repository.js";
|
|
12
|
+
export { Credit } from "./credit.js";
|
|
12
13
|
export type { CreditExpiryCronConfig, CreditExpiryCronResult } from "./credit-expiry-cron.js";
|
|
13
14
|
export { runCreditExpiryCron } from "./credit-expiry-cron.js";
|
|
14
15
|
export type {
|
|
@@ -21,5 +22,4 @@ export type {
|
|
|
21
22
|
} from "./credit-ledger.js";
|
|
22
23
|
export { CreditLedger, DrizzleCreditLedger, InsufficientBalanceError } from "./credit-ledger.js";
|
|
23
24
|
export { grantSignupCredits, SIGNUP_GRANT } from "./signup-grant.js";
|
|
24
|
-
export { Credit } from "./credit.js";
|
|
25
25
|
export type { ITenantCustomerRepository, TenantCustomerRow } from "./tenant-customer-repository.js";
|
package/src/db/schema/index.ts
CHANGED
|
@@ -5,9 +5,9 @@ export * from "./admin-users.js";
|
|
|
5
5
|
export * from "./affiliate.js";
|
|
6
6
|
export * from "./affiliate-fraud.js";
|
|
7
7
|
export * from "./coupon-codes.js";
|
|
8
|
-
export * from "./credits.js";
|
|
9
8
|
export * from "./credit-auto-topup.js";
|
|
10
9
|
export * from "./credit-auto-topup-settings.js";
|
|
10
|
+
export * from "./credits.js";
|
|
11
11
|
export * from "./dividend-distributions.js";
|
|
12
12
|
export * from "./email-notifications.js";
|
|
13
13
|
export * from "./meter-events.js";
|
package/src/email/index.ts
CHANGED
|
@@ -20,12 +20,12 @@ export { DrizzleNotificationPreferencesStore } from "./notification-preferences-
|
|
|
20
20
|
export type { INotificationQueueRepository } from "./notification-queue-store.js";
|
|
21
21
|
export { DrizzleNotificationQueueStore } from "./notification-queue-store.js";
|
|
22
22
|
export type {
|
|
23
|
-
NotificationPrefs,
|
|
24
|
-
NotificationStatus,
|
|
25
|
-
QueuedNotification,
|
|
26
23
|
NotificationEmailType,
|
|
27
24
|
NotificationInput,
|
|
25
|
+
NotificationPrefs,
|
|
28
26
|
NotificationRow,
|
|
27
|
+
NotificationStatus,
|
|
28
|
+
QueuedNotification,
|
|
29
29
|
} from "./notification-repository-types.js";
|
|
30
30
|
export { NotificationService } from "./notification-service.js";
|
|
31
31
|
export type { TemplateName as NotificationTemplateName } from "./notification-templates.js";
|
package/src/index.ts
CHANGED
|
@@ -1,36 +1,32 @@
|
|
|
1
1
|
// Database
|
|
2
|
-
export type { PlatformDb, PlatformSchema } from "./db/index.js";
|
|
3
|
-
export { createDb, schema } from "./db/index.js";
|
|
4
2
|
|
|
5
3
|
// Admin
|
|
6
4
|
export * from "./admin/index.js";
|
|
7
|
-
|
|
8
5
|
// Auth
|
|
9
6
|
export * from "./auth/index.js";
|
|
10
|
-
|
|
11
7
|
// Billing (selective — ITenantCustomerRepository/TenantCustomerRow also in credits)
|
|
12
8
|
export {
|
|
13
|
-
PaymentMethodOwnershipError,
|
|
14
|
-
noOpReplayGuard,
|
|
15
|
-
DrizzleWebhookSeenRepository,
|
|
16
|
-
type SavedPaymentMethod,
|
|
17
|
-
type CheckoutOpts,
|
|
18
|
-
type CheckoutSession,
|
|
19
9
|
type ChargeOpts,
|
|
20
10
|
type ChargeResult,
|
|
21
|
-
type
|
|
22
|
-
type
|
|
23
|
-
|
|
24
|
-
type IPaymentProcessor,
|
|
11
|
+
type CheckoutOpts,
|
|
12
|
+
type CheckoutSession,
|
|
13
|
+
DrizzleWebhookSeenRepository,
|
|
25
14
|
type Invoice,
|
|
15
|
+
type IPaymentProcessor,
|
|
26
16
|
type IWebhookSeenRepository,
|
|
17
|
+
noOpReplayGuard,
|
|
18
|
+
PaymentMethodOwnershipError,
|
|
19
|
+
type PortalOpts,
|
|
20
|
+
type SavedPaymentMethod,
|
|
21
|
+
type SetupResult,
|
|
22
|
+
type WebhookResult,
|
|
27
23
|
} from "./billing/index.js";
|
|
28
|
-
|
|
29
24
|
// Config
|
|
30
|
-
export {
|
|
31
|
-
|
|
25
|
+
export { billingConfigSchema, config, type PlatformConfig } from "./config/index.js";
|
|
32
26
|
// Credits
|
|
33
27
|
export * from "./credits/index.js";
|
|
28
|
+
export type { PlatformDb, PlatformSchema } from "./db/index.js";
|
|
29
|
+
export { createDb, schema } from "./db/index.js";
|
|
34
30
|
|
|
35
31
|
// Email
|
|
36
32
|
export * from "./email/index.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import type { PGlite } from "@electric-sql/pglite";
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
4
|
+
import { Credit } from "../credits/credit.js";
|
|
4
5
|
import type { PlatformDb } from "../db/index.js";
|
|
5
6
|
import { meterEvents, usageSummaries } from "../db/schema/meter-events.js";
|
|
6
7
|
import { createTestDb } from "../test/db.js";
|
|
7
|
-
import { Credit } from "../credits/credit.js";
|
|
8
8
|
import { DrizzleMeterAggregator } from "./aggregator.js";
|
|
9
9
|
import { DrizzleUsageSummaryRepository } from "./drizzle-usage-summary-repository.js";
|
|
10
10
|
|
|
@@ -3,9 +3,9 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import type { PGlite } from "@electric-sql/pglite";
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
6
|
+
import { Credit } from "../credits/credit.js";
|
|
6
7
|
import type { PlatformDb } from "../db/index.js";
|
|
7
8
|
import { beginTestTransaction, createTestDb, endTestTransaction, rollbackTestTransaction } from "../test/db.js";
|
|
8
|
-
import { Credit } from "../credits/credit.js";
|
|
9
9
|
import { MeterDLQ } from "./dlq.js";
|
|
10
10
|
import { DrizzleMeterEmitter } from "./emitter.js";
|
|
11
11
|
import { DrizzleMeterEventRepository } from "./meter-event-repository.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { unlinkSync } from "node:fs";
|
|
2
2
|
import type { PGlite } from "@electric-sql/pglite";
|
|
3
3
|
import { afterEach, beforeEach, bench, describe } from "vitest";
|
|
4
|
+
import { Credit } from "../credits/credit.js";
|
|
4
5
|
import type { PlatformDb } from "../db/index.js";
|
|
5
6
|
import { createTestDb } from "../test/db.js";
|
|
6
|
-
import { Credit } from "../credits/credit.js";
|
|
7
7
|
import { MeterAggregator } from "./aggregator.js";
|
|
8
8
|
import { DrizzleUsageSummaryRepository } from "./drizzle-usage-summary-repository.js";
|
|
9
9
|
import { MeterEmitter } from "./emitter.js";
|
|
@@ -2,10 +2,10 @@ import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import type { PGlite } from "@electric-sql/pglite";
|
|
3
3
|
import { eq, sql } from "drizzle-orm";
|
|
4
4
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { Credit } from "../credits/credit.js";
|
|
5
6
|
import type { PlatformDb } from "../db/index.js";
|
|
6
7
|
import { meterEvents } from "../db/schema/meter-events.js";
|
|
7
8
|
import { createTestDb, truncateAllTables } from "../test/db.js";
|
|
8
|
-
import { Credit } from "../credits/credit.js";
|
|
9
9
|
import { MeterAggregator } from "./aggregator.js";
|
|
10
10
|
import { DrizzleUsageSummaryRepository } from "./drizzle-usage-summary-repository.js";
|
|
11
11
|
import { MeterEmitter } from "./emitter.js";
|