@payez/next-mvp 4.0.47 → 4.1.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/dist/api-handlers/admin/stats.js +24 -14
- package/dist/auth/better-auth.d.ts +122 -2
- package/dist/auth/better-auth.js +24 -2
- package/dist/client/better-auth-client.d.ts +216 -216
- package/dist/lib/session-store.js +21 -21
- package/dist/server/auth.d.ts +96 -1
- package/dist/vibe/hooks/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/api-handlers/admin/stats.ts +249 -238
- package/src/auth/better-auth.ts +408 -368
- package/src/lib/session-store.ts +689 -689
- package/src/server/auth.ts +78 -78
- package/src/server/decode-session.ts +200 -200
|
@@ -128,14 +128,16 @@ function createStatsHandler(config) {
|
|
|
128
128
|
if (adminCheck.error)
|
|
129
129
|
return adminCheck.error;
|
|
130
130
|
try {
|
|
131
|
-
// Fetch from
|
|
132
|
-
const [usersResult, sessionCount, auditResult] = await Promise.allSettled([
|
|
133
|
-
// 1. Users
|
|
131
|
+
// Fetch from 4 sources in parallel
|
|
132
|
+
const [usersResult, tierDistributionResult, sessionCount, auditResult] = await Promise.allSettled([
|
|
133
|
+
// 1. Users count via HMAC proxy (Vibe collection query)
|
|
134
134
|
vibeServiceRequest('/v1/collections/vibe_app/tables/users/query', {
|
|
135
135
|
method: 'POST',
|
|
136
136
|
body: { page: 1, pageSize: 500, orderBy: 'created_at', orderDirection: 'desc' },
|
|
137
137
|
}),
|
|
138
|
-
// 2.
|
|
138
|
+
// 2. Tier distribution from analytics endpoint (uses purchases table)
|
|
139
|
+
vibeServiceRequest('/v1/analytics/tier-distribution?includeTrend=false', { method: 'GET' }),
|
|
140
|
+
// 3. Active sessions from Redis
|
|
139
141
|
(async () => {
|
|
140
142
|
const redis = (0, redis_1.getRedis)();
|
|
141
143
|
const sessionPrefix = getSessionPrefix();
|
|
@@ -148,12 +150,11 @@ function createStatsHandler(config) {
|
|
|
148
150
|
} while (cursor !== '0');
|
|
149
151
|
return sessionKeys.length;
|
|
150
152
|
})(),
|
|
151
|
-
//
|
|
153
|
+
// 4. Recent audit activity via HMAC proxy
|
|
152
154
|
vibeServiceRequest('/v1/audit?pageSize=10&sortDir=desc', { method: 'GET' }),
|
|
153
155
|
]);
|
|
154
156
|
// Parse users — deduplicate by user_id
|
|
155
157
|
let totalUsers = 0;
|
|
156
|
-
let tierBreakdown = {};
|
|
157
158
|
if (usersResult.status === 'fulfilled' && usersResult.value.ok && usersResult.value.data) {
|
|
158
159
|
const data = usersResult.value.data;
|
|
159
160
|
const rawUsers = data.data || data.documents || data.users || [];
|
|
@@ -166,17 +167,26 @@ function createStatsHandler(config) {
|
|
|
166
167
|
userMap.set(uid, u);
|
|
167
168
|
}
|
|
168
169
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
totalUsers = userMap.size;
|
|
171
|
+
}
|
|
172
|
+
// Parse tier distribution from analytics endpoint (uses purchases table)
|
|
173
|
+
let tierBreakdown = {};
|
|
174
|
+
if (tierDistributionResult.status === 'fulfilled' && tierDistributionResult.value.ok && tierDistributionResult.value.data) {
|
|
175
|
+
const data = tierDistributionResult.value.data;
|
|
176
|
+
// Handle response shape: { distribution: [{ tierKey, userCount }, ...] }
|
|
177
|
+
const distribution = data.distribution || data.data || data.tiers || [];
|
|
178
|
+
if (Array.isArray(distribution)) {
|
|
179
|
+
for (const item of distribution) {
|
|
180
|
+
const tierKey = item.tierKey || item.tier || item.name || 'free';
|
|
181
|
+
const count = item.userCount || item.count || item.users || 0;
|
|
182
|
+
tierBreakdown[tierKey] = (tierBreakdown[tierKey] || 0) + count;
|
|
177
183
|
}
|
|
178
184
|
}
|
|
179
185
|
}
|
|
186
|
+
// Fallback: if no tier data from analytics, show all as free
|
|
187
|
+
if (Object.keys(tierBreakdown).length === 0) {
|
|
188
|
+
tierBreakdown = { free: totalUsers };
|
|
189
|
+
}
|
|
180
190
|
// Parse active sessions count
|
|
181
191
|
let activeSessions = 0;
|
|
182
192
|
if (sessionCount.status === 'fulfilled') {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @see BETTER-AUTH-MIGRATION-SPEC.md
|
|
10
10
|
*/
|
|
11
11
|
import 'server-only';
|
|
12
|
+
import { type MagicLinkOptions } from 'better-auth/plugins/magic-link';
|
|
12
13
|
import type { IDPClientConfig } from '../lib/idp-client-config';
|
|
13
14
|
/**
|
|
14
15
|
* Better Auth social provider config shape.
|
|
@@ -22,13 +23,25 @@ export interface BetterAuthSocialProvider {
|
|
|
22
23
|
* Build Better Auth social providers from IDP config.
|
|
23
24
|
*/
|
|
24
25
|
export declare function buildBetterAuthProviders(config: IDPClientConfig): Record<string, BetterAuthSocialProvider>;
|
|
26
|
+
/**
|
|
27
|
+
* Optional configuration for `createBetterAuthInstance`.
|
|
28
|
+
*
|
|
29
|
+
* - `magicLink`: if provided, registers Better Auth's magic-link plugin.
|
|
30
|
+
* The host app supplies its own `sendMagicLink` callback — typically a
|
|
31
|
+
* fetch to its email service (e.g. ACP's `/v1/auth/magic-link/email`).
|
|
32
|
+
* Omit the `magicLink` key entirely to skip the plugin; the consuming
|
|
33
|
+
* app will not have a magic-link flow.
|
|
34
|
+
*/
|
|
35
|
+
export interface CreateBetterAuthInstanceOptions {
|
|
36
|
+
magicLink?: MagicLinkOptions;
|
|
37
|
+
}
|
|
25
38
|
/**
|
|
26
39
|
* Create Better Auth instance from IDP config.
|
|
27
40
|
*
|
|
28
41
|
* No database — runs in stateless mode with JWE cookie cache.
|
|
29
42
|
* Call after getIDPClientConfig() resolves.
|
|
30
43
|
*/
|
|
31
|
-
export declare function createBetterAuthInstance(idpConfig: IDPClientConfig): import("better-auth").Auth<{
|
|
44
|
+
export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opts?: CreateBetterAuthInstanceOptions): import("better-auth").Auth<{
|
|
32
45
|
baseURL: string;
|
|
33
46
|
secret: string;
|
|
34
47
|
socialProviders: Record<string, BetterAuthSocialProvider>;
|
|
@@ -65,7 +78,102 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig): im
|
|
|
65
78
|
handler: (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>;
|
|
66
79
|
}[];
|
|
67
80
|
};
|
|
68
|
-
}
|
|
81
|
+
}, ...{
|
|
82
|
+
id: "magic-link";
|
|
83
|
+
endpoints: {
|
|
84
|
+
signInMagicLink: import("better-auth").StrictEndpoint<"/sign-in/magic-link", {
|
|
85
|
+
method: "POST";
|
|
86
|
+
requireHeaders: true;
|
|
87
|
+
body: import("better-auth").ZodObject<{
|
|
88
|
+
email: import("better-auth").ZodEmail;
|
|
89
|
+
name: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
90
|
+
callbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
91
|
+
newUserCallbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
92
|
+
errorCallbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
93
|
+
metadata: import("better-auth").ZodOptional<import("better-auth").ZodRecord<import("better-auth").ZodString, import("better-auth").ZodAny>>;
|
|
94
|
+
}, import("better-auth").$strip>;
|
|
95
|
+
metadata: {
|
|
96
|
+
openapi: {
|
|
97
|
+
operationId: string;
|
|
98
|
+
description: string;
|
|
99
|
+
responses: {
|
|
100
|
+
200: {
|
|
101
|
+
description: string;
|
|
102
|
+
content: {
|
|
103
|
+
"application/json": {
|
|
104
|
+
schema: {
|
|
105
|
+
type: "object";
|
|
106
|
+
properties: {
|
|
107
|
+
status: {
|
|
108
|
+
type: string;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
}, {
|
|
119
|
+
status: boolean;
|
|
120
|
+
}>;
|
|
121
|
+
magicLinkVerify: import("better-auth").StrictEndpoint<"/magic-link/verify", {
|
|
122
|
+
method: "GET";
|
|
123
|
+
query: import("better-auth").ZodObject<{
|
|
124
|
+
token: import("better-auth").ZodString;
|
|
125
|
+
callbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
126
|
+
errorCallbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
127
|
+
newUserCallbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
128
|
+
}, import("better-auth").$strip>;
|
|
129
|
+
use: ((inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>)[];
|
|
130
|
+
requireHeaders: true;
|
|
131
|
+
metadata: {
|
|
132
|
+
openapi: {
|
|
133
|
+
operationId: string;
|
|
134
|
+
description: string;
|
|
135
|
+
responses: {
|
|
136
|
+
200: {
|
|
137
|
+
description: string;
|
|
138
|
+
content: {
|
|
139
|
+
"application/json": {
|
|
140
|
+
schema: {
|
|
141
|
+
type: "object";
|
|
142
|
+
properties: {
|
|
143
|
+
session: {
|
|
144
|
+
$ref: string;
|
|
145
|
+
};
|
|
146
|
+
user: {
|
|
147
|
+
$ref: string;
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
}, {
|
|
158
|
+
token: string;
|
|
159
|
+
user: {
|
|
160
|
+
id: string;
|
|
161
|
+
createdAt: Date;
|
|
162
|
+
updatedAt: Date;
|
|
163
|
+
email: string;
|
|
164
|
+
emailVerified: boolean;
|
|
165
|
+
name: string;
|
|
166
|
+
image?: string | null | undefined;
|
|
167
|
+
};
|
|
168
|
+
}>;
|
|
169
|
+
};
|
|
170
|
+
rateLimit: {
|
|
171
|
+
pathMatcher(path: string): boolean;
|
|
172
|
+
window: number;
|
|
173
|
+
max: number;
|
|
174
|
+
}[];
|
|
175
|
+
options: MagicLinkOptions;
|
|
176
|
+
}[]];
|
|
69
177
|
}>;
|
|
70
178
|
/**
|
|
71
179
|
* Better Auth is always enabled (NextAuth removed in 4.0).
|
|
@@ -77,6 +185,18 @@ export declare function isBetterAuthEnabled(): boolean;
|
|
|
77
185
|
*/
|
|
78
186
|
declare let cachedInstance: any;
|
|
79
187
|
export { cachedInstance as __betterAuthInstance };
|
|
188
|
+
/**
|
|
189
|
+
* Configure Better Auth instance options for this process.
|
|
190
|
+
*
|
|
191
|
+
* Must be called before the first auth request — before
|
|
192
|
+
* `getBetterAuthInstance()` caches an instance. Typically called once at
|
|
193
|
+
* app startup, e.g. from Next.js `instrumentation.ts` or an equivalent
|
|
194
|
+
* server bootstrap hook.
|
|
195
|
+
*
|
|
196
|
+
* Throws if called after the instance has already been resolved: options
|
|
197
|
+
* cannot be applied retroactively.
|
|
198
|
+
*/
|
|
199
|
+
export declare function configureBetterAuth(opts: CreateBetterAuthInstanceOptions): void;
|
|
80
200
|
export declare function getBetterAuthInstance(): Promise<any>;
|
|
81
201
|
/**
|
|
82
202
|
* Get flag-gated auth handler for Next.js route.
|
package/dist/auth/better-auth.js
CHANGED
|
@@ -47,6 +47,7 @@ exports.__betterAuthInstance = void 0;
|
|
|
47
47
|
exports.buildBetterAuthProviders = buildBetterAuthProviders;
|
|
48
48
|
exports.createBetterAuthInstance = createBetterAuthInstance;
|
|
49
49
|
exports.isBetterAuthEnabled = isBetterAuthEnabled;
|
|
50
|
+
exports.configureBetterAuth = configureBetterAuth;
|
|
50
51
|
exports.getBetterAuthInstance = getBetterAuthInstance;
|
|
51
52
|
exports.getBetterAuthHandler = getBetterAuthHandler;
|
|
52
53
|
exports.exchangeOAuthForIdpTokens = exchangeOAuthForIdpTokens;
|
|
@@ -55,6 +56,7 @@ require("server-only");
|
|
|
55
56
|
const better_auth_1 = require("better-auth");
|
|
56
57
|
const next_js_1 = require("better-auth/next-js");
|
|
57
58
|
const next_js_2 = require("better-auth/next-js");
|
|
59
|
+
const magic_link_1 = require("better-auth/plugins/magic-link");
|
|
58
60
|
const idp_client_config_1 = require("../lib/idp-client-config");
|
|
59
61
|
const app_slug_1 = require("../lib/app-slug");
|
|
60
62
|
const redis_1 = require("../lib/redis");
|
|
@@ -81,7 +83,7 @@ function buildBetterAuthProviders(config) {
|
|
|
81
83
|
* No database — runs in stateless mode with JWE cookie cache.
|
|
82
84
|
* Call after getIDPClientConfig() resolves.
|
|
83
85
|
*/
|
|
84
|
-
function createBetterAuthInstance(idpConfig) {
|
|
86
|
+
function createBetterAuthInstance(idpConfig, opts = {}) {
|
|
85
87
|
const appSlug = idpConfig.clientSlug || (0, app_slug_1.getAppSlug)();
|
|
86
88
|
// Resolve base URL: BETTER_AUTH_URL env > IDP config > localhost fallback
|
|
87
89
|
// Must include /api/auth since that's where the catch-all route is mounted
|
|
@@ -149,6 +151,7 @@ function createBetterAuthInstance(idpConfig) {
|
|
|
149
151
|
},
|
|
150
152
|
plugins: [
|
|
151
153
|
(0, next_js_1.nextCookies)(),
|
|
154
|
+
...(opts.magicLink ? [(0, magic_link_1.magicLink)(opts.magicLink)] : []),
|
|
152
155
|
],
|
|
153
156
|
});
|
|
154
157
|
}
|
|
@@ -167,12 +170,31 @@ let cachedInstance = null;
|
|
|
167
170
|
exports.__betterAuthInstance = cachedInstance;
|
|
168
171
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
169
172
|
let initPromise = null;
|
|
173
|
+
let configuredOpts = {};
|
|
174
|
+
/**
|
|
175
|
+
* Configure Better Auth instance options for this process.
|
|
176
|
+
*
|
|
177
|
+
* Must be called before the first auth request — before
|
|
178
|
+
* `getBetterAuthInstance()` caches an instance. Typically called once at
|
|
179
|
+
* app startup, e.g. from Next.js `instrumentation.ts` or an equivalent
|
|
180
|
+
* server bootstrap hook.
|
|
181
|
+
*
|
|
182
|
+
* Throws if called after the instance has already been resolved: options
|
|
183
|
+
* cannot be applied retroactively.
|
|
184
|
+
*/
|
|
185
|
+
function configureBetterAuth(opts) {
|
|
186
|
+
if (cachedInstance) {
|
|
187
|
+
throw new Error('[BETTER_AUTH] configureBetterAuth() must run before the instance is first resolved. ' +
|
|
188
|
+
'Call it in Next.js instrumentation.ts or an equivalent startup hook.');
|
|
189
|
+
}
|
|
190
|
+
configuredOpts = opts;
|
|
191
|
+
}
|
|
170
192
|
async function getBetterAuthInstance() {
|
|
171
193
|
if (cachedInstance)
|
|
172
194
|
return cachedInstance;
|
|
173
195
|
if (!initPromise) {
|
|
174
196
|
initPromise = (0, idp_client_config_1.getIDPClientConfig)(true).then(config => {
|
|
175
|
-
const instance = createBetterAuthInstance(config);
|
|
197
|
+
const instance = createBetterAuthInstance(config, configuredOpts);
|
|
176
198
|
exports.__betterAuthInstance = cachedInstance = instance;
|
|
177
199
|
console.log('[BETTER_AUTH] Instance created for', config.clientSlug || config.clientId);
|
|
178
200
|
return instance;
|