@kyro-cms/core 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap-BDTTUGY2.js +4 -0
- package/dist/{bootstrap-Q2TWUQF3.js.map → bootstrap-BDTTUGY2.js.map} +1 -1
- package/dist/bootstrap-X6TP3NKX.cjs +29 -0
- package/dist/{bootstrap-2WJK6PG7.cjs.map → bootstrap-X6TP3NKX.cjs.map} +1 -1
- package/dist/chunk-5BLDMQED.cjs +18 -0
- package/dist/{chunk-Q7SFCCGT.cjs.map → chunk-5BLDMQED.cjs.map} +1 -1
- package/dist/{chunk-U4CHJTWX.cjs → chunk-7G6EVYCU.cjs} +5 -5
- package/dist/{chunk-U4CHJTWX.cjs.map → chunk-7G6EVYCU.cjs.map} +1 -1
- package/dist/chunk-A3RQWHKD.cjs +263 -0
- package/dist/chunk-A3RQWHKD.cjs.map +1 -0
- package/dist/{chunk-V67YXRBT.js → chunk-C74MQIRL.js} +517 -203
- package/dist/chunk-C74MQIRL.js.map +1 -0
- package/dist/{chunk-XLMVCGXA.js → chunk-LRTZJJPD.js} +3 -3
- package/dist/{chunk-XLMVCGXA.js.map → chunk-LRTZJJPD.js.map} +1 -1
- package/dist/{chunk-I4BORBXT.cjs → chunk-MHS6CPO5.cjs} +517 -204
- package/dist/chunk-MHS6CPO5.cjs.map +1 -0
- package/dist/chunk-NSBPE2FW.js +15 -0
- package/dist/{chunk-PZ5AY32C.js.map → chunk-NSBPE2FW.js.map} +1 -1
- package/dist/{chunk-M4JFHQ5J.js → chunk-QUJ4OLSC.js} +3 -3
- package/dist/{chunk-M4JFHQ5J.js.map → chunk-QUJ4OLSC.js.map} +1 -1
- package/dist/{chunk-5AOILNGY.cjs → chunk-TZFJMPCH.cjs} +4 -4
- package/dist/{chunk-5AOILNGY.cjs.map → chunk-TZFJMPCH.cjs.map} +1 -1
- package/dist/chunk-VMSRTAH7.js +256 -0
- package/dist/chunk-VMSRTAH7.js.map +1 -0
- package/dist/{chunk-KA3UOIFC.js → chunk-XTZSUDSI.js} +3 -3
- package/dist/{chunk-KA3UOIFC.js.map → chunk-XTZSUDSI.js.map} +1 -1
- package/dist/{chunk-KWTKEBHM.cjs → chunk-YD7Y25W7.cjs} +19 -19
- package/dist/{chunk-KWTKEBHM.cjs.map → chunk-YD7Y25W7.cjs.map} +1 -1
- package/dist/cli/index.cjs +5 -5
- package/dist/cli/index.js +5 -5
- package/dist/database-7CJOXEZR.js +5 -0
- package/dist/{database-37KXWUER.js.map → database-7CJOXEZR.js.map} +1 -1
- package/dist/database-QOIV44GT.cjs +22 -0
- package/dist/{database-LJKD3HE4.cjs.map → database-QOIV44GT.cjs.map} +1 -1
- package/dist/drizzle/index.cjs +8 -8
- package/dist/drizzle/index.d.cts +1 -1
- package/dist/drizzle/index.d.ts +1 -1
- package/dist/drizzle/index.js +4 -4
- package/dist/graphql/index.cjs +1 -1
- package/dist/graphql/index.js +1 -1
- package/dist/{index-CzkEHKqu.d.cts → index-BMySjW6o.d.cts} +6 -0
- package/dist/{index-BVFlb7uU.d.ts → index-CMUNCIWQ.d.ts} +6 -0
- package/dist/index.cjs +727 -346
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +229 -62
- package/dist/index.d.ts +229 -62
- package/dist/index.js +706 -331
- package/dist/index.js.map +1 -1
- package/dist/mongodb/index.cjs +1 -1
- package/dist/mongodb/index.js +1 -1
- package/dist/postgres-auth-adapter-REJFUMP7.js +5 -0
- package/dist/{postgres-auth-adapter-LTDUGBMB.js.map → postgres-auth-adapter-REJFUMP7.js.map} +1 -1
- package/dist/postgres-auth-adapter-VK6GY7LX.cjs +14 -0
- package/dist/{postgres-auth-adapter-CYZAVPPP.cjs.map → postgres-auth-adapter-VK6GY7LX.cjs.map} +1 -1
- package/dist/redis-adapter-4YDY4LWE.js +4 -0
- package/dist/redis-adapter-4YDY4LWE.js.map +1 -0
- package/dist/redis-adapter-LBLNKGNS.cjs +13 -0
- package/dist/redis-adapter-LBLNKGNS.cjs.map +1 -0
- package/dist/rest/index.cjs +1 -1
- package/dist/rest/index.js +1 -1
- package/dist/templates/index.cjs +1 -1
- package/dist/templates/index.js +1 -1
- package/dist/trpc/index.cjs +1 -1
- package/dist/trpc/index.js +1 -1
- package/dist/ws/index.cjs +1 -1
- package/dist/ws/index.js +1 -1
- package/package.json +2 -2
- package/dist/bootstrap-2WJK6PG7.cjs +0 -29
- package/dist/bootstrap-Q2TWUQF3.js +0 -4
- package/dist/chunk-I4BORBXT.cjs.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -9
- package/dist/chunk-Q7SFCCGT.cjs +0 -11
- package/dist/chunk-V67YXRBT.js.map +0 -1
- package/dist/database-37KXWUER.js +0 -5
- package/dist/database-LJKD3HE4.cjs +0 -22
- package/dist/postgres-auth-adapter-CYZAVPPP.cjs +0 -14
- 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
|
-
|
|
3
|
-
|
|
2
|
+
export { RedisAuthAdapter } from './chunk-VMSRTAH7.js';
|
|
3
|
+
import { EmailTransport, PasswordPolicy, SQLiteAuthAdapter } from './chunk-C74MQIRL.js';
|
|
4
|
+
export { EmailTransport, PasswordPolicy, SQLiteAuthAdapter, autoBootstrap, bootstrapAdmin, getBootstrapFromEnv } from './chunk-C74MQIRL.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-
|
|
15
|
-
export { createDatabase, runMigrations, seedDefaultRoles } from './chunk-
|
|
16
|
-
import './chunk-
|
|
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-
|
|
21
|
+
import { __require } from './chunk-NSBPE2FW.js';
|
|
21
22
|
import { z } from 'zod';
|
|
22
23
|
export { z } from 'zod';
|
|
23
|
-
import
|
|
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
|
-
|
|
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.
|
|
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 } =
|
|
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.
|
|
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.
|
|
2848
|
-
const user = await this.
|
|
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.
|
|
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 } =
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 } =
|
|
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.
|
|
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.
|
|
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 } =
|
|
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.
|
|
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.
|
|
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.
|
|
3068
|
-
|
|
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.
|
|
3385
|
+
const newPasswordHash = await this.authAdapter.hashPassword(newPassword);
|
|
3080
3386
|
if (user.passwordHash) {
|
|
3081
|
-
await this.
|
|
3387
|
+
await this.authAdapter.addPasswordToHistory?.(
|
|
3388
|
+
user.id,
|
|
3389
|
+
user.passwordHash
|
|
3390
|
+
);
|
|
3082
3391
|
}
|
|
3083
|
-
await this.
|
|
3084
|
-
|
|
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 } =
|
|
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.
|
|
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
|
-
|
|
3236
|
-
const
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
3303
|
-
|
|
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
|
|
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,
|
|
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
|