@kyro-cms/core 0.1.4 → 0.1.6

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 (77) hide show
  1. package/dist/bootstrap-WMWQ4DBX.cjs +29 -0
  2. package/dist/{bootstrap-2WJK6PG7.cjs.map → bootstrap-WMWQ4DBX.cjs.map} +1 -1
  3. package/dist/bootstrap-WOVGAKZP.js +4 -0
  4. package/dist/{bootstrap-Q2TWUQF3.js.map → bootstrap-WOVGAKZP.js.map} +1 -1
  5. package/dist/{chunk-I4BORBXT.cjs → chunk-3EVLFWH2.cjs} +523 -204
  6. package/dist/chunk-3EVLFWH2.cjs.map +1 -0
  7. package/dist/chunk-5BLDMQED.cjs +18 -0
  8. package/dist/{chunk-Q7SFCCGT.cjs.map → chunk-5BLDMQED.cjs.map} +1 -1
  9. package/dist/{chunk-V67YXRBT.js → chunk-5Y7QGIHD.js} +523 -203
  10. package/dist/chunk-5Y7QGIHD.js.map +1 -0
  11. package/dist/{chunk-U4CHJTWX.cjs → chunk-7G6EVYCU.cjs} +5 -5
  12. package/dist/{chunk-U4CHJTWX.cjs.map → chunk-7G6EVYCU.cjs.map} +1 -1
  13. package/dist/chunk-A3RQWHKD.cjs +263 -0
  14. package/dist/chunk-A3RQWHKD.cjs.map +1 -0
  15. package/dist/{chunk-XLMVCGXA.js → chunk-LRTZJJPD.js} +3 -3
  16. package/dist/{chunk-XLMVCGXA.js.map → chunk-LRTZJJPD.js.map} +1 -1
  17. package/dist/chunk-NSBPE2FW.js +15 -0
  18. package/dist/{chunk-PZ5AY32C.js.map → chunk-NSBPE2FW.js.map} +1 -1
  19. package/dist/{chunk-M4JFHQ5J.js → chunk-QUJ4OLSC.js} +3 -3
  20. package/dist/{chunk-M4JFHQ5J.js.map → chunk-QUJ4OLSC.js.map} +1 -1
  21. package/dist/{chunk-5AOILNGY.cjs → chunk-TZFJMPCH.cjs} +4 -4
  22. package/dist/{chunk-5AOILNGY.cjs.map → chunk-TZFJMPCH.cjs.map} +1 -1
  23. package/dist/chunk-VMSRTAH7.js +256 -0
  24. package/dist/chunk-VMSRTAH7.js.map +1 -0
  25. package/dist/{chunk-KA3UOIFC.js → chunk-XTZSUDSI.js} +3 -3
  26. package/dist/{chunk-KA3UOIFC.js.map → chunk-XTZSUDSI.js.map} +1 -1
  27. package/dist/{chunk-KWTKEBHM.cjs → chunk-YD7Y25W7.cjs} +19 -19
  28. package/dist/{chunk-KWTKEBHM.cjs.map → chunk-YD7Y25W7.cjs.map} +1 -1
  29. package/dist/cli/index.cjs +5 -5
  30. package/dist/cli/index.js +5 -5
  31. package/dist/database-7CJOXEZR.js +5 -0
  32. package/dist/{database-37KXWUER.js.map → database-7CJOXEZR.js.map} +1 -1
  33. package/dist/database-QOIV44GT.cjs +22 -0
  34. package/dist/{database-LJKD3HE4.cjs.map → database-QOIV44GT.cjs.map} +1 -1
  35. package/dist/drizzle/index.cjs +8 -8
  36. package/dist/drizzle/index.d.cts +1 -1
  37. package/dist/drizzle/index.d.ts +1 -1
  38. package/dist/drizzle/index.js +4 -4
  39. package/dist/graphql/index.cjs +1 -1
  40. package/dist/graphql/index.js +1 -1
  41. package/dist/{index-CzkEHKqu.d.cts → index-BMySjW6o.d.cts} +6 -0
  42. package/dist/{index-BVFlb7uU.d.ts → index-CMUNCIWQ.d.ts} +6 -0
  43. package/dist/index.cjs +727 -346
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.d.cts +229 -62
  46. package/dist/index.d.ts +229 -62
  47. package/dist/index.js +706 -331
  48. package/dist/index.js.map +1 -1
  49. package/dist/mongodb/index.cjs +1 -1
  50. package/dist/mongodb/index.js +1 -1
  51. package/dist/postgres-auth-adapter-REJFUMP7.js +5 -0
  52. package/dist/{postgres-auth-adapter-LTDUGBMB.js.map → postgres-auth-adapter-REJFUMP7.js.map} +1 -1
  53. package/dist/postgres-auth-adapter-VK6GY7LX.cjs +14 -0
  54. package/dist/{postgres-auth-adapter-CYZAVPPP.cjs.map → postgres-auth-adapter-VK6GY7LX.cjs.map} +1 -1
  55. package/dist/redis-adapter-4YDY4LWE.js +4 -0
  56. package/dist/redis-adapter-4YDY4LWE.js.map +1 -0
  57. package/dist/redis-adapter-LBLNKGNS.cjs +13 -0
  58. package/dist/redis-adapter-LBLNKGNS.cjs.map +1 -0
  59. package/dist/rest/index.cjs +1 -1
  60. package/dist/rest/index.js +1 -1
  61. package/dist/templates/index.cjs +1 -1
  62. package/dist/templates/index.js +1 -1
  63. package/dist/trpc/index.cjs +1 -1
  64. package/dist/trpc/index.js +1 -1
  65. package/dist/ws/index.cjs +1 -1
  66. package/dist/ws/index.js +1 -1
  67. package/package.json +2 -2
  68. package/dist/bootstrap-2WJK6PG7.cjs +0 -29
  69. package/dist/bootstrap-Q2TWUQF3.js +0 -4
  70. package/dist/chunk-I4BORBXT.cjs.map +0 -1
  71. package/dist/chunk-PZ5AY32C.js +0 -9
  72. package/dist/chunk-Q7SFCCGT.cjs +0 -11
  73. package/dist/chunk-V67YXRBT.js.map +0 -1
  74. package/dist/database-37KXWUER.js +0 -5
  75. package/dist/database-LJKD3HE4.cjs +0 -22
  76. package/dist/postgres-auth-adapter-CYZAVPPP.cjs +0 -14
  77. package/dist/postgres-auth-adapter-LTDUGBMB.js +0 -5
package/dist/index.cjs CHANGED
@@ -1,30 +1,29 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkF5B64H5S_cjs = require('./chunk-F5B64H5S.cjs');
4
- var chunkI4BORBXT_cjs = require('./chunk-I4BORBXT.cjs');
4
+ var chunkA3RQWHKD_cjs = require('./chunk-A3RQWHKD.cjs');
5
+ var chunk3EVLFWH2_cjs = require('./chunk-3EVLFWH2.cjs');
5
6
  var chunk3VZCX4DF_cjs = require('./chunk-3VZCX4DF.cjs');
6
7
  var chunkK7QF2QCM_cjs = require('./chunk-K7QF2QCM.cjs');
7
8
  var chunkUEG7KMKC_cjs = require('./chunk-UEG7KMKC.cjs');
8
9
  var chunkR3XIBBAW_cjs = require('./chunk-R3XIBBAW.cjs');
9
10
  var chunkDVD5P72E_cjs = require('./chunk-DVD5P72E.cjs');
10
11
  var chunkV3B25QOK_cjs = require('./chunk-V3B25QOK.cjs');
11
- var chunkKWTKEBHM_cjs = require('./chunk-KWTKEBHM.cjs');
12
- var chunkU4CHJTWX_cjs = require('./chunk-U4CHJTWX.cjs');
13
- require('./chunk-5AOILNGY.cjs');
12
+ var chunkYD7Y25W7_cjs = require('./chunk-YD7Y25W7.cjs');
13
+ var chunk7G6EVYCU_cjs = require('./chunk-7G6EVYCU.cjs');
14
+ require('./chunk-TZFJMPCH.cjs');
14
15
  var chunkHT6VE4NW_cjs = require('./chunk-HT6VE4NW.cjs');
15
16
  var chunkRLTG4YZM_cjs = require('./chunk-RLTG4YZM.cjs');
16
- require('./chunk-Q7SFCCGT.cjs');
17
+ var chunk5BLDMQED_cjs = require('./chunk-5BLDMQED.cjs');
17
18
  var zod = require('zod');
18
- var bcrypt2 = require('bcrypt');
19
+ var bcrypt = require('bcrypt');
19
20
  var jwt2 = require('jsonwebtoken');
20
- var bcrypt = require('bcryptjs');
21
21
  var crypto = require('crypto');
22
22
 
23
23
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
24
24
 
25
- var bcrypt2__default = /*#__PURE__*/_interopDefault(bcrypt2);
26
- var jwt2__default = /*#__PURE__*/_interopDefault(jwt2);
27
25
  var bcrypt__default = /*#__PURE__*/_interopDefault(bcrypt);
