@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.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { allSettingsGlobals, blogCollections, blogGlobals, coreSettingsGlobals, createTemplateConfig, ecommerceCollections, ecommerceGlobals, ecommerceSettingsGlobals, kitchenSinkCollections, mediaCollections, minimalCollections } from './chunk-3QX6KG2S.js';
2
- import { RedisAuthAdapter, EmailTransport, PasswordPolicy } from './chunk-V67YXRBT.js';
3
- export { EmailTransport, PasswordPolicy, RedisAuthAdapter, autoBootstrap, bootstrapAdmin, getBootstrapFromEnv } from './chunk-V67YXRBT.js';
2
+ export { RedisAuthAdapter } from './chunk-VMSRTAH7.js';
3
+ import { EmailTransport, PasswordPolicy, SQLiteAuthAdapter } from './chunk-5Y7QGIHD.js';
4
+ export { EmailTransport, PasswordPolicy, SQLiteAuthAdapter, autoBootstrap, bootstrapAdmin, getBootstrapFromEnv } from './chunk-5Y7QGIHD.js';
4
5
  import { createKyroServer } from './chunk-UEYC46RL.js';
5
6
  export { createContext, createCountProcedure, createCreateProcedure, createDeleteProcedure, createDynamicRouter, createFindByIDProcedure, createFindProcedure, createKyroServer, createUpdateProcedure } from './chunk-UEYC46RL.js';
6
7
  import { buildGraphQLSchema } from './chunk-OG3KX56O.js';
@@ -11,18 +12,17 @@ export { evaluateAccess, getWhereClause, mergeWhereClauses } from './chunk-SDMNU
11
12
  import { KyroPubSub, createWSServer } from './chunk-3TPQ2BU6.js';
12
13
  export { KyroPubSub, KyroWSServer, PubSub, createWSServer } from './chunk-3TPQ2BU6.js';
13
14
  export { DrizzleAdapter, collectionToDrizzleSchema, createDrizzleAdapter, fieldToDrizzleType } from './chunk-EINVJPFM.js';
14
- export { PostgresAuthAdapter } from './chunk-M4JFHQ5J.js';
15
- export { createDatabase, runMigrations, seedDefaultRoles } from './chunk-XLMVCGXA.js';
16
- import './chunk-KA3UOIFC.js';
15
+ export { PostgresAuthAdapter } from './chunk-QUJ4OLSC.js';
16
+ export { createDatabase, runMigrations, seedDefaultRoles } from './chunk-LRTZJJPD.js';
17
+ import './chunk-XTZSUDSI.js';
17
18
  export { MongoDBAdapter, createMongoDBAdapter } from './chunk-DIC236EW.js';
18
19
  import { AbstractBaseAdapter } from './chunk-BXMWDUED.js';
19
20
  export { AbstractBaseAdapter } from './chunk-BXMWDUED.js';
20
- import './chunk-PZ5AY32C.js';
21
+ import { __require } from './chunk-NSBPE2FW.js';
21
22
  import { z } from 'zod';
22
23
  export { z } from 'zod';
23
- import bcrypt2 from 'bcrypt';
24
+ import bcrypt from 'bcrypt';
24
25
  import jwt2 from 'jsonwebtoken';
25
- import bcrypt from 'bcryptjs';
26
26
  import { randomBytes } from 'crypto';
27
27
 
28
28
  // src/registry/validator.ts
@@ -2000,277 +2000,6 @@ var defaultFieldStyling = {
2000
2000
  }
2001
2001
  }
2002
2002
  };
