better-auth-studio 1.0.6 → 1.0.8

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.
Files changed (51) hide show
  1. package/dist/config.d.ts +1 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +126 -29
  4. package/dist/config.js.map +1 -1
  5. package/dist/routes.d.ts.map +1 -1
  6. package/dist/routes.js +28 -2
  7. package/dist/routes.js.map +1 -1
  8. package/package.json +8 -1
  9. package/frontend/index.html +0 -13
  10. package/frontend/package-lock.json +0 -4675
  11. package/frontend/package.json +0 -52
  12. package/frontend/pnpm-lock.yaml +0 -4020
  13. package/frontend/postcss.config.js +0 -6
  14. package/frontend/src/App.tsx +0 -36
  15. package/frontend/src/components/CommandPalette.tsx +0 -219
  16. package/frontend/src/components/Layout.tsx +0 -159
  17. package/frontend/src/components/ui/badge.tsx +0 -40
  18. package/frontend/src/components/ui/button.tsx +0 -53
  19. package/frontend/src/components/ui/card.tsx +0 -78
  20. package/frontend/src/components/ui/input.tsx +0 -20
  21. package/frontend/src/components/ui/label.tsx +0 -19
  22. package/frontend/src/components/ui/select.tsx +0 -71
  23. package/frontend/src/index.css +0 -130
  24. package/frontend/src/lib/utils.ts +0 -6
  25. package/frontend/src/main.tsx +0 -10
  26. package/frontend/src/pages/Dashboard.tsx +0 -231
  27. package/frontend/src/pages/OrganizationDetails.tsx +0 -1281
  28. package/frontend/src/pages/Organizations.tsx +0 -874
  29. package/frontend/src/pages/Sessions.tsx +0 -623
  30. package/frontend/src/pages/Settings.tsx +0 -1019
  31. package/frontend/src/pages/TeamDetails.tsx +0 -666
  32. package/frontend/src/pages/Users.tsx +0 -728
  33. package/frontend/tailwind.config.js +0 -75
  34. package/frontend/tsconfig.json +0 -31
  35. package/frontend/tsconfig.node.json +0 -10
  36. package/frontend/vite.config.ts +0 -31
  37. package/src/auth-adapter.ts +0 -473
  38. package/src/cli.ts +0 -51
  39. package/src/config.ts +0 -320
  40. package/src/data.ts +0 -351
  41. package/src/routes.ts +0 -1585
  42. package/src/studio.ts +0 -86
  43. package/test-project/README.md +0 -0
  44. package/test-project/better-auth.db +0 -0
  45. package/test-project/better-auth_migrations/2025-08-27T15-55-04.099Z.sql +0 -7
  46. package/test-project/better-auth_migrations/2025-09-04T02-33-19.422Z.sql +0 -7
  47. package/test-project/package.json +0 -29
  48. package/test-project/pnpm-lock.yaml +0 -1728
  49. package/test-project/src/auth.ts +0 -47
  50. package/test-project/src/index.ts +0 -40
  51. package/tsconfig.json +0 -21
