archicore 0.2.0 → 0.2.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.
@@ -1,40 +1,46 @@
1
1
  /**
2
2
  * Authentication Service for ArchiCore
3
+ *
4
+ * Supports PostgreSQL (primary) with JSON file fallback
3
5
  */
4
6
  import { randomUUID } from 'crypto';
5
7
  import { createHash } from 'crypto';
6
8
  import { readFile, writeFile, mkdir } from 'fs/promises';
9
+ import bcrypt from 'bcrypt';
7
10
  import { join } from 'path';
8
11
  import { TIER_LIMITS } from '../../types/user.js';
12
+ import { db } from './database.js';
13
+ import { Logger } from '../../utils/logger.js';
9
14
  const DATA_DIR = '.archicore';
10
15
  const USERS_FILE = 'users.json';
11
16
  const SESSIONS_FILE = 'sessions.json';
12
17
  export class AuthService {
13
18
  static instance = null;
14
19
  dataDir;
20
+ // JSON fallback storage
15
21
  users = [];
16
22
  sessions = [];
17
- initialized = false;
23
+ jsonInitialized = false;
18
24
  constructor(dataDir = DATA_DIR) {
19
25
  this.dataDir = dataDir;
20
26
  }
21
- /**
22
- * Get singleton instance of AuthService
23
- */
24
27
  static getInstance() {
25
28
  if (!AuthService.instance) {
26
29
  AuthService.instance = new AuthService();
27
30
  }
28
31
  return AuthService.instance;
29
32
  }
30
- async ensureInitialized() {
31
- if (this.initialized)
33
+ useDatabase() {
34
+ return db.isAvailable();
35
+ }
36
+ // ========== JSON FALLBACK METHODS ==========
37
+ async ensureJsonInitialized() {
38
+ if (this.jsonInitialized)
32
39
  return;
33
40
  try {
34
41
  await mkdir(this.dataDir, { recursive: true });
35
42
  }
36
43
  catch { }
37
- // Load users
38
44
  try {
39
45
  const usersPath = join(this.dataDir, USERS_FILE);
40
46
  const data = await readFile(usersPath, 'utf-8');
@@ -43,10 +49,8 @@ export class AuthService {
43
49
  }
44
50
  catch {
45
51
  this.users = [];
46
- // Create default admin user
47
52
  await this.createDefaultAdmin();
48
53
  }
49
- // Load sessions
50
54
  try {
51
55
  const sessionsPath = join(this.dataDir, SESSIONS_FILE);
52
56
  const data = await readFile(sessionsPath, 'utf-8');
@@ -56,13 +60,13 @@ export class AuthService {
56
60
  catch {
57
61
  this.sessions = [];
58
62
  }
59
- this.initialized = true;
63
+ this.jsonInitialized = true;
60
64
  }
61
65
  async createDefaultAdmin() {
62
66
  const admin = {
63
67
  id: 'admin-' + randomUUID(),
64
68
  email: 'admin@archicore.io',
65
- username: 'Administrator',
69
+ username: 'ArchiCore Team',
66
70
  passwordHash: this.hashPassword('admin123'),
67
71
  tier: 'admin',
68
72
  provider: 'email',
@@ -81,9 +85,46 @@ export class AuthService {
81
85
  const sessionsPath = join(this.dataDir, SESSIONS_FILE);
82
86
  await writeFile(sessionsPath, JSON.stringify({ sessions: this.sessions }, null, 2));
83
87
  }
84
- hashPassword(password) {
88
+ // ========== HELPER METHODS ==========
89
+ BCRYPT_ROUNDS = 12;
90
+ /**
91
+ * Hash password with bcrypt (async)
92
+ */
93
+ async hashPasswordAsync(password) {
94
+ return bcrypt.hash(password, this.BCRYPT_ROUNDS);
95
+ }
96
+ /**
97
+ * Legacy SHA256 hash for backward compatibility
98
+ */
99
+ hashPasswordLegacy(password) {
85
100
  return createHash('sha256').update(password + 'archicore-salt-2024').digest('hex');
86
101
  }
102
+ /**
103
+ * Verify password against hash (supports both bcrypt and legacy SHA256)
104
+ */
105
+ async verifyPassword(password, hash) {
106
+ // Check if it's a bcrypt hash (starts with $2b$ or $2a$)
107
+ if (hash.startsWith('$2')) {
108
+ return bcrypt.compare(password, hash);
109
+ }
110
+ // Legacy SHA256 hash
111
+ return this.hashPasswordLegacy(password) === hash;
112
+ }
113
+ /**
114
+ * Check if password needs rehashing (from legacy to bcrypt)
115
+ */
116
+ needsRehash(hash) {
117
+ return !hash.startsWith('$2');
118
+ }
119
+ /**
120
+ * Sync hash for JSON fallback (still uses legacy for simplicity)
121
+ * @deprecated Use hashPasswordAsync for new passwords
122
+ */
123
+ hashPassword(password) {
124
+ // For new registrations, we should use async version
125
+ // This is kept for backward compatibility with JSON storage
126
+ return this.hashPasswordLegacy(password);
127
+ }
87
128
  createEmptyUsage() {
88
129
  return {
89
130
  requestsToday: 0,
@@ -99,13 +140,73 @@ export class AuthService {
99
140
  const { passwordHash, ...sanitized } = user;
100
141
  return sanitized;
101
142
  }
143
+ rowToUser(row) {
144
+ return {
145
+ id: row.id,
146
+ email: row.email,
147
+ username: row.username,
148
+ passwordHash: row.password_hash || undefined,
149
+ avatar: row.avatar || undefined,
150
+ tier: row.tier,
151
+ provider: row.provider,
152
+ providerId: row.provider_id || undefined,
153
+ createdAt: row.created_at,
154
+ lastLoginAt: row.last_login_at,
155
+ usage: {
156
+ requestsToday: row.requests_today,
157
+ fullAnalysisToday: row.full_analysis_today,
158
+ projectsToday: row.projects_today,
159
+ lastResetDate: row.usage_reset_date
160
+ }
161
+ };
162
+ }
163
+ // ========== DATABASE METHODS ==========
164
+ async dbCreateDefaultAdmin() {
165
+ const result = await db.query('SELECT id FROM users WHERE tier = $1 LIMIT 1', ['admin']);
166
+ if (result.rows.length > 0)
167
+ return;
168
+ const adminId = 'admin-' + randomUUID();
169
+ await db.query(`INSERT INTO users (id, email, username, password_hash, tier, provider)
170
+ VALUES ($1, $2, $3, $4, $5, $6)`, [adminId, 'admin@archicore.io', 'ArchiCore Team', this.hashPassword('admin123'), 'admin', 'email']);
171
+ Logger.info('Default admin user created');
172
+ }
173
+ // ========== PUBLIC API ==========
102
174
  async register(email, username, password) {
103
- await this.ensureInitialized();
104
- // Check if email already exists
175
+ if (this.useDatabase()) {
176
+ try {
177
+ // Check email exists
178
+ const emailCheck = await db.query('SELECT id FROM users WHERE LOWER(email) = LOWER($1)', [email]);
179
+ if (emailCheck.rows.length > 0) {
180
+ return { success: false, error: 'Email already registered' };
181
+ }
182
+ // Check username exists
183
+ const usernameCheck = await db.query('SELECT id FROM users WHERE LOWER(username) = LOWER($1)', [username]);
184
+ if (usernameCheck.rows.length > 0) {
185
+ return { success: false, error: 'Username already taken' };
186
+ }
187
+ const userId = 'user-' + randomUUID();
188
+ const token = this.generateToken();
189
+ const passwordHash = await this.hashPasswordAsync(password);
190
+ await db.transaction(async (client) => {
191
+ await client.query(`INSERT INTO users (id, email, username, password_hash, tier, provider)
192
+ VALUES ($1, $2, $3, $4, $5, $6)`, [userId, email.toLowerCase(), username, passwordHash, 'free', 'email']);
193
+ await client.query(`INSERT INTO sessions (token, user_id, expires_at)
194
+ VALUES ($1, $2, $3)`, [token, userId, new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)]);
195
+ });
196
+ const userResult = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
197
+ const user = this.rowToUser(userResult.rows[0]);
198
+ return { success: true, token, user: this.sanitizeUser(user) };
199
+ }
200
+ catch (error) {
201
+ Logger.error('Register error:', error);
202
+ return { success: false, error: 'Registration failed' };
203
+ }
204
+ }
205
+ // JSON fallback
206
+ await this.ensureJsonInitialized();
105
207
  if (this.users.find(u => u.email.toLowerCase() === email.toLowerCase())) {
106
208
  return { success: false, error: 'Email already registered' };
107
209
  }
108
- // Check if username already exists
109
210
  if (this.users.find(u => u.username.toLowerCase() === username.toLowerCase())) {
110
211
  return { success: false, error: 'Username already taken' };
111
212
  }
@@ -122,23 +223,48 @@ export class AuthService {
122
223
  };
123
224
  this.users.push(user);
124
225
  await this.saveUsers();
125
- // Create session
126
226
  const token = this.generateToken();
127
227
  const session = {
128
228
  token,
129
229
  userId: user.id,
130
- expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days
230
+ expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
131
231
  };
132
232
  this.sessions.push(session);
133
233
  await this.saveSessions();
134
- return {
135
- success: true,
136
- token,
137
- user: this.sanitizeUser(user)
138
- };
234
+ return { success: true, token, user: this.sanitizeUser(user) };
139
235
  }
140
236
  async login(email, password) {
141
- await this.ensureInitialized();
237
+ if (this.useDatabase()) {
238
+ try {
239
+ const result = await db.query('SELECT * FROM users WHERE LOWER(email) = LOWER($1)', [email]);
240
+ if (result.rows.length === 0) {
241
+ return { success: false, error: 'Invalid email or password' };
242
+ }
243
+ const row = result.rows[0];
244
+ // Verify password (supports both bcrypt and legacy SHA256)
245
+ const isValid = await this.verifyPassword(password, row.password_hash || '');
246
+ if (!isValid) {
247
+ return { success: false, error: 'Invalid email or password' };
248
+ }
249
+ const token = this.generateToken();
250
+ // Rehash legacy passwords to bcrypt
251
+ if (row.password_hash && this.needsRehash(row.password_hash)) {
252
+ const newHash = await this.hashPasswordAsync(password);
253
+ await db.query('UPDATE users SET password_hash = $1 WHERE id = $2', [newHash, row.id]);
254
+ Logger.info(`Upgraded password hash for user ${row.id}`);
255
+ }
256
+ await db.query('UPDATE users SET last_login_at = NOW() WHERE id = $1', [row.id]);
257
+ await db.query('INSERT INTO sessions (token, user_id, expires_at) VALUES ($1, $2, $3)', [token, row.id, new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)]);
258
+ const user = this.rowToUser(row);
259
+ return { success: true, token, user: this.sanitizeUser(user) };
260
+ }
261
+ catch (error) {
262
+ Logger.error('Login error:', error);
263
+ return { success: false, error: 'Login failed' };
264
+ }
265
+ }
266
+ // JSON fallback
267
+ await this.ensureJsonInitialized();
142
268
  const user = this.users.find(u => u.email.toLowerCase() === email.toLowerCase());
143
269
  if (!user) {
144
270
  return { success: false, error: 'Invalid email or password' };
@@ -146,10 +272,8 @@ export class AuthService {
146
272
  if (user.passwordHash !== this.hashPassword(password)) {
147
273
  return { success: false, error: 'Invalid email or password' };
148
274
  }
149
- // Update last login
150
275
  user.lastLoginAt = new Date();
151
276
  await this.saveUsers();
152
- // Create session
153
277
  const token = this.generateToken();
154
278
  const session = {
155
279
  token,
@@ -158,19 +282,44 @@ export class AuthService {
158
282
  };
159
283
  this.sessions.push(session);
160
284
  await this.saveSessions();
161
- return {
162
- success: true,
163
- token,
164
- user: this.sanitizeUser(user)
165
- };
285
+ return { success: true, token, user: this.sanitizeUser(user) };
166
286
  }
167
287
  async oauthLogin(provider, profile) {
168
- await this.ensureInitialized();
169
- // Find existing user by provider ID or email
288
+ if (this.useDatabase()) {
289
+ try {
290
+ // Find by provider ID or email
291
+ let result = await db.query(`SELECT * FROM users WHERE (provider = $1 AND provider_id = $2) OR LOWER(email) = LOWER($3) LIMIT 1`, [provider, profile.id, profile.email]);
292
+ let userId;
293
+ if (result.rows.length === 0) {
294
+ // Create new user
295
+ userId = 'user-' + randomUUID();
296
+ await db.query(`INSERT INTO users (id, email, username, avatar, tier, provider, provider_id)
297
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`, [userId, profile.email.toLowerCase(), profile.name || profile.email.split('@')[0], profile.avatar, 'free', provider, profile.id]);
298
+ }
299
+ else {
300
+ // Update existing
301
+ userId = result.rows[0].id;
302
+ await db.query(`UPDATE users SET last_login_at = NOW(), avatar = COALESCE($2, avatar),
303
+ provider = CASE WHEN provider_id IS NULL THEN $3 ELSE provider END,
304
+ provider_id = CASE WHEN provider_id IS NULL THEN $4 ELSE provider_id END
305
+ WHERE id = $1`, [userId, profile.avatar, provider, profile.id]);
306
+ }
307
+ const token = this.generateToken();
308
+ await db.query('INSERT INTO sessions (token, user_id, expires_at) VALUES ($1, $2, $3)', [token, userId, new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)]);
309
+ result = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
310
+ const user = this.rowToUser(result.rows[0]);
311
+ return { success: true, token, user: this.sanitizeUser(user) };
312
+ }
313
+ catch (error) {
314
+ Logger.error('OAuth login error:', error);
315
+ return { success: false, error: 'OAuth login failed' };
316
+ }
317
+ }
318
+ // JSON fallback
319
+ await this.ensureJsonInitialized();
170
320
  let user = this.users.find(u => (u.provider === provider && u.providerId === profile.id) ||
171
321
  u.email.toLowerCase() === profile.email.toLowerCase());
172
322
  if (!user) {
173
- // Create new user
174
323
  user = {
175
324
  id: 'user-' + randomUUID(),
176
325
  email: profile.email.toLowerCase(),
@@ -186,7 +335,6 @@ export class AuthService {
186
335
  this.users.push(user);
187
336
  }
188
337
  else {
189
- // Update existing user
190
338
  user.lastLoginAt = new Date();
191
339
  if (profile.avatar)
192
340
  user.avatar = profile.avatar;
@@ -196,7 +344,6 @@ export class AuthService {
196
344
  }
197
345
  }
198
346
  await this.saveUsers();
199
- // Create session
200
347
  const token = this.generateToken();
201
348
  const session = {
202
349
  token,
@@ -205,37 +352,78 @@ export class AuthService {
205
352
  };
206
353
  this.sessions.push(session);
207
354
  await this.saveSessions();
208
- return {
209
- success: true,
210
- token,
211
- user: this.sanitizeUser(user)
212
- };
355
+ return { success: true, token, user: this.sanitizeUser(user) };
213
356
  }
214
357
  async validateToken(token) {
215
- await this.ensureInitialized();
358
+ if (this.useDatabase()) {
359
+ try {
360
+ const result = await db.query('SELECT * FROM sessions WHERE token = $1', [token]);
361
+ if (result.rows.length === 0)
362
+ return null;
363
+ const session = result.rows[0];
364
+ if (new Date(session.expires_at) < new Date()) {
365
+ await db.query('DELETE FROM sessions WHERE token = $1', [token]);
366
+ return null;
367
+ }
368
+ const userResult = await db.query('SELECT * FROM users WHERE id = $1', [session.user_id]);
369
+ if (userResult.rows.length === 0)
370
+ return null;
371
+ return this.rowToUser(userResult.rows[0]);
372
+ }
373
+ catch (error) {
374
+ Logger.error('Token validation error:', error);
375
+ return null;
376
+ }
377
+ }
378
+ // JSON fallback
379
+ await this.ensureJsonInitialized();
216
380
  const session = this.sessions.find(s => s.token === token);
217
381
  if (!session)
218
382
  return null;
219
383
  if (new Date(session.expiresAt) < new Date()) {
220
- // Session expired, remove it
221
384
  this.sessions = this.sessions.filter(s => s.token !== token);
222
385
  await this.saveSessions();
223
386
  return null;
224
387
  }
225
- const user = this.users.find(u => u.id === session.userId);
226
- return user || null;
388
+ return this.users.find(u => u.id === session.userId) || null;
227
389
  }
228
390
  async logout(token) {
229
- await this.ensureInitialized();
391
+ if (this.useDatabase()) {
392
+ await db.query('DELETE FROM sessions WHERE token = $1', [token]);
393
+ return;
394
+ }
395
+ await this.ensureJsonInitialized();
230
396
  this.sessions = this.sessions.filter(s => s.token !== token);
231
397
  await this.saveSessions();
232
398
  }
233
399
  async getUser(userId) {
234
- await this.ensureInitialized();
400
+ if (this.useDatabase()) {
401
+ try {
402
+ const result = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
403
+ if (result.rows.length === 0)
404
+ return null;
405
+ return this.rowToUser(result.rows[0]);
406
+ }
407
+ catch (error) {
408
+ Logger.error('Get user error:', error);
409
+ return null;
410
+ }
411
+ }
412
+ await this.ensureJsonInitialized();
235
413
  return this.users.find(u => u.id === userId) || null;
236
414
  }
237
415
  async updateUserTier(userId, tier) {
238
- await this.ensureInitialized();
416
+ if (this.useDatabase()) {
417
+ try {
418
+ const result = await db.query('UPDATE users SET tier = $1 WHERE id = $2', [tier, userId]);
419
+ return (result.rowCount ?? 0) > 0;
420
+ }
421
+ catch (error) {
422
+ Logger.error('Update tier error:', error);
423
+ return false;
424
+ }
425
+ }
426
+ await this.ensureJsonInitialized();
239
427
  const user = this.users.find(u => u.id === userId);
240
428
  if (!user)
241
429
  return false;
@@ -244,12 +432,59 @@ export class AuthService {
244
432
  return true;
245
433
  }
246
434
  async checkAndUpdateUsage(userId, type) {
247
- await this.ensureInitialized();
435
+ if (this.useDatabase()) {
436
+ try {
437
+ const result = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
438
+ if (result.rows.length === 0)
439
+ return { allowed: false, remaining: 0, limit: 0 };
440
+ const row = result.rows[0];
441
+ const today = new Date().toISOString().split('T')[0];
442
+ // Reset usage if new day
443
+ if (row.usage_reset_date !== today) {
444
+ await db.query(`UPDATE users SET requests_today = 0, full_analysis_today = 0, projects_today = 0, usage_reset_date = $1 WHERE id = $2`, [today, userId]);
445
+ row.requests_today = 0;
446
+ row.full_analysis_today = 0;
447
+ row.projects_today = 0;
448
+ }
449
+ const limits = TIER_LIMITS[row.tier];
450
+ let current;
451
+ let limit;
452
+ let column;
453
+ switch (type) {
454
+ case 'request':
455
+ current = row.requests_today;
456
+ limit = limits.requestsPerDay;
457
+ column = 'requests_today';
458
+ break;
459
+ case 'analysis':
460
+ current = row.full_analysis_today;
461
+ limit = limits.fullAnalysisPerDay;
462
+ column = 'full_analysis_today';
463
+ break;
464
+ case 'project':
465
+ current = row.projects_today;
466
+ limit = limits.projectsPerDay;
467
+ column = 'projects_today';
468
+ break;
469
+ }
470
+ if (current >= limit && limit !== Infinity) {
471
+ return { allowed: false, remaining: 0, limit };
472
+ }
473
+ await db.query(`UPDATE users SET ${column} = ${column} + 1 WHERE id = $1`, [userId]);
474
+ const remaining = limit === Infinity ? Infinity : limit - current - 1;
475
+ return { allowed: true, remaining, limit };
476
+ }
477
+ catch (error) {
478
+ Logger.error('Usage check error:', error);
479
+ return { allowed: false, remaining: 0, limit: 0 };
480
+ }
481
+ }
482
+ // JSON fallback
483
+ await this.ensureJsonInitialized();
248
484
  const user = this.users.find(u => u.id === userId);
249
485
  if (!user)
250
486
  return { allowed: false, remaining: 0, limit: 0 };
251
487
  const today = new Date().toISOString().split('T')[0];
252
- // Reset usage if new day
253
488
  if (user.usage.lastResetDate !== today) {
254
489
  user.usage = this.createEmptyUsage();
255
490
  }
@@ -273,7 +508,6 @@ export class AuthService {
273
508
  if (current >= limit && limit !== Infinity) {
274
509
  return { allowed: false, remaining: 0, limit };
275
510
  }
276
- // Increment usage
277
511
  switch (type) {
278
512
  case 'request':
279
513
  user.usage.requestsToday++;
@@ -290,15 +524,38 @@ export class AuthService {
290
524
  return { allowed: true, remaining, limit };
291
525
  }
292
526
  async getAllUsers() {
293
- await this.ensureInitialized();
527
+ if (this.useDatabase()) {
528
+ try {
529
+ const result = await db.query('SELECT * FROM users ORDER BY created_at DESC');
530
+ return result.rows.map(row => this.sanitizeUser(this.rowToUser(row)));
531
+ }
532
+ catch (error) {
533
+ Logger.error('Get all users error:', error);
534
+ return [];
535
+ }
536
+ }
537
+ await this.ensureJsonInitialized();
294
538
  return this.users.map(u => this.sanitizeUser(u));
295
539
  }
296
540
  async deleteUser(userId) {
297
- await this.ensureInitialized();
541
+ if (this.useDatabase()) {
542
+ try {
543
+ // Don't delete admin
544
+ const check = await db.query('SELECT tier FROM users WHERE id = $1', [userId]);
545
+ if (check.rows.length === 0 || check.rows[0].tier === 'admin')
546
+ return false;
547
+ await db.query('DELETE FROM users WHERE id = $1', [userId]);
548
+ return true;
549
+ }
550
+ catch (error) {
551
+ Logger.error('Delete user error:', error);
552
+ return false;
553
+ }
554
+ }
555
+ await this.ensureJsonInitialized();
298
556
  const index = this.users.findIndex(u => u.id === userId);
299
557
  if (index === -1)
300
558
  return false;
301
- // Don't delete admin
302
559
  if (this.users[index].tier === 'admin')
303
560
  return false;
304
561
  this.users.splice(index, 1);
@@ -308,7 +565,35 @@ export class AuthService {
308
565
  return true;
309
566
  }
310
567
  async updateUser(userId, updates) {
311
- await this.ensureInitialized();
568
+ if (this.useDatabase()) {
569
+ try {
570
+ const sets = [];
571
+ const values = [];
572
+ let paramIndex = 1;
573
+ if (updates.username) {
574
+ sets.push(`username = $${paramIndex++}`);
575
+ values.push(updates.username);
576
+ }
577
+ if (updates.email) {
578
+ sets.push(`email = $${paramIndex++}`);
579
+ values.push(updates.email);
580
+ }
581
+ if (updates.avatar) {
582
+ sets.push(`avatar = $${paramIndex++}`);
583
+ values.push(updates.avatar);
584
+ }
585
+ if (sets.length === 0)
586
+ return true;
587
+ values.push(userId);
588
+ const result = await db.query(`UPDATE users SET ${sets.join(', ')} WHERE id = $${paramIndex}`, values);
589
+ return (result.rowCount ?? 0) > 0;
590
+ }
591
+ catch (error) {
592
+ Logger.error('Update user error:', error);
593
+ return false;
594
+ }
595
+ }
596
+ await this.ensureJsonInitialized();
312
597
  const user = this.users.find(u => u.id === userId);
313
598
  if (!user)
314
599
  return false;
@@ -321,5 +606,13 @@ export class AuthService {
321
606
  await this.saveUsers();
322
607
  return true;
323
608
  }
609
+ /**
610
+ * Initialize database and create default admin if needed
611
+ */
612
+ async initDatabase() {
613
+ if (this.useDatabase()) {
614
+ await this.dbCreateDefaultAdmin();
615
+ }
616
+ }
324
617
  }
325
618
  //# sourceMappingURL=auth-service.js.map
@@ -0,0 +1,77 @@
1
+ /**
2
+ * ArchiCore Redis Cache Service
3
+ *
4
+ * Provides caching for API responses to improve performance
5
+ */
6
+ export interface CacheOptions {
7
+ ttl?: number;
8
+ prefix?: string;
9
+ }
10
+ declare class CacheService {
11
+ private client;
12
+ private enabled;
13
+ private memoryCache;
14
+ connect(): Promise<void>;
15
+ private getKey;
16
+ /**
17
+ * Get value from cache
18
+ */
19
+ get<T>(key: string, prefix?: string): Promise<T | null>;
20
+ /**
21
+ * Set value in cache
22
+ */
23
+ set(key: string, value: any, options?: CacheOptions): Promise<void>;
24
+ /**
25
+ * Delete value from cache
26
+ */
27
+ del(key: string, prefix?: string): Promise<void>;
28
+ /**
29
+ * Delete all keys matching pattern
30
+ */
31
+ delPattern(pattern: string): Promise<void>;
32
+ /**
33
+ * Clear all cache
34
+ */
35
+ flush(): Promise<void>;
36
+ /**
37
+ * Get or set cache with callback
38
+ */
39
+ getOrSet<T>(key: string, callback: () => Promise<T>, options?: CacheOptions): Promise<T>;
40
+ /**
41
+ * Check if cache is available
42
+ */
43
+ isEnabled(): boolean;
44
+ /**
45
+ * Get cache stats
46
+ */
47
+ getStats(): Promise<{
48
+ enabled: boolean;
49
+ type: string;
50
+ keys: number;
51
+ }>;
52
+ /**
53
+ * Cleanup expired memory cache entries
54
+ */
55
+ private cleanupMemoryCache;
56
+ /**
57
+ * Disconnect from Redis
58
+ */
59
+ disconnect(): Promise<void>;
60
+ }
61
+ export declare const cache: CacheService;
62
+ export declare const cacheKeys: {
63
+ project: (projectId: string) => string;
64
+ projectAnalysis: (projectId: string) => string;
65
+ projectMetrics: (projectId: string) => string;
66
+ projectSearch: (projectId: string, query: string) => string;
67
+ user: (userId: string) => string;
68
+ userProjects: (userId: string) => string;
69
+ };
70
+ export declare const cacheTTL: {
71
+ short: number;
72
+ medium: number;
73
+ long: number;
74
+ day: number;
75
+ };
76
+ export {};
77
+ //# sourceMappingURL=cache.d.ts.map