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