package/src/config.ts DELETED
@@ -1,320 +0,0 @@
1
- import { readFileSync, existsSync } from 'fs';
2
- import { join, dirname } from 'path';
3
-
4
- export interface AuthProvider {
5
- type: string;
6
- clientId?: string;
7
- clientSecret?: string;
8
- redirectUri?: string;
9
- [key: string]: any;
10
- }
11
-
12
- export interface AuthDatabase {
13
- url?: string;
14
- type?: string;
15
- dialect?: string;
16
- [key: string]: any;
17
- }
18
-
19
- export interface AuthConfig {
20
- database?: AuthDatabase;
21
- providers?: AuthProvider[];
22
- socialProviders?: Record<string, any>;
23
- emailAndPassword?: any;
24
- session?: any;
25
- secret?: string;
26
- rateLimit?: any;
27
- [key: string]: any;
28
- }
29
-
30
- export async function findAuthConfig(): Promise<AuthConfig | null> {
31
- const possibleConfigFiles = [
32
- 'studio-config.json',
33
- 'auth.ts',
34
- 'auth.js',
35
- 'src/auth.ts',
36
- 'src/auth.js',
37
- 'lib/auth.ts',
38
- 'lib/auth.js',
39
- 'better-auth.config.ts',
40
- 'better-auth.config.js',
41
- 'better-auth.config.json',
42
- 'auth.config.ts',
43
- 'auth.config.js',
44
- 'auth.config.json'
45
- ];
46
-
47
- let currentDir = process.cwd();
48
- const maxDepth = 10;
49
- let depth = 0;
50
-
51
- while (currentDir && depth < maxDepth) {
52
- for (const configFile of possibleConfigFiles) {
53
- const configPath = join(currentDir, configFile);
54
-
55
- if (existsSync(configPath)) {
56
- try {
57
- const config = await loadConfig(configPath);
58
- if (config) {
59
- return config;
60
- }
61
- } catch (error) {
62
- console.warn(`Failed to load config from ${configPath}:`, error);
63
- }
64
- }
65
- }
66
-
67
- const parentDir = dirname(currentDir);
68
- if (parentDir === currentDir) {
69
- break;
70
- }
71
- currentDir = parentDir;
72
- depth++;
73
- }
74
-
75
- return null;
76
- }
77
-
78
- async function loadConfig(configPath: string): Promise<AuthConfig | null> {
79
- const ext = configPath.split('.').pop();
80
-
81
- try {
82
- if (ext === 'json') {
83
- const content = readFileSync(configPath, 'utf-8');
84
- return JSON.parse(content);
85
- } else if (ext === 'js' || ext === 'ts') {
86
- return await loadTypeScriptConfig(configPath);
87
- }
88
- } catch (error) {
89
- console.warn(`Error loading config from ${configPath}:`, error);
90
- }
91
-
92
- return null;
93
- }
94
-
95
- async function loadTypeScriptConfig(configPath: string): Promise<AuthConfig | null> {
96
- try {
97
- if (configPath.endsWith('.ts')) {
98
- try {
99
- const authModule = await import(configPath);
100
-
101
- if (authModule.auth) {
102
- console.log('Found auth export, extracting configuration...');
103
- const config = authModule.auth.options || authModule.auth;
104
- return extractBetterAuthFields(config);
105
- } else if (authModule.default) {
106
- console.log('Found default export, extracting configuration...');
107
- const config = authModule.default.options || authModule.default;
108
- return extractBetterAuthFields(config);
109
- }
110
- } catch (importError) {
111
- console.warn(`Failed to import auth config from ${configPath}:`, importError);
112
- console.log('Falling back to regex extraction...');
113
- }
114
- }
115
-
116
- const content = readFileSync(configPath, 'utf-8');
117
- const authConfig = extractBetterAuthConfig(content);
118
- if (authConfig) {
119
- return authConfig;
120
- }
121
-
122
- if (configPath.endsWith('.js')) {
123
- return await evaluateJSConfig(configPath);
124
- }
125
-
126
- return null;
127
- } catch (error) {
128
- console.warn(`Error loading TypeScript config from ${configPath}:`, error);
129
- return null;
130
- }
131
- }
132
-
133
- function extractBetterAuthConfig(content: string): AuthConfig | null {
134
- console.log('Extracting config from content:', content.substring(0, 500) + '...');
135
-
136
- const patterns = [
137
- /export\s+const\s+\w+\s*=\s*betterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
138
- /export\s+const\s+\w+\s*=\s*BetterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
139
- /const\s+\w+\s*=\s*betterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
140
- /const\s+\w+\s*=\s*BetterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
141
- /export\s+default\s+betterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
142
- /export\s+default\s+BetterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
143
- /module\.exports\s*=\s*betterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
144
- /module\.exports\s*=\s*BetterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
145
- /export\s+default\s*({[\s\S]*?});?$/m,
146
- /module\.exports\s*=\s*({[\s\S]*?});?$/m,
147
- /betterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
148
- /BetterAuth\s*\(\s*({[\s\S]*?})\s*\)/,
149
- /({[\s\S]*?"socialProviders"[\s\S]*?})/,
150
- /({[\s\S]*?"emailAndPassword"[\s\S]*?})/,
151
- /({[\s\S]*?"database"[\s\S]*?})/
152
- ];
153
-
154
- for (let i = 0; i < patterns.length; i++) {
155
- const pattern = patterns[i];
156
- const match = content.match(pattern);
157
- if (match) {
158
- console.log(`Pattern ${i + 1} matched!`);
159
- console.log('Matched content:', match[1].substring(0, 200) + '...');
160
- try {
161
- let configStr = match[1];
162
-
163
- configStr = configStr
164
- .replace(/(\d+\s*\*\s*\d+\s*\*\s*\d+\s*\*\s*\d+)/g, (match) => {
165
- try {
166
- return eval(match).toString();
167
- } catch {
168
- return match;
169
- }
170
- })
171
- .replace(/(\d+\s*\*\s*\d+\s*\*\s*\d+)/g, (match) => {
172
- try {
173
- return eval(match).toString();
174
- } catch {
175
- return match;
176
- }
177
- })
178
- .replace(/(\d+\s*\*\s*\d+)/g, (match) => {
179
- try {
180
- return eval(match).toString();
181
- } catch {
182
- return match;
183
- }
184
- });
185
-
186
- configStr = configStr
187
- .replace(/:\s*process\.env\.(\w+)(\s*\|\|\s*"[^"]*")?/g, ':"$1"') // Replace process.env.VAR || "default" with "VAR"
188
- .replace(/:\s*`([^`]*)`/g, ':"$1"') // Replace template literals
189
- .replace(/:\s*'([^']*)'/g, ':"$1"') // Replace single quotes
190
- .replace(/:\s*"([^"]*)"/g, ':"$1"') // Keep double quotes
191
- .replace(/:\s*(\w+)/g, ':"$1"') // Replace unquoted keys
192
- .replace(/(\w+):/g, '"$1":') // Quote property names
193
- .replace(/,\s*}/g, '}') // Remove trailing commas
194
- .replace(/,\s*]/g, ']') // Remove trailing commas in arrays
195
- .replace(/\/\/.*$/gm, '') // Remove single-line comments
196
- .replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments
197
- .replace(/\s+/g, ' ') // Normalize whitespace
198
- .replace(/:\s*(\d+)/g, ':$1') // Keep numbers
199
- .replace(/:\s*(true|false)/g, ':$1') // Keep booleans
200
- .replace(/:\s*null/g, ':null') // Keep null
201
- .trim();
202
-
203
- console.log('Cleaned config string:', configStr.substring(0, 300) + '...');
204
-
205
- let config;
206
- try {
207
- config = JSON.parse(configStr);
208
- } catch (error) {
209
- console.warn(`Failed to parse config: ${error instanceof Error ? error.message : 'Unknown error'}`);
210
- console.warn('Config string that failed:', configStr.substring(0, 200) + '...');
211
- return null;
212
- }
213
-
214
- return extractBetterAuthFields(config);
215
- } catch (error) {
216
- console.warn(`Failed to parse config pattern: ${error instanceof Error ? error.message : 'Unknown error'}`);
217
- continue;
218
- }
219
- }
220
- }
221
-
222
- return null;
223
- }
224
-
225
- function extractBetterAuthFields(config: any): AuthConfig {
226
- console.log('Extracting fields from config:', JSON.stringify(config, null, 2));
227
-
228
- const authConfig: AuthConfig = {};
229
-
230
- if (config.database) {
231
- let dbType = 'postgresql'; // default
232
- let dbName = config.database.name;
233
-
234
- if (config.database.constructor && config.database.constructor.name === 'Database') {
235
- dbType = 'sqlite';
236
- dbName = config.database.name || './better-auth.db';
237
- } else if (config.database.name && config.database.name.endsWith('.db')) {
238
- dbType = 'sqlite';
239
- } else if (config.database.type) {
240
- dbType = config.database.type;
241
- } else if (config.database.dialect) {
242
- dbType = config.database.dialect;
243
- }
244
-
245
- authConfig.database = {
246
- url: config.database.url || config.database.connectionString,
247
- name: dbName,
248
- type: dbType,
249
- dialect: config.database.dialect,
250
- casing: config.database.casing
251
- };
252
- }
253
-
254
- if (config.socialProviders) {
255
- if (typeof config.socialProviders === 'object' && !Array.isArray(config.socialProviders)) {
256
- authConfig.socialProviders = config.socialProviders;
257
- authConfig.providers = Object.entries(config.socialProviders).map(([provider, config]: [string, any]) => ({
258
- type: provider,
259
- clientId: config.clientId,
260
- clientSecret: config.clientSecret,
261
- redirectUri: config.redirectUri,
262
- ...config
263
- }));
264
- } else if (Array.isArray(config.socialProviders)) {
265
- authConfig.socialProviders = config.socialProviders;
266
- authConfig.providers = config.socialProviders;
267
- }
268
- }
269
-
270
- if (config.providers && Array.isArray(config.providers)) {
271
- authConfig.providers = config.providers.map((provider: any) => ({
272
- type: provider.type || provider.id,
273
- clientId: provider.clientId || provider.client_id,
274
- clientSecret: provider.clientSecret || provider.client_secret,
275
- ...provider
276
- }));
277
- }
278
-
279
- if (config.emailAndPassword) {
280
- authConfig.emailAndPassword = config.emailAndPassword;
281
- }
282
-
283
- if (config.session) {
284
- authConfig.session = config.session;
285
- }
286
-
287
- if (config.secret) {
288
- authConfig.secret = config.secret;
289
- }
290
-
291
- if (config.rateLimit) {
292
- authConfig.rateLimit = config.rateLimit;
293
- }
294
-
295
- if (config.telemetry) {
296
- authConfig.telemetry = config.telemetry;
297
- }
298
- if(config.plugins) {
299
- authConfig.plugins = config.plugins.map((plugin: any) => plugin.id);
300
- }
301
- console.log('Extracted auth config:', JSON.stringify(authConfig, null, 2));
302
- return authConfig;
303
- }
304
-
305
- async function evaluateJSConfig(configPath: string): Promise<AuthConfig | null> {
306
- try {
307
- const config = require(configPath);
308
-
309
- if (config.default) {
310
- return extractBetterAuthFields(config.default);
311
- } else if (typeof config === 'object') {
312
- return extractBetterAuthFields(config);
313
- }
314
-
315
- return null;
316
- } catch (error) {
317
- console.warn(`Error evaluating JS config from ${configPath}:`, error);
318
- return null;
319
- }
320
- }
package/src/data.ts DELETED
@@ -1,351 +0,0 @@
1
- import { AuthConfig } from './config';
2
- import { getAuthAdapter } from './auth-adapter';
3
-
4
- export interface User {
5
- id: string;
6
- email?: string;
7
- name?: string;
8
- image?: string;
9
- emailVerified?: Date;
10
- createdAt: Date;
11
- updatedAt: Date;
12
- provider?: string;
13
- lastSignIn?: Date;
14
- }
15
-
16
- export interface Session {
17
- id: string;
18
- userId: string;
19
- expires: Date;
20
- createdAt: Date;
21
- userAgent?: string;
22
- ip?: string;
23
- }
24
-
25
- export interface AuthStats {
26
- totalUsers: number;
27
- activeUsers: number;
28
- totalSessions: number;
29
- activeSessions: number;
30
- usersByProvider: Record<string, number>;
31
- recentSignups: User[];
32
- recentLogins: Session[];
33
- }
34
-
35
- export interface PaginatedResult<T> {
36
- data: T[];
37
- total: number;
38
- page: number;
39
- limit: number;
40
- totalPages: number;
41
- }
42
-
43
- export async function getAuthData(
44
- authConfig: AuthConfig,
45
- type: 'stats' | 'users' | 'sessions' | 'providers' | 'deleteUser' | 'updateUser' = 'stats',
46
- options?: any
47
- ): Promise<any> {
48
- try {
49
- const adapter = await getAuthAdapter();
50
-
51
- if (!adapter) {
52
- console.log('No adapter available, falling back to mock data');
53
- return getMockData(type, options);
54
- }
55
-
56
- switch (type) {
57
- case 'stats':
58
- return await getRealStats(adapter);
59
- case 'users':
60
- return await getRealUsers(adapter, options);
61
- case 'sessions':
62
- return await getRealSessions(adapter, options);
63
- case 'providers':
64
- return await getRealProviderStats(adapter);
65
- case 'deleteUser':
66
- return await deleteRealUser(adapter, options.id);
67
- case 'updateUser':
68
- console.log({adapter})
69
- return await updateRealUser(adapter, options.id, options.userData);
70
- default:
71
- throw new Error(`Unknown data type: ${type}`);
72
- }
73
- } catch (error) {
74
- console.error(`Error fetching ${type} data:`, error);
75
- return getMockData(type, options);
76
- }
77
- }
78
-
79
- async function getRealStats(adapter: any): Promise<AuthStats> {
80
- try {
81
- const users = adapter.getUsers ? await adapter.getUsers() : [];
82
- const sessions = adapter.getSessions ? await adapter.getSessions() : [];
83
-
84
- const now = new Date();
85
- const activeSessions = sessions.filter((s: any) => new Date(s.expiresAt || s.expires) > now);
86
- const activeUsers = new Set(activeSessions.map((s: any) => s.userId)).size;
87
-
88
- const usersByProvider: Record<string, number> = {
89
- email: users.length,
90
- github: 0
91
- };
92
-
93
- const recentSignups = users
94
- .sort((a: any, b: any) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
95
- .slice(0, 5)
96
- .map((user: any) => ({
97
- ...user,
98
- provider: 'email'
99
- }));
100
-
101
- const recentLogins = activeSessions
102
- .sort((a: any, b: any) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
103
- .slice(0, 5);
104
-
105
- return {
106
- totalUsers: users.length,
107
- activeUsers,
108
- totalSessions: sessions.length,
109
- activeSessions: activeSessions.length,
110
- usersByProvider,
111
- recentSignups,
112
- recentLogins
113
- };
114
- } catch (error) {
115
- console.error('Error fetching stats from adapter:', error);
116
- return getMockData('stats');
117
- }
118
- }
119
-
120
- async function getRealUsers(adapter: any, options: { page: number; limit: number; search?: string }): Promise<PaginatedResult<User>> {
121
- const { page, limit, search } = options;
122
-
123
- try {
124
- if (adapter.getUsers) {
125
- const allUsers = await adapter.getUsers();
126
-
127
- let filteredUsers = allUsers;
128
- if (search) {
129
- filteredUsers = allUsers.filter((user: any) =>
130
- user.email?.toLowerCase().includes(search.toLowerCase()) ||
131
- user.name?.toLowerCase().includes(search.toLowerCase())
132
- );
133
- }
134
-
135
- const startIndex = (page - 1) * limit;
136
- const endIndex = startIndex + limit;
137
- const paginatedUsers = filteredUsers.slice(startIndex, endIndex);
138
-
139
- return {
140
- data: paginatedUsers,
141
- total: filteredUsers.length,
142
- page,
143
- limit,
144
- totalPages: Math.ceil(filteredUsers.length / limit)
145
- };
146
- }
147
-
148
- return getMockData('users', options);
149
- } catch (error) {
150
- console.error('Error fetching users from adapter:', error);
151
- return getMockData('users', options);
152
- }
153
- }
154
-
155
- async function getRealSessions(adapter: any, options: { page: number; limit: number }): Promise<PaginatedResult<Session>> {
156
- const { page, limit } = options;
157
-
158
- try {
159
- if (adapter.getSessions) {
160
- const allSessions = await adapter.getSessions();
161
-
162
- const startIndex = (page - 1) * limit;
163
- const endIndex = startIndex + limit;
164
- const paginatedSessions = allSessions.slice(startIndex, endIndex);
165
-
166
- return {
167
- data: paginatedSessions,
168
- total: allSessions.length,
169
- page,
170
- limit,
171
- totalPages: Math.ceil(allSessions.length / limit)
172
- };
173
- }
174
-
175
- return getMockData('sessions', options);
176
- } catch (error) {
177
- console.error('Error fetching sessions from adapter:', error);
178
- return getMockData('sessions', options);
179
- }
180
- }
181
-
182
- async function getRealProviderStats(adapter: any) {
183
- try {
184
- return [
185
- { type: 'email', users: 0, active: 0 },
186
- { type: 'github', users: 0, active: 0 }
187
- ];
188
- } catch (error) {
189
- console.error('Error fetching provider stats from adapter:', error);
190
- return getMockData('providers');
191
- }
192
- }
193
-
194
- async function deleteRealUser(adapter: any, userId: string): Promise<void> {
195
- try {
196
- if (adapter.delete) {
197
- await adapter.delete({ model: 'user', where: [{ field: 'id', value: userId }] });
198
- } else {
199
- console.warn('Delete method not available on adapter');
200
- }
201
- } catch (error) {
202
- console.error('Error deleting user from adapter:', error);
203
- throw error;
204
- }
205
- }
206
-
207
- async function updateRealUser(adapter: any, userId: string, userData: Partial<User>): Promise<User> {
208
- console.log({userId, userData})
209
- try {
210
- const updatedUser = await adapter.update({
211
- model: 'user',
212
- where: [
213
- {
214
- field: 'id',
215
- value: userId
216
- }
217
- ],
218
- update: {...userData}
219
- });
220
- console.log({updatedUser})
221
- return updatedUser;
222
-
223
- } catch (error) {
224
- console.error('Error updating user from adapter:', error);
225
- throw error;
226
- }
227
- }
228
-
229
- function getMockData(type: string, options?: any): any {
230
- switch (type) {
231
- case 'stats':
232
- return getMockStats();
233
- case 'users':
234
- return getMockUsers(options);
235
- case 'sessions':
236
- return getMockSessions(options);
237
- case 'providers':
238
- return getMockProviderStats();
239
- case 'deleteUser':
240
- return Promise.resolve();
241
- case 'updateUser':
242
- return Promise.resolve(generateMockUsers(1)[0]);
243
- default:
244
- throw new Error(`Unknown data type: ${type}`);
245
- }
246
- }
247
-
248
- function getMockStats(): AuthStats {
249
- return {
250
- totalUsers: 1247,
251
- activeUsers: 892,
252
- totalSessions: 3456,
253
- activeSessions: 1234,
254
- usersByProvider: {
255
- 'google': 456,
256
- 'github': 234,
257
- 'email': 557
258
- },
259
- recentSignups: generateMockUsers(5),
260
- recentLogins: generateMockSessions(5)
261
- };
262
- }
263
-
264
- function getMockUsers(options: { page: number; limit: number; search?: string }): PaginatedResult<User> {
265
- const { page, limit, search } = options;
266
- const allUsers = generateMockUsers(100);
267
-
268
- let filteredUsers = allUsers;
269
- if (search) {
270
- filteredUsers = allUsers.filter(user =>
271
- user.email?.toLowerCase().includes(search.toLowerCase()) ||
272
- user.name?.toLowerCase().includes(search.toLowerCase())
273
- );
274
- }
275
-
276
- const start = (page - 1) * limit;
277
- const end = start + limit;
278
- const data = filteredUsers.slice(start, end);
279
-
280
- return {
281
- data,
282
- total: filteredUsers.length,
283
- page,
284
- limit,
285
- totalPages: Math.ceil(filteredUsers.length / limit)
286
- };
287
- }
288
-
289
- function getMockSessions(options: { page: number; limit: number }): PaginatedResult<Session> {
290
- const { page, limit } = options;
291
- const allSessions = generateMockSessions(200);
292
-
293
- const start = (page - 1) * limit;
294
- const end = start + limit;
295
- const data = allSessions.slice(start, end);
296
-
297
- return {
298
- data,
299
- total: allSessions.length,
300
- page,
301
- limit,
302
- totalPages: Math.ceil(allSessions.length / limit)
303
- };
304
- }
305
-
306
- function getMockProviderStats() {
307
- return [
308
- { type: 'google', users: 456, active: 234 },
309
- { type: 'github', users: 234, active: 123 },
310
- { type: 'email', users: 557, active: 345 }
311
- ];
312
- }
313
-
314
- function generateMockUsers(count: number): User[] {
315
- const users: User[] = [];
316
- const providers = ['google', 'github', 'email'];
317
-
318
- for (let i = 0; i < count; i++) {
319
- const provider = providers[Math.floor(Math.random() * providers.length)];
320
- users.push({
321
- id: `user_${i + 1}`,
322
- email: `user${i + 1}@example.com`,
323
- name: `User ${i + 1}`,
324
- image: `https://api.dicebear.com/7.x/avataaars/svg?seed=user${i + 1}`,
325
- emailVerified: Math.random() > 0.3 ? new Date() : undefined,
326
- createdAt: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000),
327
- updatedAt: new Date(),
328
- provider,
329
- lastSignIn: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000)
330
- });
331
- }
332
-
333
- return users;
334
- }
335
-
336
- function generateMockSessions(count: number): Session[] {
337
- const sessions: Session[] = [];
338
-
339
- for (let i = 0; i < count; i++) {
340
- sessions.push({
341
- id: `session_${i + 1}`,
342
- userId: `user_${Math.floor(Math.random() * 100) + 1}`,
343
- expires: new Date(Date.now() + Math.random() * 24 * 60 * 60 * 1000),
344
- createdAt: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000),
345
- userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
346
- ip: `192.168.1.${Math.floor(Math.random() * 255)}`
347
- });
348
- }
349
-
350
- return sessions;
351
- }