26
+ var jwt2__default = /*#__PURE__*/_interopDefault(jwt2);
28
27
 
29
28
  // src/registry/validator.ts
30
29
  var ConfigValidationError = class extends Error {
@@ -2001,277 +2000,6 @@ var defaultFieldStyling = {
2001
2000
  }
2002
2001
  }
2003
2002
  };
2004
- var SQLiteAuthAdapter = class {
2005
- db = null;
2006
- path;
2007
- saltRounds;
2008
- externalDb;
2009
- constructor(options = {}) {
2010
- this.path = options.path || "./data.db";
2011
- this.saltRounds = options.saltRounds || 12;
2012
- this.externalDb = !!options.db;
2013
- if (options.db) {
2014
- this.db = options.db;
2015
- }
2016
- }
2017
- async connect() {
2018
- if (this.db) return;
2019
- const Database = (await import('better-sqlite3')).default;
2020
- this.db = new Database(this.path);
2021
- this.db.pragma("journal_mode = WAL");
2022
- this.db.pragma("foreign_keys = ON");
2023
- this.ensureTables();
2024
- }
2025
- async disconnect() {
2026
- if (this.db && !this.externalDb) {
2027
- this.db.close();
2028
- this.db = null;
2029
- }
2030
- }
2031
- ensureTables() {
2032
- if (!this.db) return;
2033
- this.db.exec(`
2034
- CREATE TABLE IF NOT EXISTS kyro_users (
2035
- id TEXT PRIMARY KEY,
2036
- email TEXT UNIQUE NOT NULL,
2037
- password_hash TEXT NOT NULL,
2038
- role TEXT NOT NULL DEFAULT 'customer',
2039
- tenant_id TEXT,
2040
- email_verified INTEGER DEFAULT 0,
2041
- locked INTEGER DEFAULT 0,
2042
- last_login TEXT,
2043
- failed_login_attempts INTEGER DEFAULT 0,
2044
- locked_until TEXT,
2045
- created_at TEXT NOT NULL,
2046
- updated_at TEXT NOT NULL
2047
- );
2048
-
2049
- CREATE TABLE IF NOT EXISTS kyro_sessions (
2050
- id TEXT PRIMARY KEY,
2051
- user_id TEXT NOT NULL,
2052
- token TEXT NOT NULL,
2053
- refresh_token TEXT,
2054
- expires_at TEXT NOT NULL,
2055
- created_at TEXT NOT NULL,
2056
- ip_address TEXT,
2057
- user_agent TEXT,
2058
- FOREIGN KEY (user_id) REFERENCES kyro_users(id) ON DELETE CASCADE
2059
- );
2060
-
2061
- CREATE TABLE IF NOT EXISTS kyro_password_history (
2062
- id INTEGER PRIMARY KEY AUTOINCREMENT,
2063
- user_id TEXT NOT NULL,
2064
- password_hash TEXT NOT NULL,
2065
- created_at TEXT NOT NULL,
2066
- FOREIGN KEY (user_id) REFERENCES kyro_users(id) ON DELETE CASCADE
2067
- );
2068
-
2069
- CREATE INDEX IF NOT EXISTS idx_kyro_users_email ON kyro_users(email);
2070
- CREATE INDEX IF NOT EXISTS idx_kyro_sessions_user_id ON kyro_sessions(user_id);
2071
- CREATE INDEX IF NOT EXISTS idx_kyro_sessions_token ON kyro_sessions(token);
2072
- CREATE INDEX IF NOT EXISTS idx_kyro_sessions_refresh_token ON kyro_sessions(refresh_token);
2073
- CREATE INDEX IF NOT EXISTS idx_kyro_password_history_user_id ON kyro_password_history(user_id);
2074
- `);
2075
- }
2076
- async createUser(data) {
2077
- if (!this.db) throw new Error("Not connected");
2078
- const id = crypto.randomBytes(16).toString("hex");
2079
- const now = (/* @__PURE__ */ new Date()).toISOString();
2080
- const user = {
2081
- id,
2082
- email: data.email.toLowerCase(),
2083
- passwordHash: data.passwordHash,
2084
- role: data.role || "customer",
2085
- tenantId: data.tenantId,
2086
- createdAt: now,
2087
- updatedAt: now
2088
- };
2089
- this.db.prepare(
2090
- `INSERT INTO kyro_users (id, email, password_hash, role, tenant_id, created_at, updated_at)
2091
- VALUES (?, ?, ?, ?, ?, ?, ?)`
2092
- ).run(
2093
- id,
2094
- user.email,
2095
- user.passwordHash,
2096
- user.role,
2097
- user.tenantId,
2098
- now,
2099
- now
2100
- );
2101
- return user;
2102
- }
2103
- async findUserByEmail(email) {
2104
- if (!this.db) throw new Error("Not connected");
2105
- const row = this.db.prepare("SELECT * FROM kyro_users WHERE email = ?").get(email.toLowerCase());
2106
- if (!row) return null;
2107
- return this.rowToUser(row);
2108
- }
2109
- async findUserById(userId) {
2110
- if (!this.db) throw new Error("Not connected");
2111
- const row = this.db.prepare("SELECT * FROM kyro_users WHERE id = ?").get(userId);
2112
- if (!row) return null;
2113
- return this.rowToUser(row);
2114
- }
2115
- async updateUser(userId, data) {
2116
- if (!this.db) throw new Error("Not connected");
2117
- const existing = await this.findUserById(userId);
2118
- if (!existing) return null;
2119
- const updates = [];
2120
- const values = [];
2121
- if (data.email !== void 0) {
2122
- updates.push("email = ?");
2123
- values.push(data.email.toLowerCase());
2124
- }
2125
- if (data.passwordHash !== void 0) {
2126
- updates.push("password_hash = ?");
2127
- values.push(data.passwordHash);
2128
- }
2129
- if (data.role !== void 0) {
2130
- updates.push("role = ?");
2131
- values.push(data.role);
2132
- }
2133
- if (data.tenantId !== void 0) {
2134
- updates.push("tenant_id = ?");
2135
- values.push(data.tenantId);
2136
- }
2137
- if (data.emailVerified !== void 0) {
2138
- updates.push("email_verified = ?");
2139
- values.push(data.emailVerified ? 1 : 0);
2140
- }
2141
- if (data.locked !== void 0) {
2142
- updates.push("locked = ?");
2143
- values.push(data.locked ? 1 : 0);
2144
- }
2145
- if (data.lastLogin !== void 0) {
2146
- updates.push("last_login = ?");
2147
- values.push(data.lastLogin);
2148
- }
2149
- if (data.failedLoginAttempts !== void 0) {
2150
- updates.push("failed_login_attempts = ?");
2151
- values.push(data.failedLoginAttempts);
2152
- }
2153
- updates.push("updated_at = ?");
2154
- values.push((/* @__PURE__ */ new Date()).toISOString());
2155
- values.push(userId);
2156
- this.db.prepare(`UPDATE kyro_users SET ${updates.join(", ")} WHERE id = ?`).run(...values);
2157
- return this.findUserById(userId);
2158
- }
2159
- async deleteUser(userId) {
2160
- if (!this.db) throw new Error("Not connected");
2161
- const result = this.db.prepare("DELETE FROM kyro_users WHERE id = ?").run(userId);
2162
- return result.changes > 0;
2163
- }
2164
- async hashPassword(password) {
2165
- return bcrypt__default.default.hash(password, this.saltRounds);
2166
- }
2167
- async verifyPassword(password, hash) {
2168
- return bcrypt__default.default.compare(password, hash);
2169
- }
2170
- async createSession(userId, data = {}) {
2171
- if (!this.db) throw new Error("Not connected");
2172
- const id = crypto.randomBytes(32).toString("hex");
2173
- const token = crypto.randomBytes(32).toString("base64url");
2174
- const refreshToken = crypto.randomBytes(32).toString("base64url");
2175
- const now = /* @__PURE__ */ new Date();
2176
- const expiresAt = new Date(now.getTime() + 864e5).toISOString();
2177
- const session = {
2178
- id,
2179
- userId,
2180
- token,
2181
- refreshToken,
2182
- expiresAt,
2183
- createdAt: now.toISOString(),
2184
- ipAddress: data.ipAddress,
2185
- userAgent: data.userAgent
2186
- };
2187
- this.db.prepare(
2188
- `INSERT INTO kyro_sessions (id, user_id, token, refresh_token, expires_at, created_at, ip_address, user_agent)
2189
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
2190
- ).run(
2191
- session.id,
2192
- session.userId,
2193
- session.token,
2194
- session.refreshToken,
2195
- session.expiresAt,
2196
- session.createdAt,
2197
- session.ipAddress,
2198
- session.userAgent
2199
- );
2200
- return session;
2201
- }
2202
- async findSessionByToken(token) {
2203
- if (!this.db) throw new Error("Not connected");
2204
- const row = this.db.prepare("SELECT * FROM kyro_sessions WHERE token = ?").get(token);
2205
- if (!row) return null;
2206
- return this.rowToSession(row);
2207
- }
2208
- async findSessionByRefreshToken(refreshToken) {
2209
- if (!this.db) throw new Error("Not connected");
2210
- const row = this.db.prepare("SELECT * FROM kyro_sessions WHERE refresh_token = ?").get(refreshToken);
2211
- if (!row) return null;
2212
- return this.rowToSession(row);
2213
- }
2214
- async deleteSession(sessionId) {
2215
- if (!this.db) throw new Error("Not connected");
2216
- const result = this.db.prepare("DELETE FROM kyro_sessions WHERE id = ? OR token = ?").run(sessionId, sessionId);
2217
- return result.changes > 0;
2218
- }
2219
- async deleteUserSessions(userId) {
2220
- if (!this.db) throw new Error("Not connected");
2221
- const result = this.db.prepare("DELETE FROM kyro_sessions WHERE user_id = ?").run(userId);
2222
- return result.changes;
2223
- }
2224
- async hasAnyUsers() {
2225
- if (!this.db) throw new Error("Not connected");
2226
- const row = this.db.prepare("SELECT COUNT(*) as count FROM kyro_users").get();
2227
- return row.count > 0;
2228
- }
2229
- async addPasswordToHistory(userId, passwordHash) {
2230
- if (!this.db) throw new Error("Not connected");
2231
- this.db.prepare(
2232
- "INSERT INTO kyro_password_history (user_id, password_hash, created_at) VALUES (?, ?, ?)"
2233
- ).run(userId, passwordHash, (/* @__PURE__ */ new Date()).toISOString());
2234
- this.db.prepare(
2235
- `DELETE FROM kyro_password_history WHERE id IN (
2236
- SELECT id FROM kyro_password_history WHERE user_id = ? ORDER BY created_at DESC LIMIT -1 OFFSET 5
2237
- )`
2238
- ).run(userId);
2239
- }
2240
- async getPasswordHistory(userId, count = 5) {
2241
- if (!this.db) throw new Error("Not connected");
2242
- const rows = this.db.prepare(
2243
- "SELECT password_hash FROM kyro_password_history WHERE user_id = ? ORDER BY created_at DESC LIMIT ?"
2244
- ).all(userId, count);
2245
- return rows.map((r) => r.password_hash);
2246
- }
2247
- rowToUser(row) {
2248
- return {
2249
- id: row.id,
2250
- email: row.email,
2251
- passwordHash: row.password_hash,
2252
- role: row.role,
2253
- tenantId: row.tenant_id,
2254
- emailVerified: row.email_verified === 1,
2255
- locked: row.locked === 1,
2256
- lastLogin: row.last_login,
2257
- failedLoginAttempts: row.failed_login_attempts || 0,
2258
- createdAt: row.created_at,
2259
- updatedAt: row.updated_at
2260
- };
2261
- }
2262
- rowToSession(row) {
2263
- return {
2264
- id: row.id,
2265
- userId: row.user_id,
2266
- token: row.token,
2267
- refreshToken: row.refresh_token,
2268
- expiresAt: row.expires_at,
2269
- createdAt: row.created_at,
2270
- ipAddress: row.ip_address,
2271
- userAgent: row.user_agent
2272
- };
2273
- }
2274
- };
2275
2003
 
2276
2004
  // src/auth/security/lockout.ts
2277
2005
  var DEFAULT_LOCKOUT_CONFIG = {
@@ -2538,6 +2266,8 @@ var RateLimiter = class {
2538
2266
  this.userLimits[type] = config;
2539
2267
  }
2540
2268
  };
2269
+ var DEFAULT_RETENTION_CONFIG = {
2270
+ retentionDays: 30};
2541
2271
  var AuditLogger = class {
2542
2272
  redis;
2543
2273
  prefix;
@@ -2768,6 +2498,572 @@ function createAuditContext(req) {
2768
2498
  userAgent: req.headers.get("user-agent") || "unknown"
2769
2499
  };
2770
2500
  }
2501
+ var InMemoryAuthAdapter = class {
2502
+ users = /* @__PURE__ */ new Map();
2503
+ sessions = /* @__PURE__ */ new Map();
2504
+ refreshTokens = /* @__PURE__ */ new Map();
2505
+ // refreshToken -> sessionId
2506
+ emailToUserId = /* @__PURE__ */ new Map();
2507
+ passwordHistory = /* @__PURE__ */ new Map();
2508
+ // userId -> passwordHash[]
2509
+ externalDb = false;
2510
+ constructor() {
2511
+ }
2512
+ async connect() {
2513
+ }
2514
+ async disconnect() {
2515
+ this.users.clear();
2516
+ this.sessions.clear();
2517
+ this.refreshTokens.clear();
2518
+ this.emailToUserId.clear();
2519
+ this.passwordHistory.clear();
2520
+ }
2521
+ async createUser(data) {
2522
+ const userId = crypto.randomBytes(16).toString("hex");
2523
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2524
+ const user = {
2525
+ id: userId,
2526
+ email: data.email.toLowerCase(),
2527
+ passwordHash: data.passwordHash,
2528
+ role: data.role || "customer",
2529
+ tenantId: data.tenantId,
2530
+ createdAt: now,
2531
+ updatedAt: now
2532
+ };
2533
+ this.users.set(userId, user);
2534
+ this.emailToUserId.set(data.email.toLowerCase(), userId);
2535
+ this.passwordHistory.set(userId, []);
2536
+ return user;
2537
+ }
2538
+ async findUserByEmail(email) {
2539
+ const userId = this.emailToUserId.get(email.toLowerCase());
2540
+ if (!userId) return null;
2541
+ return this.findUserById(userId);
2542
+ }
2543
+ async findUserById(userId) {
2544
+ return this.users.get(userId) || null;
2545
+ }
2546
+ async updateUser(userId, data) {
2547
+ const existing = await this.findUserById(userId);
2548
+ if (!existing) return null;
2549
+ const updated = {
2550
+ ...existing,
2551
+ ...data,
2552
+ id: userId,
2553
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2554
+ };
2555
+ if (data.email && data.email !== existing.email) {
2556
+ this.emailToUserId.delete(existing.email.toLowerCase());
2557
+ this.emailToUserId.set(data.email.toLowerCase(), userId);
2558
+ }
2559
+ this.users.set(userId, updated);
2560
+ return updated;
2561
+ }
2562
+ async deleteUser(userId) {
2563
+ const user = await this.findUserById(userId);
2564
+ if (!user) return false;
2565
+ this.users.delete(userId);
2566
+ this.emailToUserId.delete(user.email.toLowerCase());
2567
+ this.refreshTokens.forEach((sessionId, refreshToken) => {
2568
+ if (this.sessions.get(sessionId)?.userId === userId) {
2569
+ this.refreshTokens.delete(refreshToken);
2570
+ this.sessions.delete(sessionId);
2571
+ }
2572
+ });
2573
+ this.passwordHistory.delete(userId);
2574
+ this.sessions.forEach((session, sessionId) => {
2575
+ if (session.userId === userId) {
2576
+ this.sessions.delete(sessionId);
2577
+ }
2578
+ });
2579
+ return true;
2580
+ }
2581
+ async hashPassword(password) {
2582
+ const bcrypt2 = (await import('bcryptjs')).default;
2583
+ return bcrypt2.hash(password, 12);
2584
+ }
2585
+ async verifyPassword(password, hash) {
2586
+ const bcrypt2 = (await import('bcryptjs')).default;
2587
+ return bcrypt2.compare(password, hash);
2588
+ }
2589
+ async createSession(userId, data = {}) {
2590
+ const sessionId = crypto.randomBytes(32).toString("hex");
2591
+ const token = crypto.randomBytes(32).toString("base64url");
2592
+ const refreshToken = crypto.randomBytes(32).toString("base64url");
2593
+ const now = /* @__PURE__ */ new Date();
2594
+ const session = {
2595
+ id: sessionId,
2596
+ userId,
2597
+ token,
2598
+ refreshToken,
2599
+ expiresAt: new Date(now.getTime() + 86400 * 1e3).toISOString(),
2600
+ // 24 hours
2601
+ createdAt: now.toISOString(),
2602
+ ipAddress: data.ipAddress,
2603
+ userAgent: data.userAgent
2604
+ };
2605
+ this.sessions.set(sessionId, session);
2606
+ this.refreshTokens.set(refreshToken, sessionId);
2607
+ return session;
2608
+ }
2609
+ async findSessionByToken(token) {
2610
+ return this.sessions.get(token) || null;
2611
+ }
2612
+ async deleteSession(sessionId) {
2613
+ const session = this.sessions.get(sessionId);
2614
+ if (!session) return false;
2615
+ if (session.refreshToken) {
2616
+ this.refreshTokens.delete(session.refreshToken);
2617
+ }
2618
+ this.sessions.delete(sessionId);
2619
+ return true;
2620
+ }
2621
+ async deleteUserSessions(userId) {
2622
+ let deleted = 0;
2623
+ this.sessions.forEach((session, sessionId) => {
2624
+ if (session.userId === userId) {
2625
+ if (session.refreshToken) {
2626
+ this.refreshTokens.delete(session.refreshToken);
2627
+ }
2628
+ this.sessions.delete(sessionId);
2629
+ deleted++;
2630
+ }
2631
+ });
2632
+ return deleted;
2633
+ }
2634
+ async addPasswordToHistory(userId, passwordHash) {
2635
+ const history = this.passwordHistory.get(userId) || [];
2636
+ history.push(passwordHash);
2637
+ if (history.length > 5) {
2638
+ history.splice(0, history.length - 5);
2639
+ }
2640
+ this.passwordHistory.set(userId, history);
2641
+ }
2642
+ async getPasswordHistory(userId, count = 5) {
2643
+ return this.passwordHistory.get(userId) || [];
2644
+ }
2645
+ async isPasswordInHistory(password, userId, historyCount = 5) {
2646
+ const history = await this.getPasswordHistory(userId, historyCount);
2647
+ const bcrypt2 = (await import('bcryptjs')).default;
2648
+ for (const hash of history) {
2649
+ if (await bcrypt2.compare(password, hash)) {
2650
+ return true;
2651
+ }
2652
+ }
2653
+ return false;
2654
+ }
2655
+ async hasAnyUsers() {
2656
+ return this.users.size > 0;
2657
+ }
2658
+ };
2659
+
2660
+ // src/auth/security/in-memory-rate-limit.ts
2661
+ var InMemoryRateLimiter = class {
2662
+ storage = /* @__PURE__ */ new Map();
2663
+ userStorage = /* @__PURE__ */ new Map();
2664
+ limits;
2665
+ userLimits;
2666
+ constructor(limits, userLimits) {
2667
+ this.limits = { ...DEFAULT_RATE_LIMITS2, ...limits };
2668
+ this.userLimits = userLimits || {
2669
+ "user:api": { window: 6e4, max: 500 },
2670
+ "user:write": { window: 36e5, max: 100 }
2671
+ };
2672
+ }
2673
+ getKey(type, identifier) {
2674
+ return `${type}:${identifier}`;
2675
+ }
2676
+ getUserKey(type, userId, identifier) {
2677
+ return `user:${type}:${userId}:${identifier}`;
2678
+ }
2679
+ cleanupOldEntries(entries, window) {
2680
+ const now = Date.now();
2681
+ const windowStart = now - window;
2682
+ while (entries.length > 0 && entries[0].timestamp < windowStart) {
2683
+ entries.shift();
2684
+ }
2685
+ }
2686
+ async check(type, identifier) {
2687
+ const config = this.limits[type] || this.limits["api:general"];
2688
+ const key = this.getKey(type, identifier);
2689
+ let entries = this.storage.get(key);
2690
+ if (!entries) {
2691
+ entries = [];
2692
+ this.storage.set(key, entries);
2693
+ }
2694
+ this.cleanupOldEntries(entries, config.window);
2695
+ const now = Date.now();
2696
+ const count = entries.reduce((sum, entry) => sum + entry.count, 0);
2697
+ entries.push({ timestamp: now, count: 1 });
2698
+ if (count >= config.max) {
2699
+ const oldestEntry = entries.reduce(
2700
+ (oldest, current) => oldest.timestamp < current.timestamp ? oldest : current,
2701
+ entries[0]
2702
+ );
2703
+ const resetAt = oldestEntry.timestamp + config.window;
2704
+ return {
2705
+ allowed: false,
2706
+ remaining: 0,
2707
+ resetAt,
2708
+ retryAfter: Math.ceil((resetAt - now) / 1e3)
2709
+ };
2710
+ }
2711
+ return {
2712
+ allowed: true,
2713
+ remaining: config.max - count - 1,
2714
+ resetAt: now + config.window
2715
+ };
2716
+ }
2717
+ async checkUser(type, userId, identifier) {
2718
+ const config = this.userLimits[type] || this.userLimits["user:api"];
2719
+ const userMap = this.userStorage.get(userId);
2720
+ let entries = [];
2721
+ if (userMap) {
2722
+ entries = userMap.get(this.getKey(type, identifier)) || [];
2723
+ } else {
2724
+ if (!this.userStorage.has(userId)) {
2725
+ this.userStorage.set(userId, /* @__PURE__ */ new Map());
2726
+ }
2727
+ this.userStorage.get(userId).set(this.getKey(type, identifier), entries);
2728
+ }
2729
+ this.cleanupOldEntries(entries, config.window);
2730
+ const now = Date.now();
2731
+ const count = entries.reduce((sum, entry) => sum + entry.count, 0);
2732
+ entries.push({ timestamp: now, count: 1 });
2733
+ if (count >= config.max) {
2734
+ const oldestEntry = entries.reduce(
2735
+ (oldest, current) => oldest.timestamp < current.timestamp ? oldest : current,
2736
+ entries[0]
2737
+ );
2738
+ const resetAt = oldestEntry.timestamp + config.window;
2739
+ return {
2740
+ allowed: false,
2741
+ remaining: 0,
2742
+ resetAt,
2743
+ retryAfter: Math.ceil((resetAt - now) / 1e3)
2744
+ };
2745
+ }
2746
+ return {
2747
+ allowed: true,
2748
+ remaining: config.max - count - 1,
2749
+ resetAt: now + config.window
2750
+ };
2751
+ }
2752
+ async reset(type, identifier) {
2753
+ const key = this.getKey(type, identifier);
2754
+ this.storage.delete(key);
2755
+ }
2756
+ async resetUser(type, userId, identifier) {
2757
+ const userMap = this.userStorage.get(userId);
2758
+ if (userMap) {
2759
+ const key = this.getKey(type, identifier);
2760
+ userMap.delete(key);
2761
+ }
2762
+ }
2763
+ async getStatus(type, identifier) {
2764
+ const config = this.limits[type] || this.limits["api:general"];
2765
+ const key = this.getKey(type, identifier);
2766
+ let entries = this.storage.get(key);
2767
+ if (!entries) {
2768
+ entries = [];
2769
+ this.storage.set(key, entries);
2770
+ }
2771
+ this.cleanupOldEntries(entries, config.window);
2772
+ const now = Date.now();
2773
+ const count = entries.reduce((sum, entry) => sum + entry.count, 0);
2774
+ return {
2775
+ count,
2776
+ limit: config.max,
2777
+ remaining: Math.max(0, config.max - count),
2778
+ resetAt: now + config.window
2779
+ };
2780
+ }
2781
+ setLimit(type, config) {
2782
+ this.limits[type] = config;
2783
+ }
2784
+ setUserLimit(type, config) {
2785
+ this.userLimits[type] = config;
2786
+ }
2787
+ };
2788
+ var DEFAULT_RATE_LIMITS2 = {
2789
+ "auth:login": { window: 9e5, max: 5 },
2790
+ "auth:register": { window: 36e5, max: 3 },
2791
+ "auth:forgot": { window: 36e5, max: 3 },
2792
+ "auth:reset": { window: 36e5, max: 5 },
2793
+ "auth:verify": { window: 36e5, max: 5 },
2794
+ "api:general": { window: 6e4, max: 100 },
2795
+ "api:authenticated": { window: 6e4, max: 200 }
2796
+ };
2797
+
2798
+ // src/auth/security/in-memory-lockout.ts
2799
+ var InMemoryAccountLockout = class {
2800
+ storage = /* @__PURE__ */ new Map();
2801
+ history = /* @__PURE__ */ new Map();
2802
+ // userId -> attempt timestamps
2803
+ config;
2804
+ constructor(config = {}) {
2805
+ this.config = {
2806
+ maxAttempts: 5,
2807
+ lockDuration: 9e5,
2808
+ // 15 minutes
2809
+ notifyUser: true,
2810
+ notifyAdmin: true,
2811
+ adminNotifyAfter: 3,
2812
+ ...config
2813
+ };
2814
+ }
2815
+ async checkLockout(userId) {
2816
+ const now = Date.now();
2817
+ const record = this.storage.get(userId);
2818
+ if (record && record.lockedUntil !== null && record.lockedUntil <= now) {
2819
+ await this.resetAttempts(userId);
2820
+ return {
2821
+ locked: false,
2822
+ attemptsRemaining: this.config.maxAttempts,
2823
+ totalAttempts: 0
2824
+ };
2825
+ }
2826
+ if (!record) {
2827
+ return {
2828
+ locked: false,
2829
+ attemptsRemaining: this.config.maxAttempts,
2830
+ totalAttempts: 0
2831
+ };
2832
+ }
2833
+ const { attempts, lockedUntil } = record;
2834
+ if (lockedUntil !== null && lockedUntil > now) {
2835
+ return {
2836
+ locked: true,
2837
+ attemptsRemaining: 0,
2838
+ lockedUntil: new Date(lockedUntil),
2839
+ totalAttempts: attempts
2840
+ };
2841
+ }
2842
+ return {
2843
+ locked: false,
2844
+ attemptsRemaining: Math.max(0, this.config.maxAttempts - attempts),
2845
+ totalAttempts: attempts
2846
+ };
2847
+ }
2848
+ async recordFailedAttempt(userId) {
2849
+ const now = Date.now();
2850
+ const record = this.storage.get(userId) || {
2851
+ attempts: 0,
2852
+ lastAttempt: null,
2853
+ lockedAt: null,
2854
+ lockedUntil: null
2855
+ };
2856
+ record.attempts += 1;
2857
+ record.lastAttempt = now;
2858
+ let history = this.history.get(userId) || [];
2859
+ history.push(now);
2860
+ if (history.length > 100) {
2861
+ history.splice(0, history.length - 100);
2862
+ }
2863
+ this.history.set(userId, history);
2864
+ this.storage.set(userId, record);
2865
+ if (record.attempts >= this.config.maxAttempts) {
2866
+ const lockedUntil = new Date(now + this.config.lockDuration);
2867
+ record.lockedAt = now;
2868
+ record.lockedUntil = lockedUntil.getTime();
2869
+ this.storage.set(userId, record);
2870
+ return {
2871
+ locked: true,
2872
+ attemptsRemaining: 0,
2873
+ lockedUntil,
2874
+ totalAttempts: record.attempts
2875
+ };
2876
+ }
2877
+ return {
2878
+ locked: false,
2879
+ attemptsRemaining: Math.max(0, this.config.maxAttempts - record.attempts),
2880
+ totalAttempts: record.attempts
2881
+ };
2882
+ }
2883
+ async lockAccount(userId, duration) {
2884
+ const now = Date.now();
2885
+ const lockDuration = duration || this.config.lockDuration;
2886
+ const lockedUntil = new Date(now + lockDuration);
2887
+ const record = this.storage.get(userId) || {
2888
+ attempts: 0,
2889
+ lastAttempt: null,
2890
+ lockedAt: null,
2891
+ lockedUntil: null
2892
+ };
2893
+ record.attempts = this.config.maxAttempts;
2894
+ record.lockedAt = now;
2895
+ record.lockedUntil = lockedUntil.getTime();
2896
+ this.storage.set(userId, record);
2897
+ }
2898
+ async unlockAccount(userId) {
2899
+ await this.resetAttempts(userId);
2900
+ }
2901
+ async resetAttempts(userId) {
2902
+ const record = this.storage.get(userId);
2903
+ if (record) {
2904
+ record.attempts = 0;
2905
+ record.lockedAt = null;
2906
+ record.lockedUntil = null;
2907
+ this.storage.set(userId, record);
2908
+ }
2909
+ this.history.delete(userId);
2910
+ }
2911
+ async getLockoutHistory(userId, limit = 10) {
2912
+ const history = this.history.get(userId) || [];
2913
+ return history.slice(-limit).reverse().map((timestamp) => new Date(timestamp));
2914
+ }
2915
+ async getLockoutStats(userId) {
2916
+ const history = this.history.get(userId) || [];
2917
+ const totalFailedAttempts = history.length;
2918
+ const lockoutCount = Math.floor(
2919
+ totalFailedAttempts / this.config.maxAttempts
2920
+ );
2921
+ let lastLockout = null;
2922
+ const record = this.storage.get(userId);
2923
+ if (record && record.lockedAt !== null) {
2924
+ lastLockout = new Date(record.lockedAt);
2925
+ }
2926
+ const averageAttemptsBeforeLockout = lockoutCount > 0 ? this.config.maxAttempts : 0;
2927
+ return {
2928
+ totalFailedAttempts,
2929
+ lockoutCount,
2930
+ lastLockout,
2931
+ averageAttemptsBeforeLockout
2932
+ };
2933
+ }
2934
+ shouldNotifyAdmin(currentAttempts) {
2935
+ return this.config.notifyAdmin && currentAttempts >= this.config.adminNotifyAfter;
2936
+ }
2937
+ getConfig() {
2938
+ return { ...this.config };
2939
+ }
2940
+ setConfig(config) {
2941
+ this.config = { ...this.config, ...config };
2942
+ }
2943
+ };
2944
+ var InMemoryAuditLogger = class {
2945
+ logs = [];
2946
+ retentionDays;
2947
+ constructor(retentionDays = DEFAULT_RETENTION_CONFIG.retentionDays) {
2948
+ this.retentionDays = retentionDays;
2949
+ }
2950
+ async log(data) {
2951
+ const id = crypto.randomBytes(16).toString("hex");
2952
+ const timestamp = /* @__PURE__ */ new Date();
2953
+ const log = {
2954
+ ...data,
2955
+ id,
2956
+ timestamp
2957
+ };
2958
+ this.logs.push(log);
2959
+ this.cleanupOldLogs();
2960
+ return id;
2961
+ }
2962
+ async get(id) {
2963
+ return this.logs.find((log) => log.id === id) || null;
2964
+ }
2965
+ async query(filter = {}) {
2966
+ const { limit = 50, offset = 0 } = filter;
2967
+ let filteredLogs = [...this.logs];
2968
+ if (filter.userId) {
2969
+ filteredLogs = filteredLogs.filter((log) => log.userId === filter.userId);
2970
+ }
2971
+ if (filter.action) {
2972
+ const actions = Array.isArray(filter.action) ? filter.action : [filter.action];
2973
+ filteredLogs = filteredLogs.filter((log) => actions.includes(log.action));
2974
+ }
2975
+ if (filter.resource) {
2976
+ filteredLogs = filteredLogs.filter(
2977
+ (log) => log.resource === filter.resource
2978
+ );
2979
+ }
2980
+ if (filter.resourceId) {
2981
+ filteredLogs = filteredLogs.filter(
2982
+ (log) => log.resourceId === filter.resourceId
2983
+ );
2984
+ }
2985
+ if (filter.success !== void 0) {
2986
+ filteredLogs = filteredLogs.filter(
2987
+ (log) => log.success === filter.success
2988
+ );
2989
+ }
2990
+ const startDate = filter.startDate;
2991
+ const endDate = filter.endDate;
2992
+ if (startDate !== void 0) {
2993
+ filteredLogs = filteredLogs.filter((log) => log.timestamp >= startDate);
2994
+ }
2995
+ if (endDate !== void 0) {
2996
+ filteredLogs = filteredLogs.filter((log) => log.timestamp <= endDate);
2997
+ }
2998
+ filteredLogs.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
2999
+ const total = filteredLogs.length;
3000
+ const paginatedLogs = filteredLogs.slice(offset, offset + limit);
3001
+ return { logs: paginatedLogs, total };
3002
+ }
3003
+ async getRecent(limit = 50) {
3004
+ const cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
3005
+ const recentLogs = this.logs.filter((log) => log.timestamp >= cutoffDate).sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()).slice(0, limit);
3006
+ return recentLogs;
3007
+ }
3008
+ async getUserActivity(userId, limit = 50) {
3009
+ const userLogs = this.logs.filter((log) => log.userId === userId).sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()).slice(0, limit);
3010
+ return userLogs;
3011
+ }
3012
+ async getStats(startDate, endDate) {
3013
+ const start = startDate || new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
3014
+ const end = endDate || /* @__PURE__ */ new Date();
3015
+ const filteredLogs = this.logs.filter(
3016
+ (log) => log.timestamp >= start && log.timestamp <= end
3017
+ );
3018
+ const byAction = {};
3019
+ let totalEvents = 0;
3020
+ let failedLogins = 0;
3021
+ let successCount = 0;
3022
+ const uniqueUsers = /* @__PURE__ */ new Set();
3023
+ for (const log of filteredLogs) {
3024
+ totalEvents++;
3025
+ const action = log.action;
3026
+ byAction[action] = (byAction[action] || 0) + 1;
3027
+ if (log.success) {
3028
+ successCount++;
3029
+ }
3030
+ if (log.action === "login_failed") {
3031
+ failedLogins++;
3032
+ }
3033
+ if (log.userId) {
3034
+ uniqueUsers.add(log.userId);
3035
+ }
3036
+ }
3037
+ return {
3038
+ totalEvents,
3039
+ byAction,
3040
+ successRate: totalEvents > 0 ? successCount / totalEvents : 1,
3041
+ failedLogins,
3042
+ uniqueUsers
3043
+ };
3044
+ }
3045
+ async cleanup() {
3046
+ const cutoffDate = new Date(
3047
+ Date.now() - this.retentionDays * 24 * 60 * 60 * 1e3
3048
+ );
3049
+ const initialCount = this.logs.length;
3050
+ this.logs = this.logs.filter((log) => log.timestamp >= cutoffDate);
3051
+ return initialCount - this.logs.length;
3052
+ }
3053
+ cleanupOldLogs() {
3054
+ const cutoffDate = new Date(
3055
+ Date.now() - this.retentionDays * 24 * 60 * 60 * 1e3
3056
+ );
3057
+ this.logs.length;
3058
+ this.logs = this.logs.filter((log) => log.timestamp >= cutoffDate);
3059
+ }
3060
+ };
3061
+ function createAuditContext2(req) {
3062
+ return {
3063
+ ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || req.headers.get("x-real-ip") || "unknown",
3064
+ userAgent: req.headers.get("user-agent") || "unknown"
3065
+ };
3066
+ }
2771
3067
  function defaultExtractToken(req) {
2772
3068
  const authHeader = req.headers.get("Authorization");
2773
3069
  if (authHeader?.startsWith("Bearer ")) {
@@ -2795,7 +3091,7 @@ function generateToken(payload, secret, options = {}) {
2795
3091
 
2796
3092
  // src/api/rest/auth-routes.ts
2797
3093
  var AuthRoutes = class {
2798
- redis;
3094
+ authAdapter;
2799
3095
  email;
2800
3096
  jwtSecret;
2801
3097
  jwtExpiresIn;
@@ -2808,13 +3104,13 @@ var AuthRoutes = class {
2808
3104
  baseUrl;
2809
3105
  emailVerificationRequired;
2810
3106
  constructor(config) {
2811
- this.redis = config.redis;
3107
+ this.authAdapter = config.redis;
2812
3108
  this.email = config.email;
2813
3109
  this.jwtSecret = config.jwtSecret;
2814
3110
  this.jwtExpiresIn = config.jwtExpiresIn || "24h";
2815
3111
  this.jwtIssuer = config.jwtIssuer;
2816
3112
  this.jwtAudience = config.jwtAudience;
2817
- this.passwordPolicy = config.passwordPolicy || new chunkI4BORBXT_cjs.PasswordPolicy();
3113
+ this.passwordPolicy = config.passwordPolicy || new chunk3EVLFWH2_cjs.PasswordPolicy();
2818
3114
  this.lockout = config.lockout;
2819
3115
  this.rateLimiter = config.rateLimiter;
2820
3116
  this.auditLogger = config.auditLogger;
@@ -2822,7 +3118,7 @@ var AuthRoutes = class {
2822
3118
  this.emailVerificationRequired = config.emailVerificationRequired ?? true;
2823
3119
  }
2824
3120
  async register(req) {
2825
- const { ipAddress, userAgent } = createAuditContext(req);
3121
+ const { ipAddress, userAgent } = createAuditContext2(req);
2826
3122
  if (this.rateLimiter) {
2827
3123
  const limit = await this.rateLimiter.check("auth:register", ipAddress);
2828
3124
  if (!limit.allowed) {
@@ -2841,12 +3137,12 @@ var AuthRoutes = class {
2841
3137
  if (!passwordValidation.valid) {
2842
3138
  return this.errorResponse(passwordValidation.errors.join(". "), 400);
2843
3139
  }
2844
- const existingUser = await this.redis.findUserByEmail(body.email);
3140
+ const existingUser = await this.authAdapter.findUserByEmail(body.email);
2845
3141
  if (existingUser) {
2846
3142
  return this.errorResponse("Email already registered", 400);
2847
3143
  }
2848
- const passwordHash = await this.redis.hashPassword(body.password);
2849
- const user = await this.redis.createUser({
3144
+ const passwordHash = await this.authAdapter.hashPassword(body.password);
3145
+ const user = await this.authAdapter.createUser({
2850
3146
  email: body.email,
2851
3147
  passwordHash,
2852
3148
  role: body.role || "customer",
@@ -2855,7 +3151,7 @@ var AuthRoutes = class {
2855
3151
  if (this.emailVerificationRequired && this.email) {
2856
3152
  const verificationToken = crypto.randomBytes(32).toString("hex");
2857
3153
  const verificationUrl = `${this.baseUrl}/api/auth/verify?token=${verificationToken}`;
2858
- await this.redis.createSession(user.id, { ipAddress, userAgent });
3154
+ await this.authAdapter.createSession(user.id, { ipAddress, userAgent });
2859
3155
  const template = this.email.getTemplates().verifyEmail(verificationUrl, body.email);
2860
3156
  await this.email.send({ to: body.email, ...template });
2861
3157
  }
@@ -2884,7 +3180,7 @@ var AuthRoutes = class {
2884
3180
  }
2885
3181
  }
2886
3182
  async login(req) {
2887
- const { ipAddress, userAgent } = createAuditContext(req);
3183
+ const { ipAddress, userAgent } = createAuditContext2(req);
2888
3184
  if (this.rateLimiter) {
2889
3185
  const limit = await this.rateLimiter.check("auth:login", ipAddress);
2890
3186
  if (!limit.allowed) {
@@ -2896,7 +3192,7 @@ var AuthRoutes = class {
2896
3192
  if (!body.email || !body.password) {
2897
3193
  return this.errorResponse("Email and password are required", 400);
2898
3194
  }
2899
- const user = await this.redis.findUserByEmail(body.email);
3195
+ const user = await this.authAdapter.findUserByEmail(body.email);
2900
3196
  if (!user) {
2901
3197
  await this.recordFailedLogin(ipAddress, userAgent);
2902
3198
  return this.errorResponse("Invalid credentials", 401);
@@ -2922,7 +3218,10 @@ var AuthRoutes = class {
2922
3218
  );
2923
3219
  }
2924
3220
  }
2925
- const validPassword = user.passwordHash ? await this.redis.verifyPassword(body.password, user.passwordHash) : false;
3221
+ const validPassword = user.passwordHash ? await this.authAdapter.verifyPassword(
3222
+ body.password,
3223
+ user.passwordHash
3224
+ ) : false;
2926
3225
  if (!validPassword) {
2927
3226
  await this.recordFailedLogin(ipAddress, userAgent, user.id, user.email);
2928
3227
  return this.errorResponse("Invalid credentials", 401);
@@ -2930,7 +3229,7 @@ var AuthRoutes = class {
2930
3229
  if (this.lockout) {
2931
3230
  await this.lockout.resetAttempts(user.id);
2932
3231
  }
2933
- const session = await this.redis.createSession(user.id, {
3232
+ const session = await this.authAdapter.createSession(user.id, {
2934
3233
  ipAddress,
2935
3234
  userAgent
2936
3235
  });
@@ -2959,7 +3258,7 @@ var AuthRoutes = class {
2959
3258
  success: true
2960
3259
  });
2961
3260
  }
2962
- await this.redis.updateUser(user.id, {
3261
+ await this.authAdapter.updateUser(user.id, {
2963
3262
  lastLogin: (/* @__PURE__ */ new Date()).toISOString()
2964
3263
  });
2965
3264
  return this.jsonResponse({
@@ -2978,11 +3277,11 @@ var AuthRoutes = class {
2978
3277
  if (!token) {
2979
3278
  return this.errorResponse("No session to logout", 401);
2980
3279
  }
2981
- const { ipAddress, userAgent } = createAuditContext(req);
3280
+ const { ipAddress, userAgent } = createAuditContext2(req);
2982
3281
  try {
2983
3282
  const payload = jwt2__default.default.decode(token);
2984
3283
  if (payload && payload.sub) {
2985
- await this.redis.deleteUserSessions(payload.sub);
3284
+ await this.authAdapter.deleteUserSessions(payload.sub);
2986
3285
  if (this.auditLogger) {
2987
3286
  await this.auditLogger.log({
2988
3287
  action: "logout",
@@ -3025,7 +3324,7 @@ var AuthRoutes = class {
3025
3324
  issuer: this.jwtIssuer,
3026
3325
  audience: this.jwtAudience
3027
3326
  });
3028
- const user = await this.redis.findUserById(payload.sub);
3327
+ const user = await this.authAdapter.findUserById(payload.sub);
3029
3328
  if (!user) {
3030
3329
  return this.errorResponse("User not found", 404);
3031
3330
  }
@@ -3042,7 +3341,7 @@ var AuthRoutes = class {
3042
3341
  if (!token) {
3043
3342
  return this.errorResponse("Not authenticated", 401);
3044
3343
  }
3045
- const { ipAddress, userAgent } = createAuditContext(req);
3344
+ const { ipAddress, userAgent } = createAuditContext2(req);
3046
3345
  try {
3047
3346
  const payload = jwt2__default.default.verify(token, this.jwtSecret);
3048
3347
  const body = await req.json();
@@ -3057,16 +3356,22 @@ var AuthRoutes = class {
3057
3356
  if (!passwordValidation.valid) {
3058
3357
  return this.errorResponse(passwordValidation.errors.join(". "), 400);
3059
3358
  }
3060
- const user = await this.redis.findUserById(payload.sub);
3359
+ const user = await this.authAdapter.findUserById(payload.sub);
3061
3360
  if (!user) {
3062
3361
  return this.errorResponse("User not found", 404);
3063
3362
  }
3064
- const validPassword = user.passwordHash ? await this.redis.verifyPassword(currentPassword, user.passwordHash) : false;
3363
+ const validPassword = user.passwordHash ? await this.authAdapter.verifyPassword(
3364
+ currentPassword,
3365
+ user.passwordHash
3366
+ ) : false;
3065
3367
  if (!validPassword) {
3066
3368
  return this.errorResponse("Current password is incorrect", 401);
3067
3369
  }
3068
- const passwordHistory = await this.redis.getPasswordHistory(user.id, 5);
3069
- const isReused = await this.redis.isPasswordInHistory(
3370
+ const passwordHistory = await this.authAdapter.getPasswordHistory?.(
3371
+ user.id,
3372
+ 5
3373
+ );
3374
+ const isReused = await this.authAdapter.isPasswordInHistory?.(
3070
3375
  newPassword,
3071
3376
  user.id,
3072
3377
  5
@@ -3077,12 +3382,17 @@ var AuthRoutes = class {
3077
3382
  400
3078
3383
  );
3079
3384
  }
3080
- const newPasswordHash = await this.redis.hashPassword(newPassword);
3385
+ const newPasswordHash = await this.authAdapter.hashPassword(newPassword);
3081
3386
  if (user.passwordHash) {
3082
- await this.redis.addPasswordToHistory(user.id, user.passwordHash);
3387
+ await this.authAdapter.addPasswordToHistory?.(
3388
+ user.id,
3389
+ user.passwordHash
3390
+ );
3083
3391
  }
3084
- await this.redis.updateUser(user.id, { passwordHash: newPasswordHash });
3085
- await this.redis.deleteUserSessions(user.id);
3392
+ await this.authAdapter.updateUser(user.id, {
3393
+ passwordHash: newPasswordHash
3394
+ });
3395
+ await this.authAdapter.deleteUserSessions(user.id);
3086
3396
  if (this.email && this.email.getTemplates) {
3087
3397
  const template = this.email.getTemplates().passwordChanged(user.email);
3088
3398
  await this.email.send({ to: user.email, ...template });
@@ -3107,7 +3417,7 @@ var AuthRoutes = class {
3107
3417
  }
3108
3418
  }
3109
3419
  async forgotPassword(req) {
3110
- const { ipAddress, userAgent } = createAuditContext(req);
3420
+ const { ipAddress, userAgent } = createAuditContext2(req);
3111
3421
  if (this.rateLimiter) {
3112
3422
  const limit = await this.rateLimiter.check("auth:forgot", ipAddress);
3113
3423
  if (!limit.allowed) {
@@ -3120,7 +3430,7 @@ var AuthRoutes = class {
3120
3430
  if (!email) {
3121
3431
  return this.errorResponse("Email required", 400);
3122
3432
  }
3123
- const user = await this.redis.findUserByEmail(email);
3433
+ const user = await this.authAdapter.findUserByEmail(email);
3124
3434
  if (!user) {
3125
3435
  return this.jsonResponse({
3126
3436
  success: true,
@@ -3233,23 +3543,72 @@ function getEnvNum(key, fallback = 0) {
3233
3543
  if (!val) return fallback;
3234
3544
  return parseInt(val, 10);
3235
3545
  }
3236
- async function createAuthConfig() {
3237
- const redisUrl = getEnv("REDIS_URL", "redis://localhost:6379");
3238
- const redisKeyPrefix = getEnv("REDIS_KEY_PREFIX", "kyro:auth:");
3239
- const redisSessionTTL = getEnvNum("REDIS_SESSION_TTL", 86400);
3240
- const redisRefreshTTL = getEnvNum("REDIS_REFRESH_TOKEN_TTL", 604800);
3241
- const redisAdapter = new chunkI4BORBXT_cjs.RedisAuthAdapter({
3242
- url: redisUrl,
3243
- keyPrefix: redisKeyPrefix,
3244
- tokenExpiration: redisSessionTTL,
3245
- refreshTokenExpiration: redisRefreshTTL,
3246
- tls: getEnvBool("REDIS_TLS", false)
3247
- });
3248
- await redisAdapter.connect();
3249
- const redisClient = redisAdapter.redis;
3546
+ function detectDatabaseType() {
3547
+ const envDb = process.env.KYRO_AUTH_DATABASE?.toLowerCase();
3548
+ if (envDb && ["sqlite", "postgres", "mysql", "mongodb", "memory"].includes(envDb)) {
3549
+ return envDb;
3550
+ }
3551
+ try {
3552
+ const { readFileSync } = chunk5BLDMQED_cjs.__require("fs");
3553
+ const { join } = chunk5BLDMQED_cjs.__require("path");
3554
+ const configPath = join(process.cwd(), "kyro.config.ts");
3555
+ const configContent = readFileSync(configPath, "utf8");
3556
+ if (configContent.includes("createLocalAdapter")) {
3557
+ return "sqlite";
3558
+ } else if (configContent.includes("createDrizzleAdapter")) {
3559
+ if (configContent.includes("postgres") || configContent.includes("postgresql")) {
3560
+ return "postgres";
3561
+ } else if (configContent.includes("mysql")) {
3562
+ return "mysql";
3563
+ }
3564
+ return "postgres";
3565
+ } else if (configContent.includes("createMongoDBAdapter")) {
3566
+ return "mongodb";
3567
+ }
3568
+ } catch {
3569
+ }
3570
+ return "memory";
3571
+ }
3572
+ async function createAuthAdapter(databaseType) {
3573
+ switch (databaseType) {
3574
+ case "sqlite":
3575
+ return new chunk3EVLFWH2_cjs.SQLiteAuthAdapter({
3576
+ path: getEnv("KYRO_AUTH_DB_PATH", "./data/auth.db")
3577
+ });
3578
+ case "postgres":
3579
+ case "mysql":
3580
+ return new chunk3EVLFWH2_cjs.SQLiteAuthAdapter({
3581
+ path: getEnv("KYRO_AUTH_DB_PATH", "./data/auth.db")
3582
+ });
3583
+ case "mongodb":
3584
+ return new chunk3EVLFWH2_cjs.SQLiteAuthAdapter({
3585
+ path: getEnv("KYRO_AUTH_DB_PATH", "./data/auth.db")
3586
+ });
3587
+ case "memory":
3588
+ default:
3589
+ return new InMemoryAuthAdapter();
3590
+ }
3591
+ }
3592
+ async function createAuthConfig(databaseType) {
3593
+ const distributed = getEnvBool("KYRO_DISTRIBUTED", false);
3594
+ let authAdapter;
3595
+ if (distributed) {
3596
+ const { RedisAuthAdapter: RedisAuthAdapter2 } = await import('./redis-adapter-LBLNKGNS.cjs');
3597
+ const redisUrl = getEnv("REDIS_URL", "redis://localhost:6379");
3598
+ const redisTls = getEnvBool("REDIS_TLS", false);
3599
+ const redisAdapter = new RedisAuthAdapter2({ url: redisUrl, tls: redisTls });
3600
+ await redisAdapter.connect?.();
3601
+ authAdapter = redisAdapter;
3602
+ } else {
3603
+ const initialDbType = databaseType || detectDatabaseType();
3604
+ authAdapter = await createAuthAdapter(initialDbType);
3605
+ if (authAdapter.connect) {
3606
+ await authAdapter.connect();
3607
+ }
3608
+ }
3250
3609
  const emailConfig = getEmailConfig();
3251
- const email = emailConfig ? new chunkI4BORBXT_cjs.EmailTransport(emailConfig) : void 0;
3252
- const passwordPolicy = new chunkI4BORBXT_cjs.PasswordPolicy({
3610
+ const email = emailConfig ? new chunk3EVLFWH2_cjs.EmailTransport(emailConfig) : void 0;
3611
+ const passwordPolicy = new chunk3EVLFWH2_cjs.PasswordPolicy({
3253
3612
  minLength: getEnvNum("PASSWORD_MIN_LENGTH", 12),
3254
3613
  requireUppercase: getEnvBool("PASSWORD_REQUIRE_UPPERCASE", true),
3255
3614
  requireLowercase: getEnvBool("PASSWORD_REQUIRE_LOWERCASE", true),
@@ -3259,14 +3618,15 @@ async function createAuthConfig() {
3259
3618
  maxLength: getEnvNum("PASSWORD_MAX_LENGTH", 128)
3260
3619
  });
3261
3620
  let lockout;
3262
- if (getEnvBool("LOCKOUT_ENABLED", true)) {
3621
+ let rateLimiter;
3622
+ let auditLogger;
3623
+ if (distributed) {
3624
+ const redis = authAdapter;
3625
+ const redisClient = redis.redis;
3263
3626
  lockout = new AccountLockout(redisClient, {
3264
3627
  maxAttempts: getEnvNum("LOCKOUT_MAX_ATTEMPTS", 5),
3265
3628
  lockDuration: getEnvNum("LOCKOUT_DURATION_MINUTES", 15) * 60 * 1e3
3266
3629
  });
3267
- }
3268
- let rateLimiter;
3269
- if (getEnvBool("RATE_LIMIT_ENABLED", true)) {
3270
3630
  rateLimiter = new RateLimiter(redisClient, {
3271
3631
  "auth:login": {
3272
3632
  window: getEnvNum("RATE_LIMIT_AUTH_WINDOW_MS", 9e5),
@@ -3277,16 +3637,29 @@ async function createAuthConfig() {
3277
3637
  max: getEnvNum("RATE_LIMIT_MAX_REQUESTS", 100)
3278
3638
  }
3279
3639
  });
3280
- }
3281
- let auditLogger;
3282
- if (getEnvBool("AUDIT_LOG_ENABLED", true)) {
3283
3640
  auditLogger = new AuditLogger(
3284
3641
  redisClient,
3285
3642
  getEnvNum("AUDIT_LOG_RETENTION_DAYS", 30)
3286
3643
  );
3644
+ } else {
3645
+ lockout = new InMemoryAccountLockout({
3646
+ maxAttempts: getEnvNum("LOCKOUT_MAX_ATTEMPTS", 5),
3647
+ lockDuration: getEnvNum("LOCKOUT_DURATION_MINUTES", 15) * 60 * 1e3
3648
+ });
3649
+ rateLimiter = new InMemoryRateLimiter({
3650
+ "auth:login": {
3651
+ window: getEnvNum("RATE_LIMIT_AUTH_WINDOW_MS", 9e5),
3652
+ max: getEnvNum("RATE_LIMIT_AUTH_MAX_REQUESTS", 10)
3653
+ },
3654
+ "api:general": {
3655
+ window: getEnvNum("RATE_LIMIT_WINDOW_MS", 6e4),
3656
+ max: getEnvNum("RATE_LIMIT_MAX_REQUESTS", 100)
3657
+ }
3658
+ });
3659
+ auditLogger = getEnvBool("AUDIT_LOG_ENABLED", true) ? new InMemoryAuditLogger(getEnvNum("AUDIT_LOG_RETENTION_DAYS", 30)) : void 0;
3287
3660
  }
3288
3661
  const routes = new AuthRoutes({
3289
- redis: redisAdapter,
3662
+ redis: authAdapter,
3290
3663
  email,
3291
3664
  jwtSecret: getEnv("JWT_SECRET", "change-me"),
3292
3665
  jwtExpiresIn: getEnv("JWT_EXPIRES_IN", "24h"),
@@ -3299,9 +3672,10 @@ async function createAuthConfig() {
3299
3672
  baseUrl: getEnv("EMAIL_BASE_URL", "http://localhost:4321"),
3300
3673
  emailVerificationRequired: getEnvBool("EMAIL_VERIFICATION_REQUIRED", true)
3301
3674
  });
3675
+ const actualDbType = distributed ? "distributed" : databaseType || detectDatabaseType();
3302
3676
  return {
3303
- redis: redisAdapter,
3304
- redisClient,
3677
+ authAdapter,
3678
+ databaseType: actualDbType,
3305
3679
  email,
3306
3680
  passwordPolicy,
3307
3681
  lockout,
@@ -3491,7 +3865,7 @@ var Auth = class {
3491
3865
  return jwt2__default.default.sign(payload, this.config.secret, signOptions);
3492
3866
  }
3493
3867
  async hashPassword(password) {
3494
- return bcrypt2__default.default.hash(password, this.config.saltRounds);
3868
+ return bcrypt__default.default.hash(password, this.config.saltRounds);
3495
3869
  }
3496
3870
  parseExpiresIn(value) {
3497
3871
  if (typeof value === "number") return value;
@@ -3736,29 +4110,33 @@ Object.defineProperty(exports, "minimalCollections", {
3736
4110
  enumerable: true,
3737
4111
  get: function () { return chunkF5B64H5S_cjs.minimalCollections; }
3738
4112
  });
4113
+ Object.defineProperty(exports, "RedisAuthAdapter", {
4114
+ enumerable: true,
4115
+ get: function () { return chunkA3RQWHKD_cjs.RedisAuthAdapter; }
4116
+ });
3739
4117
  Object.defineProperty(exports, "EmailTransport", {
3740
4118
  enumerable: true,
3741
- get: function () { return chunkI4BORBXT_cjs.EmailTransport; }
4119
+ get: function () { return chunk3EVLFWH2_cjs.EmailTransport; }
3742
4120
  });
3743
4121
  Object.defineProperty(exports, "PasswordPolicy", {
3744
4122
  enumerable: true,
3745
- get: function () { return chunkI4BORBXT_cjs.PasswordPolicy; }
4123
+ get: function () { return chunk3EVLFWH2_cjs.PasswordPolicy; }
3746
4124
  });
3747
- Object.defineProperty(exports, "RedisAuthAdapter", {
4125
+ Object.defineProperty(exports, "SQLiteAuthAdapter", {
3748
4126
  enumerable: true,
3749
- get: function () { return chunkI4BORBXT_cjs.RedisAuthAdapter; }
4127
+ get: function () { return chunk3EVLFWH2_cjs.SQLiteAuthAdapter; }
3750
4128
  });
3751
4129
  Object.defineProperty(exports, "autoBootstrap", {
3752
4130
  enumerable: true,
3753
- get: function () { return chunkI4BORBXT_cjs.autoBootstrap; }
4131
+ get: function () { return chunk3EVLFWH2_cjs.autoBootstrap; }
3754
4132
  });
3755
4133
  Object.defineProperty(exports, "bootstrapAdmin", {
3756
4134
  enumerable: true,
3757
- get: function () { return chunkI4BORBXT_cjs.bootstrapAdmin; }
4135
+ get: function () { return chunk3EVLFWH2_cjs.bootstrapAdmin; }
3758
4136
  });
3759
4137
  Object.defineProperty(exports, "getBootstrapFromEnv", {
3760
4138
  enumerable: true,
3761
- get: function () { return chunkI4BORBXT_cjs.getBootstrapFromEnv; }
4139
+ get: function () { return chunk3EVLFWH2_cjs.getBootstrapFromEnv; }
3762
4140
  });
3763
4141
  Object.defineProperty(exports, "createContext", {
3764
4142
  enumerable: true,
@@ -3858,19 +4236,19 @@ Object.defineProperty(exports, "fieldToDrizzleType", {
3858
4236
  });
3859
4237
  Object.defineProperty(exports, "PostgresAuthAdapter", {
3860
4238
  enumerable: true,
3861
- get: function () { return chunkKWTKEBHM_cjs.PostgresAuthAdapter; }
4239
+ get: function () { return chunkYD7Y25W7_cjs.PostgresAuthAdapter; }
3862
4240
  });
3863
4241
  Object.defineProperty(exports, "createDatabase", {
3864
4242
  enumerable: true,
3865
- get: function () { return chunkU4CHJTWX_cjs.createDatabase; }
4243
+ get: function () { return chunk7G6EVYCU_cjs.createDatabase; }
3866
4244
  });
3867
4245
  Object.defineProperty(exports, "runMigrations", {
3868
4246
  enumerable: true,
3869
- get: function () { return chunkU4CHJTWX_cjs.runMigrations; }
4247
+ get: function () { return chunk7G6EVYCU_cjs.runMigrations; }
3870
4248
  });
3871
4249
  Object.defineProperty(exports, "seedDefaultRoles", {
3872
4250
  enumerable: true,
3873
- get: function () { return chunkU4CHJTWX_cjs.seedDefaultRoles; }
4251
+ get: function () { return chunk7G6EVYCU_cjs.seedDefaultRoles; }
3874
4252
  });
3875
4253
  Object.defineProperty(exports, "MongoDBAdapter", {
3876
4254
  enumerable: true,
@@ -3897,6 +4275,10 @@ exports.COMPLEX_FIELD_TYPES = COMPLEX_FIELD_TYPES;
3897
4275
  exports.CSSGenerator = CSSGenerator;
3898
4276
  exports.CommentsPlugin = CommentsPlugin;
3899
4277
  exports.ConfigValidationError = ConfigValidationError;
4278
+ exports.InMemoryAccountLockout = InMemoryAccountLockout;
4279
+ exports.InMemoryAuditLogger = InMemoryAuditLogger;
4280
+ exports.InMemoryAuthAdapter = InMemoryAuthAdapter;
4281
+ exports.InMemoryRateLimiter = InMemoryRateLimiter;
3900
4282
  exports.Kyro = Kyro;
3901
4283
  exports.KyroPlugin = KyroPlugin;
3902
4284
  exports.LAYOUT_FIELD_TYPES = LAYOUT_FIELD_TYPES;
@@ -3908,7 +4290,6 @@ exports.RateLimiter = RateLimiter;
3908
4290
  exports.Registry = Registry;
3909
4291
  exports.ReviewsPlugin = ReviewsPlugin;
3910
4292
  exports.SEOPLugin = SEOPLugin;
3911
- exports.SQLiteAuthAdapter = SQLiteAuthAdapter;
3912
4293
  exports.VersionManager = VersionManager;
3913
4294
  exports.WishlistPlugin = WishlistPlugin;
3914
4295
  exports.authConfig = authConfig;