2003
- var SQLiteAuthAdapter = class {
2004
- db = null;
2005
- path;
2006
- saltRounds;
2007
- externalDb;
2008
- constructor(options = {}) {
2009
- this.path = options.path || "./data.db";
2010
- this.saltRounds = options.saltRounds || 12;
2011
- this.externalDb = !!options.db;
2012
- if (options.db) {
2013
- this.db = options.db;
2014
- }
2015
- }
2016
- async connect() {
2017
- if (this.db) return;
2018
- const Database = (await import('better-sqlite3')).default;
2019
- this.db = new Database(this.path);
2020
- this.db.pragma("journal_mode = WAL");
2021
- this.db.pragma("foreign_keys = ON");
2022
- this.ensureTables();
2023
- }
2024
- async disconnect() {
2025
- if (this.db && !this.externalDb) {
2026
- this.db.close();
2027
- this.db = null;
2028
- }
2029
- }
2030
- ensureTables() {
2031
- if (!this.db) return;
2032
- this.db.exec(`
2033
- CREATE TABLE IF NOT EXISTS kyro_users (
2034
- id TEXT PRIMARY KEY,
2035
- email TEXT UNIQUE NOT NULL,
2036
- password_hash TEXT NOT NULL,
2037
- role TEXT NOT NULL DEFAULT 'customer',
2038
- tenant_id TEXT,
2039
- email_verified INTEGER DEFAULT 0,
2040
- locked INTEGER DEFAULT 0,
2041
- last_login TEXT,
2042
- failed_login_attempts INTEGER DEFAULT 0,
2043
- locked_until TEXT,
2044
- created_at TEXT NOT NULL,
2045
- updated_at TEXT NOT NULL
2046
- );
2047
-
2048
- CREATE TABLE IF NOT EXISTS kyro_sessions (
2049
- id TEXT PRIMARY KEY,
2050
- user_id TEXT NOT NULL,
2051
- token TEXT NOT NULL,
2052
- refresh_token TEXT,
2053
- expires_at TEXT NOT NULL,
2054
- created_at TEXT NOT NULL,
2055
- ip_address TEXT,
2056
- user_agent TEXT,
2057
- FOREIGN KEY (user_id) REFERENCES kyro_users(id) ON DELETE CASCADE
2058
- );
2059
-
2060
- CREATE TABLE IF NOT EXISTS kyro_password_history (
2061
- id INTEGER PRIMARY KEY AUTOINCREMENT,
2062
- user_id TEXT NOT NULL,
2063
- password_hash TEXT NOT NULL,
2064
- created_at TEXT NOT NULL,
2065
- FOREIGN KEY (user_id) REFERENCES kyro_users(id) ON DELETE CASCADE
2066
- );
2067
-
2068
- CREATE INDEX IF NOT EXISTS idx_kyro_users_email ON kyro_users(email);
2069
- CREATE INDEX IF NOT EXISTS idx_kyro_sessions_user_id ON kyro_sessions(user_id);
2070
- CREATE INDEX IF NOT EXISTS idx_kyro_sessions_token ON kyro_sessions(token);
2071
- CREATE INDEX IF NOT EXISTS idx_kyro_sessions_refresh_token ON kyro_sessions(refresh_token);
2072
- CREATE INDEX IF NOT EXISTS idx_kyro_password_history_user_id ON kyro_password_history(user_id);
2073
- `);
2074
- }
2075
- async createUser(data) {
2076
- if (!this.db) throw new Error("Not connected");
2077
- const id = randomBytes(16).toString("hex");
2078
- const now = (/* @__PURE__ */ new Date()).toISOString();
2079
- const user = {
2080
- id,
2081
- email: data.email.toLowerCase(),
2082
- passwordHash: data.passwordHash,
2083
- role: data.role || "customer",
2084
- tenantId: data.tenantId,
2085
- createdAt: now,
2086
- updatedAt: now
2087
- };
2088
- this.db.prepare(
2089
- `INSERT INTO kyro_users (id, email, password_hash, role, tenant_id, created_at, updated_at)
2090
- VALUES (?, ?, ?, ?, ?, ?, ?)`
2091
- ).run(
2092
- id,
2093
- user.email,
2094
- user.passwordHash,
2095
- user.role,
2096
- user.tenantId,
2097
- now,
2098
- now
2099
- );
2100
- return user;
2101
- }
2102
- async findUserByEmail(email) {
2103
- if (!this.db) throw new Error("Not connected");
2104
- const row = this.db.prepare("SELECT * FROM kyro_users WHERE email = ?").get(email.toLowerCase());
2105
- if (!row) return null;
2106
- return this.rowToUser(row);
2107
- }
2108
- async findUserById(userId) {
2109
- if (!this.db) throw new Error("Not connected");
2110
- const row = this.db.prepare("SELECT * FROM kyro_users WHERE id = ?").get(userId);
2111
- if (!row) return null;
2112
- return this.rowToUser(row);
2113
- }
2114
- async updateUser(userId, data) {
2115
- if (!this.db) throw new Error("Not connected");
2116
- const existing = await this.findUserById(userId);
2117
- if (!existing) return null;
2118
- const updates = [];
2119
- const values = [];
2120
- if (data.email !== void 0) {
2121
- updates.push("email = ?");
2122
- values.push(data.email.toLowerCase());
2123
- }
2124
- if (data.passwordHash !== void 0) {
2125
- updates.push("password_hash = ?");
2126
- values.push(data.passwordHash);
2127
- }
2128
- if (data.role !== void 0) {
2129
- updates.push("role = ?");
2130
- values.push(data.role);
2131
- }
2132
- if (data.tenantId !== void 0) {
2133
- updates.push("tenant_id = ?");
2134
- values.push(data.tenantId);
2135
- }
2136
- if (data.emailVerified !== void 0) {
2137
- updates.push("email_verified = ?");
2138
- values.push(data.emailVerified ? 1 : 0);
2139
- }
2140
- if (data.locked !== void 0) {
2141
- updates.push("locked = ?");
2142
- values.push(data.locked ? 1 : 0);
2143
- }
2144
- if (data.lastLogin !== void 0) {
2145
- updates.push("last_login = ?");
2146
- values.push(data.lastLogin);
2147
- }
2148
- if (data.failedLoginAttempts !== void 0) {
2149
- updates.push("failed_login_attempts = ?");
2150
- values.push(data.failedLoginAttempts);
2151
- }
2152
- updates.push("updated_at = ?");
2153
- values.push((/* @__PURE__ */ new Date()).toISOString());
2154
- values.push(userId);
2155
- this.db.prepare(`UPDATE kyro_users SET ${updates.join(", ")} WHERE id = ?`).run(...values);
2156
- return this.findUserById(userId);
2157
- }
2158
- async deleteUser(userId) {
2159
- if (!this.db) throw new Error("Not connected");
2160
- const result = this.db.prepare("DELETE FROM kyro_users WHERE id = ?").run(userId);
2161
- return result.changes > 0;
2162
- }
2163
- async hashPassword(password) {
2164
- return bcrypt.hash(password, this.saltRounds);
2165
- }
2166
- async verifyPassword(password, hash) {
2167
- return bcrypt.compare(password, hash);
2168
- }
2169
- async createSession(userId, data = {}) {
2170
- if (!this.db) throw new Error("Not connected");
2171
- const id = randomBytes(32).toString("hex");
2172
- const token = randomBytes(32).toString("base64url");
2173
- const refreshToken = randomBytes(32).toString("base64url");
2174
- const now = /* @__PURE__ */ new Date();
2175
- const expiresAt = new Date(now.getTime() + 864e5).toISOString();
2176
- const session = {
2177
- id,
2178
- userId,
2179
- token,
2180
- refreshToken,
2181
- expiresAt,
2182
- createdAt: now.toISOString(),
2183
- ipAddress: data.ipAddress,
2184
- userAgent: data.userAgent
2185
- };
2186
- this.db.prepare(
2187
- `INSERT INTO kyro_sessions (id, user_id, token, refresh_token, expires_at, created_at, ip_address, user_agent)
2188
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
2189
- ).run(
2190
- session.id,
2191
- session.userId,
2192
- session.token,
2193
- session.refreshToken,
2194
- session.expiresAt,
2195
- session.createdAt,
2196
- session.ipAddress,
2197
- session.userAgent
2198
- );
2199
- return session;
2200
- }
2201
- async findSessionByToken(token) {
2202
- if (!this.db) throw new Error("Not connected");
2203
- const row = this.db.prepare("SELECT * FROM kyro_sessions WHERE token = ?").get(token);
2204
- if (!row) return null;
2205
- return this.rowToSession(row);
2206
- }
2207
- async findSessionByRefreshToken(refreshToken) {
2208
- if (!this.db) throw new Error("Not connected");
2209
- const row = this.db.prepare("SELECT * FROM kyro_sessions WHERE refresh_token = ?").get(refreshToken);
2210
- if (!row) return null;
2211
- return this.rowToSession(row);
2212
- }
2213
- async deleteSession(sessionId) {
2214
- if (!this.db) throw new Error("Not connected");
2215
- const result = this.db.prepare("DELETE FROM kyro_sessions WHERE id = ? OR token = ?").run(sessionId, sessionId);
2216
- return result.changes > 0;
2217
- }
2218
- async deleteUserSessions(userId) {
2219
- if (!this.db) throw new Error("Not connected");
2220
- const result = this.db.prepare("DELETE FROM kyro_sessions WHERE user_id = ?").run(userId);
2221
- return result.changes;
2222
- }
2223
- async hasAnyUsers() {
2224
- if (!this.db) throw new Error("Not connected");
2225
- const row = this.db.prepare("SELECT COUNT(*) as count FROM kyro_users").get();
2226
- return row.count > 0;
2227
- }
2228
- async addPasswordToHistory(userId, passwordHash) {
2229
- if (!this.db) throw new Error("Not connected");
2230
- this.db.prepare(
2231
- "INSERT INTO kyro_password_history (user_id, password_hash, created_at) VALUES (?, ?, ?)"
2232
- ).run(userId, passwordHash, (/* @__PURE__ */ new Date()).toISOString());
2233
- this.db.prepare(
2234
- `DELETE FROM kyro_password_history WHERE id IN (
2235
- SELECT id FROM kyro_password_history WHERE user_id = ? ORDER BY created_at DESC LIMIT -1 OFFSET 5
2236
- )`
2237
- ).run(userId);
2238
- }
2239
- async getPasswordHistory(userId, count = 5) {
2240
- if (!this.db) throw new Error("Not connected");
2241
- const rows = this.db.prepare(
2242
- "SELECT password_hash FROM kyro_password_history WHERE user_id = ? ORDER BY created_at DESC LIMIT ?"
2243
- ).all(userId, count);
2244
- return rows.map((r) => r.password_hash);
2245
- }
2246
- rowToUser(row) {
2247
- return {
2248
- id: row.id,
2249
- email: row.email,
2250
- passwordHash: row.password_hash,
2251
- role: row.role,
2252
- tenantId: row.tenant_id,
2253
- emailVerified: row.email_verified === 1,
2254
- locked: row.locked === 1,
2255
- lastLogin: row.last_login,
2256
- failedLoginAttempts: row.failed_login_attempts || 0,
2257
- createdAt: row.created_at,
2258
- updatedAt: row.updated_at
2259
- };
2260
- }
2261
- rowToSession(row) {
2262
- return {
2263
- id: row.id,
2264
- userId: row.user_id,
2265
- token: row.token,
2266
- refreshToken: row.refresh_token,
2267
- expiresAt: row.expires_at,
2268
- createdAt: row.created_at,
2269
- ipAddress: row.ip_address,
2270
- userAgent: row.user_agent
2271
- };
2272
- }
2273
- };
2274
2003
 
2275
2004
  // src/auth/security/lockout.ts
2276
2005
  var DEFAULT_LOCKOUT_CONFIG = {
@@ -2537,6 +2266,8 @@ var RateLimiter = class {
2537
2266
  this.userLimits[type] = config;
2538
2267
  }
2539
2268
  };
2269
+ var DEFAULT_RETENTION_CONFIG = {
2270
+ retentionDays: 30};
2540
2271
  var AuditLogger = class {
2541
2272
  redis;
2542
2273
  prefix;
@@ -2767,6 +2498,572 @@ function createAuditContext(req) {
2767
2498
  userAgent: req.headers.get("user-agent") || "unknown"
2768
2499
  };
2769
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 = 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 = randomBytes(32).toString("hex");
2591
+ const token = randomBytes(32).toString("base64url");
2592
+ const refreshToken = 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 = 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
+ }
2770
3067
  function defaultExtractToken(req) {
2771
3068
  const authHeader = req.headers.get("Authorization");
2772
3069
  if (authHeader?.startsWith("Bearer ")) {
@@ -2794,7 +3091,7 @@ function generateToken(payload, secret, options = {}) {
2794
3091
 
2795
3092
  // src/api/rest/auth-routes.ts
2796
3093
  var AuthRoutes = class {
2797
- redis;
3094
+ authAdapter;
2798
3095
  email;
2799
3096
  jwtSecret;
2800
3097
  jwtExpiresIn;
@@ -2807,7 +3104,7 @@ var AuthRoutes = class {
2807
3104
  baseUrl;
2808
3105
  emailVerificationRequired;
2809
3106
  constructor(config) {
2810
- this.redis = config.redis;
3107
+ this.authAdapter = config.redis;
2811
3108
  this.email = config.email;
2812
3109
  this.jwtSecret = config.jwtSecret;
2813
3110
  this.jwtExpiresIn = config.jwtExpiresIn || "24h";
@@ -2821,7 +3118,7 @@ var AuthRoutes = class {
2821
3118
  this.emailVerificationRequired = config.emailVerificationRequired ?? true;
2822
3119
  }
2823
3120
  async register(req) {
2824
- const { ipAddress, userAgent } = createAuditContext(req);
3121
+ const { ipAddress, userAgent } = createAuditContext2(req);
2825
3122
  if (this.rateLimiter) {
2826
3123
  const limit = await this.rateLimiter.check("auth:register", ipAddress);
2827
3124
  if (!limit.allowed) {
@@ -2840,12 +3137,12 @@ var AuthRoutes = class {
2840
3137
  if (!passwordValidation.valid) {
2841
3138
  return this.errorResponse(passwordValidation.errors.join(". "), 400);
2842
3139
  }
2843
- const existingUser = await this.redis.findUserByEmail(body.email);
3140
+ const existingUser = await this.authAdapter.findUserByEmail(body.email);
2844
3141
  if (existingUser) {
2845
3142
  return this.errorResponse("Email already registered", 400);
2846
3143
  }
2847
- const passwordHash = await this.redis.hashPassword(body.password);
2848
- const user = await this.redis.createUser({
3144
+ const passwordHash = await this.authAdapter.hashPassword(body.password);
3145
+ const user = await this.authAdapter.createUser({
2849
3146
  email: body.email,
2850
3147
  passwordHash,
2851
3148
  role: body.role || "customer",
@@ -2854,7 +3151,7 @@ var AuthRoutes = class {
2854
3151
  if (this.emailVerificationRequired && this.email) {
2855
3152
  const verificationToken = randomBytes(32).toString("hex");
2856
3153
  const verificationUrl = `${this.baseUrl}/api/auth/verify?token=${verificationToken}`;
2857
- await this.redis.createSession(user.id, { ipAddress, userAgent });
3154
+ await this.authAdapter.createSession(user.id, { ipAddress, userAgent });
2858
3155
  const template = this.email.getTemplates().verifyEmail(verificationUrl, body.email);
2859
3156
  await this.email.send({ to: body.email, ...template });
2860
3157
  }
@@ -2883,7 +3180,7 @@ var AuthRoutes = class {
2883
3180
  }
2884
3181
  }
2885
3182
  async login(req) {
2886
- const { ipAddress, userAgent } = createAuditContext(req);
3183
+ const { ipAddress, userAgent } = createAuditContext2(req);
2887
3184
  if (this.rateLimiter) {
2888
3185
  const limit = await this.rateLimiter.check("auth:login", ipAddress);
2889
3186
  if (!limit.allowed) {
@@ -2895,7 +3192,7 @@ var AuthRoutes = class {
2895
3192
  if (!body.email || !body.password) {
2896
3193
  return this.errorResponse("Email and password are required", 400);
2897
3194
  }
2898
- const user = await this.redis.findUserByEmail(body.email);
3195
+ const user = await this.authAdapter.findUserByEmail(body.email);
2899
3196
  if (!user) {
2900
3197
  await this.recordFailedLogin(ipAddress, userAgent);
2901
3198
  return this.errorResponse("Invalid credentials", 401);
@@ -2921,7 +3218,10 @@ var AuthRoutes = class {
2921
3218
  );
2922
3219
  }
2923
3220
  }
2924
- 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;
2925
3225
  if (!validPassword) {
2926
3226
  await this.recordFailedLogin(ipAddress, userAgent, user.id, user.email);
2927
3227
  return this.errorResponse("Invalid credentials", 401);
@@ -2929,7 +3229,7 @@ var AuthRoutes = class {
2929
3229
  if (this.lockout) {
2930
3230
  await this.lockout.resetAttempts(user.id);
2931
3231
  }
2932
- const session = await this.redis.createSession(user.id, {
3232
+ const session = await this.authAdapter.createSession(user.id, {
2933
3233
  ipAddress,
2934
3234
  userAgent
2935
3235
  });
@@ -2958,7 +3258,7 @@ var AuthRoutes = class {
2958
3258
  success: true
2959
3259
  });
2960
3260
  }
2961
- await this.redis.updateUser(user.id, {
3261
+ await this.authAdapter.updateUser(user.id, {
2962
3262
  lastLogin: (/* @__PURE__ */ new Date()).toISOString()
2963
3263
  });
2964
3264
  return this.jsonResponse({
@@ -2977,11 +3277,11 @@ var AuthRoutes = class {
2977
3277
  if (!token) {
2978
3278
  return this.errorResponse("No session to logout", 401);
2979
3279
  }
2980
- const { ipAddress, userAgent } = createAuditContext(req);
3280
+ const { ipAddress, userAgent } = createAuditContext2(req);
2981
3281
  try {
2982
3282
  const payload = jwt2.decode(token);
2983
3283
  if (payload && payload.sub) {
2984
- await this.redis.deleteUserSessions(payload.sub);
3284
+ await this.authAdapter.deleteUserSessions(payload.sub);
2985
3285
  if (this.auditLogger) {
2986
3286
  await this.auditLogger.log({
2987
3287
  action: "logout",
@@ -3024,7 +3324,7 @@ var AuthRoutes = class {
3024
3324
  issuer: this.jwtIssuer,
3025
3325
  audience: this.jwtAudience
3026
3326
  });
3027
- const user = await this.redis.findUserById(payload.sub);
3327
+ const user = await this.authAdapter.findUserById(payload.sub);
3028
3328
  if (!user) {
3029
3329
  return this.errorResponse("User not found", 404);
3030
3330
  }
@@ -3041,7 +3341,7 @@ var AuthRoutes = class {
3041
3341
  if (!token) {
3042
3342
  return this.errorResponse("Not authenticated", 401);
3043
3343
  }
3044
- const { ipAddress, userAgent } = createAuditContext(req);
3344
+ const { ipAddress, userAgent } = createAuditContext2(req);
3045
3345
  try {
3046
3346
  const payload = jwt2.verify(token, this.jwtSecret);
3047
3347
  const body = await req.json();
@@ -3056,16 +3356,22 @@ var AuthRoutes = class {
3056
3356
  if (!passwordValidation.valid) {
3057
3357
  return this.errorResponse(passwordValidation.errors.join(". "), 400);
3058
3358
  }
3059
- const user = await this.redis.findUserById(payload.sub);
3359
+ const user = await this.authAdapter.findUserById(payload.sub);
3060
3360
  if (!user) {
3061
3361
  return this.errorResponse("User not found", 404);
3062
3362
  }
3063
- 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;
3064
3367
  if (!validPassword) {
3065
3368
  return this.errorResponse("Current password is incorrect", 401);
3066
3369
  }
3067
- const passwordHistory = await this.redis.getPasswordHistory(user.id, 5);
3068
- 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?.(
3069
3375
  newPassword,
3070
3376
  user.id,
3071
3377
  5
@@ -3076,12 +3382,17 @@ var AuthRoutes = class {
3076
3382
  400
3077
3383
  );
3078
3384
  }
3079
- const newPasswordHash = await this.redis.hashPassword(newPassword);
3385
+ const newPasswordHash = await this.authAdapter.hashPassword(newPassword);
3080
3386
  if (user.passwordHash) {
3081
- await this.redis.addPasswordToHistory(user.id, user.passwordHash);
3387
+ await this.authAdapter.addPasswordToHistory?.(
3388
+ user.id,
3389
+ user.passwordHash
3390
+ );
3082
3391
  }
3083
- await this.redis.updateUser(user.id, { passwordHash: newPasswordHash });
3084
- await this.redis.deleteUserSessions(user.id);
3392
+ await this.authAdapter.updateUser(user.id, {
3393
+ passwordHash: newPasswordHash
3394
+ });
3395
+ await this.authAdapter.deleteUserSessions(user.id);
3085
3396
  if (this.email && this.email.getTemplates) {
3086
3397
  const template = this.email.getTemplates().passwordChanged(user.email);
3087
3398
  await this.email.send({ to: user.email, ...template });
@@ -3106,7 +3417,7 @@ var AuthRoutes = class {
3106
3417
  }
3107
3418
  }
3108
3419
  async forgotPassword(req) {
3109
- const { ipAddress, userAgent } = createAuditContext(req);
3420
+ const { ipAddress, userAgent } = createAuditContext2(req);
3110
3421
  if (this.rateLimiter) {
3111
3422
  const limit = await this.rateLimiter.check("auth:forgot", ipAddress);
3112
3423
  if (!limit.allowed) {
@@ -3119,7 +3430,7 @@ var AuthRoutes = class {
3119
3430
  if (!email) {
3120
3431
  return this.errorResponse("Email required", 400);
3121
3432
  }
3122
- const user = await this.redis.findUserByEmail(email);
3433
+ const user = await this.authAdapter.findUserByEmail(email);
3123
3434
  if (!user) {
3124
3435
  return this.jsonResponse({
3125
3436
  success: true,
@@ -3232,20 +3543,69 @@ function getEnvNum(key, fallback = 0) {
3232
3543
  if (!val) return fallback;
3233
3544
  return parseInt(val, 10);
3234
3545
  }
3235
- async function createAuthConfig() {
3236
- const redisUrl = getEnv("REDIS_URL", "redis://localhost:6379");
3237
- const redisKeyPrefix = getEnv("REDIS_KEY_PREFIX", "kyro:auth:");
3238
- const redisSessionTTL = getEnvNum("REDIS_SESSION_TTL", 86400);
3239
- const redisRefreshTTL = getEnvNum("REDIS_REFRESH_TOKEN_TTL", 604800);
3240
- const redisAdapter = new RedisAuthAdapter({
3241
- url: redisUrl,
3242
- keyPrefix: redisKeyPrefix,
3243
- tokenExpiration: redisSessionTTL,
3244
- refreshTokenExpiration: redisRefreshTTL,
3245
- tls: getEnvBool("REDIS_TLS", false)
3246
- });
3247
- await redisAdapter.connect();
3248
- 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 } = __require("fs");
3553
+ const { join } = __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 SQLiteAuthAdapter({
3576
+ path: getEnv("KYRO_AUTH_DB_PATH", "./data/auth.db")
3577
+ });
3578
+ case "postgres":
3579
+ case "mysql":
3580
+ return new SQLiteAuthAdapter({
3581
+ path: getEnv("KYRO_AUTH_DB_PATH", "./data/auth.db")
3582
+ });
3583
+ case "mongodb":
3584
+ return new 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-4YDY4LWE.js');
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
+ }
3249
3609
  const emailConfig = getEmailConfig();
3250
3610
  const email = emailConfig ? new EmailTransport(emailConfig) : void 0;
3251
3611
  const passwordPolicy = new PasswordPolicy({
@@ -3258,14 +3618,15 @@ async function createAuthConfig() {
3258
3618
  maxLength: getEnvNum("PASSWORD_MAX_LENGTH", 128)
3259
3619
  });
3260
3620
  let lockout;
3261
- if (getEnvBool("LOCKOUT_ENABLED", true)) {
3621
+ let rateLimiter;
3622
+ let auditLogger;
3623
+ if (distributed) {
3624
+ const redis = authAdapter;
3625
+ const redisClient = redis.redis;
3262
3626
  lockout = new AccountLockout(redisClient, {
3263
3627
  maxAttempts: getEnvNum("LOCKOUT_MAX_ATTEMPTS", 5),
3264
3628
  lockDuration: getEnvNum("LOCKOUT_DURATION_MINUTES", 15) * 60 * 1e3
3265
3629
  });
3266
- }
3267
- let rateLimiter;
3268
- if (getEnvBool("RATE_LIMIT_ENABLED", true)) {
3269
3630
  rateLimiter = new RateLimiter(redisClient, {
3270
3631
  "auth:login": {
3271
3632
  window: getEnvNum("RATE_LIMIT_AUTH_WINDOW_MS", 9e5),
@@ -3276,16 +3637,29 @@ async function createAuthConfig() {
3276
3637
  max: getEnvNum("RATE_LIMIT_MAX_REQUESTS", 100)
3277
3638
  }
3278
3639
  });
3279
- }
3280
- let auditLogger;
3281
- if (getEnvBool("AUDIT_LOG_ENABLED", true)) {
3282
3640
  auditLogger = new AuditLogger(
3283
3641
  redisClient,
3284
3642
  getEnvNum("AUDIT_LOG_RETENTION_DAYS", 30)
3285
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;
3286
3660
  }
3287
3661
  const routes = new AuthRoutes({
3288
- redis: redisAdapter,
3662
+ redis: authAdapter,
3289
3663
  email,
3290
3664
  jwtSecret: getEnv("JWT_SECRET", "change-me"),
3291
3665
  jwtExpiresIn: getEnv("JWT_EXPIRES_IN", "24h"),
@@ -3298,9 +3672,10 @@ async function createAuthConfig() {
3298
3672
  baseUrl: getEnv("EMAIL_BASE_URL", "http://localhost:4321"),
3299
3673
  emailVerificationRequired: getEnvBool("EMAIL_VERIFICATION_REQUIRED", true)
3300
3674
  });
3675
+ const actualDbType = distributed ? "distributed" : databaseType || detectDatabaseType();
3301
3676
  return {
3302
- redis: redisAdapter,
3303
- redisClient,
3677
+ authAdapter,
3678
+ databaseType: actualDbType,
3304
3679
  email,
3305
3680
  passwordPolicy,
3306
3681
  lockout,
@@ -3490,7 +3865,7 @@ var Auth = class {
3490
3865
  return jwt2.sign(payload, this.config.secret, signOptions);
3491
3866
  }
3492
3867
  async hashPassword(password) {
3493
- return bcrypt2.hash(password, this.config.saltRounds);
3868
+ return bcrypt.hash(password, this.config.saltRounds);
3494
3869
  }
3495
3870
  parseExpiresIn(value) {
3496
3871
  if (typeof value === "number") return value;
@@ -3691,6 +4066,6 @@ function defineConfig(config) {
3691
4066
  };
3692
4067
  }
3693
4068
 
3694
- export { ALL_FIELD_TYPES, AccountLockout, AnalyticsPlugin, AuditLogger, Auth, COMPLEX_FIELD_TYPES, CSSGenerator, CommentsPlugin, ConfigValidationError, Kyro, KyroPlugin, LAYOUT_FIELD_TYPES, LocalAdapter, PRIMITIVE_FIELD_TYPES, PluginManager, RELATIONAL_FIELD_TYPES, RateLimiter, Registry, ReviewsPlugin, SEOPLugin, SQLiteAuthAdapter, VersionManager, WishlistPlugin, authConfig, collectionToCreateZod, collectionToUpdateZod, collectionToWhereZod, collectionToZod, createAdminStyling, createAuditContext, createAuth, createAuthConfig, createKyro, createLocalAdapter, createRegistry, createVersionManager, defaultDarkTheme, defaultFieldStyling, defaultLightTheme, defineConfig, ecommerce2026Theme, fieldToZod, generateCSSVariables, generateTailwindConfig, getDefaultDraftPublishConfig, getRegistry, globalToZod, isArchived, isArrayField, isBlocksField, isDraft, isGroupField, isLayoutField, isNumberField, isPublished, isRelationshipField, isRichTextField, isSelectField, isTextField, isUploadField, presetPlugins, resetRegistry, runFieldHooks, runHooks, validateCollection, validateConfig, validateFields, validateGlobal };
4069
+ export { ALL_FIELD_TYPES, AccountLockout, AnalyticsPlugin, AuditLogger, Auth, COMPLEX_FIELD_TYPES, CSSGenerator, CommentsPlugin, ConfigValidationError, InMemoryAccountLockout, InMemoryAuditLogger, InMemoryAuthAdapter, InMemoryRateLimiter, Kyro, KyroPlugin, LAYOUT_FIELD_TYPES, LocalAdapter, PRIMITIVE_FIELD_TYPES, PluginManager, RELATIONAL_FIELD_TYPES, RateLimiter, Registry, ReviewsPlugin, SEOPLugin, VersionManager, WishlistPlugin, authConfig, collectionToCreateZod, collectionToUpdateZod, collectionToWhereZod, collectionToZod, createAdminStyling, createAuditContext, createAuth, createAuthConfig, createKyro, createLocalAdapter, createRegistry, createVersionManager, defaultDarkTheme, defaultFieldStyling, defaultLightTheme, defineConfig, ecommerce2026Theme, fieldToZod, generateCSSVariables, generateTailwindConfig, getDefaultDraftPublishConfig, getRegistry, globalToZod, isArchived, isArrayField, isBlocksField, isDraft, isGroupField, isLayoutField, isNumberField, isPublished, isRelationshipField, isRichTextField, isSelectField, isTextField, isUploadField, presetPlugins, resetRegistry, runFieldHooks, runHooks, validateCollection, validateConfig, validateFields, validateGlobal };
3695
4070
  //# sourceMappingURL=index.js.map
3696
4071
  //# sourceMappingURL=index.js.map