@kyro-cms/core 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap-WMWQ4DBX.cjs +29 -0
- package/dist/{bootstrap-2WJK6PG7.cjs.map → bootstrap-WMWQ4DBX.cjs.map} +1 -1
- package/dist/bootstrap-WOVGAKZP.js +4 -0
- package/dist/{bootstrap-Q2TWUQF3.js.map → bootstrap-WOVGAKZP.js.map} +1 -1
- package/dist/{chunk-I4BORBXT.cjs → chunk-3EVLFWH2.cjs} +523 -204
- package/dist/chunk-3EVLFWH2.cjs.map +1 -0
- package/dist/chunk-5BLDMQED.cjs +18 -0
- package/dist/{chunk-Q7SFCCGT.cjs.map → chunk-5BLDMQED.cjs.map} +1 -1
- package/dist/{chunk-V67YXRBT.js → chunk-5Y7QGIHD.js} +523 -203
- package/dist/chunk-5Y7QGIHD.js.map +1 -0
- 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-XLMVCGXA.js → chunk-LRTZJJPD.js} +3 -3
- package/dist/{chunk-XLMVCGXA.js.map → chunk-LRTZJJPD.js.map} +1 -1
- 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
|
@@ -1,63 +1,283 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var Redis2 = require('ioredis');
|
|
4
3
|
var bcrypt = require('bcryptjs');
|
|
5
4
|
var crypto = require('crypto');
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
var path = require('path');
|
|
6
7
|
var nodemailer = require('nodemailer');
|
|
7
8
|
|
|
8
9
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
10
|
|
|
10
|
-
var Redis2__default = /*#__PURE__*/_interopDefault(Redis2);
|
|
11
11
|
var bcrypt__default = /*#__PURE__*/_interopDefault(bcrypt);
|
|
12
12
|
var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
|
|
13
13
|
|
|
14
|
-
// src/auth/
|
|
15
|
-
var
|
|
16
|
-
var
|
|
17
|
-
var
|
|
18
|
-
var
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
// src/auth/sqlite-adapter.ts
|
|
15
|
+
var DEFAULT_BUSY_TIMEOUT = 5e3;
|
|
16
|
+
var DEFAULT_WAL_CHECKPOINT = 1e3;
|
|
17
|
+
var DEFAULT_CACHE_SIZE = -64e3;
|
|
18
|
+
var DEFAULT_MMAP_SIZE = 268435456;
|
|
19
|
+
var SQLiteAuthAdapter = class {
|
|
20
|
+
db = null;
|
|
21
|
+
path;
|
|
22
|
+
saltRounds;
|
|
23
|
+
externalDb;
|
|
24
|
+
busyTimeout;
|
|
25
|
+
walAutoCheckpoint;
|
|
26
|
+
cacheSize;
|
|
27
|
+
mmapSize;
|
|
28
|
+
preparedStatements = /* @__PURE__ */ new Map();
|
|
23
29
|
constructor(options = {}) {
|
|
24
|
-
|
|
25
|
-
this.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
this.path = options.path || "./data/auth.db";
|
|
31
|
+
this.saltRounds = options.saltRounds || 12;
|
|
32
|
+
this.externalDb = !!options.db;
|
|
33
|
+
this.busyTimeout = options.busyTimeout ?? DEFAULT_BUSY_TIMEOUT;
|
|
34
|
+
this.walAutoCheckpoint = options.walAutoCheckpoint ?? DEFAULT_WAL_CHECKPOINT;
|
|
35
|
+
this.cacheSize = options.cacheSize ?? DEFAULT_CACHE_SIZE;
|
|
36
|
+
this.mmapSize = options.mmapSize ?? DEFAULT_MMAP_SIZE;
|
|
37
|
+
if (options.db) {
|
|
38
|
+
this.db = options.db;
|
|
39
|
+
}
|
|
34
40
|
}
|
|
35
41
|
async connect() {
|
|
36
|
-
|
|
42
|
+
if (this.db) return;
|
|
43
|
+
const dir = path.dirname(this.path);
|
|
44
|
+
if (dir && dir !== ".") {
|
|
45
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
const Database = (await import('better-sqlite3')).default;
|
|
48
|
+
this.db = new Database(this.path, {
|
|
49
|
+
timeout: this.busyTimeout
|
|
50
|
+
});
|
|
51
|
+
this.db.pragma("journal_mode = WAL");
|
|
52
|
+
this.db.pragma("synchronous = NORMAL");
|
|
53
|
+
this.db.pragma("cache_size = " + this.cacheSize);
|
|
54
|
+
this.db.pragma("mmap_size = " + this.mmapSize);
|
|
55
|
+
this.db.pragma("wal_autocheckpoint = " + this.walAutoCheckpoint);
|
|
56
|
+
this.db.pragma("foreign_keys = ON");
|
|
57
|
+
this.db.pragma("temp_store = MEMORY");
|
|
58
|
+
this.ensureTables();
|
|
59
|
+
this.prepareStatements();
|
|
37
60
|
}
|
|
38
61
|
async disconnect() {
|
|
39
|
-
|
|
62
|
+
if (this.db && !this.externalDb) {
|
|
63
|
+
this.db.pragma("wal_checkpoint(TRUNCATE)");
|
|
64
|
+
this.db.close();
|
|
65
|
+
this.db = null;
|
|
66
|
+
this.preparedStatements.clear();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
ensureTables() {
|
|
70
|
+
if (!this.db) return;
|
|
71
|
+
this.db.exec(`
|
|
72
|
+
CREATE TABLE IF NOT EXISTS kyro_users (
|
|
73
|
+
id TEXT PRIMARY KEY,
|
|
74
|
+
email TEXT UNIQUE NOT NULL,
|
|
75
|
+
password_hash TEXT NOT NULL,
|
|
76
|
+
role TEXT NOT NULL DEFAULT 'customer',
|
|
77
|
+
tenant_id TEXT,
|
|
78
|
+
email_verified INTEGER DEFAULT 0,
|
|
79
|
+
locked INTEGER DEFAULT 0,
|
|
80
|
+
last_login TEXT,
|
|
81
|
+
failed_login_attempts INTEGER DEFAULT 0,
|
|
82
|
+
locked_until TEXT,
|
|
83
|
+
created_at TEXT NOT NULL,
|
|
84
|
+
updated_at TEXT NOT NULL
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
CREATE TABLE IF NOT EXISTS kyro_sessions (
|
|
88
|
+
id TEXT PRIMARY KEY,
|
|
89
|
+
user_id TEXT NOT NULL,
|
|
90
|
+
token TEXT NOT NULL,
|
|
91
|
+
refresh_token TEXT,
|
|
92
|
+
expires_at TEXT NOT NULL,
|
|
93
|
+
created_at TEXT NOT NULL,
|
|
94
|
+
ip_address TEXT,
|
|
95
|
+
user_agent TEXT,
|
|
96
|
+
FOREIGN KEY (user_id) REFERENCES kyro_users(id) ON DELETE CASCADE
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
CREATE TABLE IF NOT EXISTS kyro_password_history (
|
|
100
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
101
|
+
user_id TEXT NOT NULL,
|
|
102
|
+
password_hash TEXT NOT NULL,
|
|
103
|
+
created_at TEXT NOT NULL,
|
|
104
|
+
FOREIGN KEY (user_id) REFERENCES kyro_users(id) ON DELETE CASCADE
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
CREATE TABLE IF NOT EXISTS kyro_rate_limits (
|
|
108
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
109
|
+
key TEXT NOT NULL,
|
|
110
|
+
window_start INTEGER NOT NULL,
|
|
111
|
+
count INTEGER NOT NULL DEFAULT 1,
|
|
112
|
+
UNIQUE(key, window_start)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
CREATE TABLE IF NOT EXISTS kyro_lockouts (
|
|
116
|
+
user_id TEXT PRIMARY KEY,
|
|
117
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
118
|
+
last_attempt INTEGER,
|
|
119
|
+
locked_at INTEGER,
|
|
120
|
+
locked_until INTEGER
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
CREATE TABLE IF NOT EXISTS kyro_audit_logs (
|
|
124
|
+
id TEXT PRIMARY KEY,
|
|
125
|
+
timestamp TEXT NOT NULL,
|
|
126
|
+
action TEXT NOT NULL,
|
|
127
|
+
user_id TEXT,
|
|
128
|
+
user_email TEXT,
|
|
129
|
+
role TEXT,
|
|
130
|
+
resource TEXT NOT NULL,
|
|
131
|
+
resource_id TEXT,
|
|
132
|
+
ip_address TEXT,
|
|
133
|
+
user_agent TEXT,
|
|
134
|
+
success INTEGER NOT NULL,
|
|
135
|
+
error TEXT,
|
|
136
|
+
metadata TEXT,
|
|
137
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_users_email ON kyro_users(email);
|
|
141
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_sessions_user_id ON kyro_sessions(user_id);
|
|
142
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_sessions_token ON kyro_sessions(token);
|
|
143
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_sessions_refresh_token ON kyro_sessions(refresh_token);
|
|
144
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_sessions_expires ON kyro_sessions(expires_at);
|
|
145
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_password_history_user_id ON kyro_password_history(user_id);
|
|
146
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_rate_limits_key ON kyro_rate_limits(key);
|
|
147
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_rate_limits_window ON kyro_rate_limits(window_start);
|
|
148
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_lockouts_locked_until ON kyro_lockouts(locked_until);
|
|
149
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_audit_logs_timestamp ON kyro_audit_logs(timestamp);
|
|
150
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_audit_logs_action ON kyro_audit_logs(action);
|
|
151
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_audit_logs_user_id ON kyro_audit_logs(user_id);
|
|
152
|
+
CREATE INDEX IF NOT EXISTS idx_kyro_audit_logs_resource ON kyro_audit_logs(resource);
|
|
153
|
+
`);
|
|
40
154
|
}
|
|
41
|
-
|
|
42
|
-
|
|
155
|
+
prepareStatements() {
|
|
156
|
+
if (!this.db) return;
|
|
157
|
+
this.preparedStatements.set(
|
|
158
|
+
"findUserByEmail",
|
|
159
|
+
this.db.prepare("SELECT * FROM kyro_users WHERE email = ?")
|
|
160
|
+
);
|
|
161
|
+
this.preparedStatements.set(
|
|
162
|
+
"findUserById",
|
|
163
|
+
this.db.prepare("SELECT * FROM kyro_users WHERE id = ?")
|
|
164
|
+
);
|
|
165
|
+
this.preparedStatements.set(
|
|
166
|
+
"findSessionByToken",
|
|
167
|
+
this.db.prepare("SELECT * FROM kyro_sessions WHERE token = ?")
|
|
168
|
+
);
|
|
169
|
+
this.preparedStatements.set(
|
|
170
|
+
"findSessionByRefreshToken",
|
|
171
|
+
this.db.prepare("SELECT * FROM kyro_sessions WHERE refresh_token = ?")
|
|
172
|
+
);
|
|
173
|
+
this.preparedStatements.set(
|
|
174
|
+
"deleteSession",
|
|
175
|
+
this.db.prepare("DELETE FROM kyro_sessions WHERE id = ? OR token = ?")
|
|
176
|
+
);
|
|
177
|
+
this.preparedStatements.set(
|
|
178
|
+
"deleteUserSessions",
|
|
179
|
+
this.db.prepare("DELETE FROM kyro_sessions WHERE user_id = ?")
|
|
180
|
+
);
|
|
181
|
+
this.preparedStatements.set(
|
|
182
|
+
"countUsers",
|
|
183
|
+
this.db.prepare("SELECT COUNT(*) as count FROM kyro_users")
|
|
184
|
+
);
|
|
185
|
+
this.preparedStatements.set(
|
|
186
|
+
"deleteUser",
|
|
187
|
+
this.db.prepare("DELETE FROM kyro_users WHERE id = ?")
|
|
188
|
+
);
|
|
189
|
+
this.preparedStatements.set(
|
|
190
|
+
"getPasswordHistory",
|
|
191
|
+
this.db.prepare(
|
|
192
|
+
"SELECT password_hash FROM kyro_password_history WHERE user_id = ? ORDER BY created_at DESC LIMIT ?"
|
|
193
|
+
)
|
|
194
|
+
);
|
|
195
|
+
this.preparedStatements.set(
|
|
196
|
+
"addPasswordHistory",
|
|
197
|
+
this.db.prepare(
|
|
198
|
+
"INSERT INTO kyro_password_history (user_id, password_hash, created_at) VALUES (?, ?, ?)"
|
|
199
|
+
)
|
|
200
|
+
);
|
|
201
|
+
this.preparedStatements.set(
|
|
202
|
+
"trimPasswordHistory",
|
|
203
|
+
this.db.prepare(
|
|
204
|
+
`DELETE FROM kyro_password_history WHERE id IN (
|
|
205
|
+
SELECT id FROM kyro_password_history WHERE user_id = ? ORDER BY created_at DESC LIMIT -1 OFFSET 5
|
|
206
|
+
)`
|
|
207
|
+
)
|
|
208
|
+
);
|
|
209
|
+
this.preparedStatements.set(
|
|
210
|
+
"deleteExpiredSessions",
|
|
211
|
+
this.db.prepare("DELETE FROM kyro_sessions WHERE expires_at < ?")
|
|
212
|
+
);
|
|
213
|
+
this.preparedStatements.set(
|
|
214
|
+
"cleanupOldAuditLogs",
|
|
215
|
+
this.db.prepare("DELETE FROM kyro_audit_logs WHERE timestamp < ?")
|
|
216
|
+
);
|
|
217
|
+
this.preparedStatements.set(
|
|
218
|
+
"cleanupExpiredLockouts",
|
|
219
|
+
this.db.prepare(
|
|
220
|
+
"UPDATE kyro_lockouts SET attempts = 0, locked_at = NULL, locked_until = NULL WHERE locked_until < ?"
|
|
221
|
+
)
|
|
222
|
+
);
|
|
223
|
+
this.preparedStatements.set(
|
|
224
|
+
"getLockout",
|
|
225
|
+
this.db.prepare("SELECT * FROM kyro_lockouts WHERE user_id = ?")
|
|
226
|
+
);
|
|
227
|
+
this.preparedStatements.set(
|
|
228
|
+
"upsertLockout",
|
|
229
|
+
this.db.prepare(`
|
|
230
|
+
INSERT INTO kyro_lockouts (user_id, attempts, last_attempt, locked_at, locked_until)
|
|
231
|
+
VALUES (?, ?, ?, ?, ?)
|
|
232
|
+
ON CONFLICT(user_id) DO UPDATE SET
|
|
233
|
+
attempts = excluded.attempts,
|
|
234
|
+
last_attempt = excluded.last_attempt,
|
|
235
|
+
locked_at = excluded.locked_at,
|
|
236
|
+
locked_until = excluded.locked_until
|
|
237
|
+
`)
|
|
238
|
+
);
|
|
239
|
+
this.preparedStatements.set(
|
|
240
|
+
"resetLockout",
|
|
241
|
+
this.db.prepare(
|
|
242
|
+
"UPDATE kyro_lockouts SET attempts = 0, locked_at = NULL, locked_until = NULL WHERE user_id = ?"
|
|
243
|
+
)
|
|
244
|
+
);
|
|
43
245
|
}
|
|
44
|
-
|
|
45
|
-
|
|
246
|
+
stmt(name) {
|
|
247
|
+
const stmt = this.preparedStatements.get(name);
|
|
248
|
+
if (!stmt) throw new Error(`Prepared statement not found: ${name}`);
|
|
249
|
+
return stmt;
|
|
46
250
|
}
|
|
47
|
-
|
|
48
|
-
|
|
251
|
+
async cleanupExpiredSessions() {
|
|
252
|
+
if (!this.db) throw new Error("Not connected");
|
|
253
|
+
const result = this.stmt("deleteExpiredSessions").run(
|
|
254
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
255
|
+
);
|
|
256
|
+
return result.changes;
|
|
49
257
|
}
|
|
50
|
-
|
|
51
|
-
|
|
258
|
+
async cleanupOldAuditLogs(retentionDays = 30) {
|
|
259
|
+
if (!this.db) throw new Error("Not connected");
|
|
260
|
+
const cutoff = new Date(
|
|
261
|
+
Date.now() - retentionDays * 24 * 60 * 60 * 1e3
|
|
262
|
+
).toISOString();
|
|
263
|
+
const result = this.stmt("cleanupOldAuditLogs").run(cutoff);
|
|
264
|
+
return result.changes;
|
|
52
265
|
}
|
|
53
|
-
|
|
54
|
-
|
|
266
|
+
async getStats() {
|
|
267
|
+
if (!this.db) throw new Error("Not connected");
|
|
268
|
+
const userCount = this.stmt("countUsers").get().count;
|
|
269
|
+
const activeSessionCount = this.db.prepare(
|
|
270
|
+
"SELECT COUNT(*) as count FROM kyro_sessions WHERE expires_at > ?"
|
|
271
|
+
).get((/* @__PURE__ */ new Date()).toISOString()).count;
|
|
272
|
+
const auditLogCount = this.db.prepare("SELECT COUNT(*) as count FROM kyro_audit_logs").get().count;
|
|
273
|
+
return { userCount, activeSessionCount, auditLogCount };
|
|
55
274
|
}
|
|
56
275
|
async createUser(data) {
|
|
57
|
-
|
|
276
|
+
if (!this.db) throw new Error("Not connected");
|
|
277
|
+
const id = crypto.randomBytes(16).toString("hex");
|
|
58
278
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
59
279
|
const user = {
|
|
60
|
-
id
|
|
280
|
+
id,
|
|
61
281
|
email: data.email.toLowerCase(),
|
|
62
282
|
passwordHash: data.passwordHash,
|
|
63
283
|
role: data.role || "customer",
|
|
@@ -65,197 +285,317 @@ var RedisAuthAdapter = class {
|
|
|
65
285
|
createdAt: now,
|
|
66
286
|
updatedAt: now
|
|
67
287
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
288
|
+
this.db.prepare(
|
|
289
|
+
`INSERT INTO kyro_users (id, email, password_hash, role, tenant_id, created_at, updated_at)
|
|
290
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
291
|
+
).run(
|
|
292
|
+
id,
|
|
293
|
+
user.email,
|
|
294
|
+
user.passwordHash,
|
|
295
|
+
user.role,
|
|
296
|
+
user.tenantId,
|
|
297
|
+
now,
|
|
298
|
+
now
|
|
299
|
+
);
|
|
72
300
|
return user;
|
|
73
301
|
}
|
|
74
302
|
async findUserByEmail(email) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
return this.findUserById(userId);
|
|
303
|
+
if (!this.db) throw new Error("Not connected");
|
|
304
|
+
const row = this.stmt("findUserByEmail").get(email.toLowerCase());
|
|
305
|
+
if (!row) return null;
|
|
306
|
+
return this.rowToUser(row);
|
|
80
307
|
}
|
|
81
308
|
async findUserById(userId) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
309
|
+
if (!this.db) throw new Error("Not connected");
|
|
310
|
+
const row = this.stmt("findUserById").get(userId);
|
|
311
|
+
if (!row) return null;
|
|
312
|
+
return this.rowToUser(row);
|
|
85
313
|
}
|
|
86
314
|
async updateUser(userId, data) {
|
|
315
|
+
if (!this.db) throw new Error("Not connected");
|
|
87
316
|
const existing = await this.findUserById(userId);
|
|
88
317
|
if (!existing) return null;
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
if (data.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
318
|
+
const updates = [];
|
|
319
|
+
const values = [];
|
|
320
|
+
if (data.email !== void 0) {
|
|
321
|
+
updates.push("email = ?");
|
|
322
|
+
values.push(data.email.toLowerCase());
|
|
323
|
+
}
|
|
324
|
+
if (data.passwordHash !== void 0) {
|
|
325
|
+
updates.push("password_hash = ?");
|
|
326
|
+
values.push(data.passwordHash);
|
|
327
|
+
}
|
|
328
|
+
if (data.role !== void 0) {
|
|
329
|
+
updates.push("role = ?");
|
|
330
|
+
values.push(data.role);
|
|
331
|
+
}
|
|
332
|
+
if (data.tenantId !== void 0) {
|
|
333
|
+
updates.push("tenant_id = ?");
|
|
334
|
+
values.push(data.tenantId);
|
|
335
|
+
}
|
|
336
|
+
if (data.emailVerified !== void 0) {
|
|
337
|
+
updates.push("email_verified = ?");
|
|
338
|
+
values.push(data.emailVerified ? 1 : 0);
|
|
100
339
|
}
|
|
101
|
-
|
|
102
|
-
|
|
340
|
+
if (data.locked !== void 0) {
|
|
341
|
+
updates.push("locked = ?");
|
|
342
|
+
values.push(data.locked ? 1 : 0);
|
|
343
|
+
}
|
|
344
|
+
if (data.lastLogin !== void 0) {
|
|
345
|
+
updates.push("last_login = ?");
|
|
346
|
+
values.push(data.lastLogin);
|
|
347
|
+
}
|
|
348
|
+
if (data.failedLoginAttempts !== void 0) {
|
|
349
|
+
updates.push("failed_login_attempts = ?");
|
|
350
|
+
values.push(data.failedLoginAttempts);
|
|
351
|
+
}
|
|
352
|
+
updates.push("updated_at = ?");
|
|
353
|
+
values.push((/* @__PURE__ */ new Date()).toISOString());
|
|
354
|
+
values.push(userId);
|
|
355
|
+
this.db.prepare(`UPDATE kyro_users SET ${updates.join(", ")} WHERE id = ?`).run(...values);
|
|
356
|
+
return this.findUserById(userId);
|
|
103
357
|
}
|
|
104
358
|
async deleteUser(userId) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
pipeline.del(this.userKey(userId));
|
|
109
|
-
pipeline.del(this.userByEmailKey(user.email));
|
|
110
|
-
pipeline.del(this.passwordHistoryKey(userId));
|
|
111
|
-
await pipeline.exec();
|
|
112
|
-
return true;
|
|
359
|
+
if (!this.db) throw new Error("Not connected");
|
|
360
|
+
const result = this.stmt("deleteUser").run(userId);
|
|
361
|
+
return result.changes > 0;
|
|
113
362
|
}
|
|
114
363
|
async hashPassword(password) {
|
|
115
|
-
return bcrypt__default.default.hash(password,
|
|
364
|
+
return bcrypt__default.default.hash(password, this.saltRounds);
|
|
116
365
|
}
|
|
117
366
|
async verifyPassword(password, hash) {
|
|
118
367
|
return bcrypt__default.default.compare(password, hash);
|
|
119
368
|
}
|
|
120
369
|
async createSession(userId, data = {}) {
|
|
121
|
-
|
|
370
|
+
if (!this.db) throw new Error("Not connected");
|
|
371
|
+
const id = crypto.randomBytes(32).toString("hex");
|
|
122
372
|
const token = crypto.randomBytes(32).toString("base64url");
|
|
123
373
|
const refreshToken = crypto.randomBytes(32).toString("base64url");
|
|
124
374
|
const now = /* @__PURE__ */ new Date();
|
|
375
|
+
const expiresAt = new Date(now.getTime() + 864e5).toISOString();
|
|
125
376
|
const session = {
|
|
126
|
-
id
|
|
377
|
+
id,
|
|
127
378
|
userId,
|
|
128
379
|
token,
|
|
129
380
|
refreshToken,
|
|
130
|
-
expiresAt
|
|
131
|
-
now.getTime() + this.tokenExpiration * 1e3
|
|
132
|
-
).toISOString(),
|
|
381
|
+
expiresAt,
|
|
133
382
|
createdAt: now.toISOString(),
|
|
134
383
|
ipAddress: data.ipAddress,
|
|
135
384
|
userAgent: data.userAgent
|
|
136
385
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
386
|
+
this.db.prepare(
|
|
387
|
+
`INSERT INTO kyro_sessions (id, user_id, token, refresh_token, expires_at, created_at, ip_address, user_agent)
|
|
388
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
389
|
+
).run(
|
|
390
|
+
session.id,
|
|
391
|
+
session.userId,
|
|
392
|
+
session.token,
|
|
393
|
+
session.refreshToken,
|
|
394
|
+
session.expiresAt,
|
|
395
|
+
session.createdAt,
|
|
396
|
+
session.ipAddress,
|
|
397
|
+
session.userAgent
|
|
143
398
|
);
|
|
144
|
-
await pipeline.exec();
|
|
145
399
|
return session;
|
|
146
400
|
}
|
|
147
401
|
async findSessionByToken(token) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
402
|
+
if (!this.db) throw new Error("Not connected");
|
|
403
|
+
const row = this.stmt("findSessionByToken").get(token);
|
|
404
|
+
if (!row) return null;
|
|
405
|
+
return this.rowToSession(row);
|
|
406
|
+
}
|
|
407
|
+
async findSessionByRefreshToken(refreshToken) {
|
|
408
|
+
if (!this.db) throw new Error("Not connected");
|
|
409
|
+
const row = this.stmt("findSessionByRefreshToken").get(refreshToken);
|
|
410
|
+
if (!row) return null;
|
|
411
|
+
return this.rowToSession(row);
|
|
151
412
|
}
|
|
152
413
|
async deleteSession(sessionId) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
pipeline.del(this.sessionKey(sessionId));
|
|
157
|
-
if (session.refreshToken) {
|
|
158
|
-
pipeline.del(this.refreshKey(session.refreshToken));
|
|
159
|
-
}
|
|
160
|
-
await pipeline.exec();
|
|
161
|
-
return true;
|
|
414
|
+
if (!this.db) throw new Error("Not connected");
|
|
415
|
+
const result = this.stmt("deleteSession").run(sessionId, sessionId);
|
|
416
|
+
return result.changes > 0;
|
|
162
417
|
}
|
|
163
418
|
async deleteUserSessions(userId) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
"COUNT",
|
|
173
|
-
100
|
|
174
|
-
);
|
|
175
|
-
cursor = nextCursor;
|
|
176
|
-
for (const key of keys) {
|
|
177
|
-
const sessionData = await this.redis.hgetall(key);
|
|
178
|
-
if (sessionData.userId === userId) {
|
|
179
|
-
const sessionId = key.replace(`${this.prefix}sessions:`, "");
|
|
180
|
-
await this.deleteSession(sessionId);
|
|
181
|
-
deleted++;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
} while (cursor !== "0");
|
|
185
|
-
return deleted;
|
|
419
|
+
if (!this.db) throw new Error("Not connected");
|
|
420
|
+
const result = this.stmt("deleteUserSessions").run(userId);
|
|
421
|
+
return result.changes;
|
|
422
|
+
}
|
|
423
|
+
async hasAnyUsers() {
|
|
424
|
+
if (!this.db) throw new Error("Not connected");
|
|
425
|
+
const row = this.stmt("countUsers").get();
|
|
426
|
+
return row.count > 0;
|
|
186
427
|
}
|
|
187
428
|
async addPasswordToHistory(userId, passwordHash) {
|
|
188
|
-
|
|
189
|
-
|
|
429
|
+
if (!this.db) throw new Error("Not connected");
|
|
430
|
+
this.stmt("addPasswordHistory").run(
|
|
431
|
+
userId,
|
|
432
|
+
passwordHash,
|
|
433
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
434
|
+
);
|
|
435
|
+
this.stmt("trimPasswordHistory").run(userId);
|
|
190
436
|
}
|
|
191
437
|
async getPasswordHistory(userId, count = 5) {
|
|
192
|
-
|
|
438
|
+
if (!this.db) throw new Error("Not connected");
|
|
439
|
+
const rows = this.stmt("getPasswordHistory").all(userId, count);
|
|
440
|
+
return rows.map((r) => r.password_hash);
|
|
193
441
|
}
|
|
194
442
|
async isPasswordInHistory(password, userId, historyCount = 5) {
|
|
195
443
|
const history = await this.getPasswordHistory(userId, historyCount);
|
|
196
444
|
for (const hash of history) {
|
|
197
|
-
if (await
|
|
445
|
+
if (await bcrypt__default.default.compare(password, hash)) {
|
|
198
446
|
return true;
|
|
199
447
|
}
|
|
200
448
|
}
|
|
201
449
|
return false;
|
|
202
450
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
451
|
+
async recordFailedAttempt(userId) {
|
|
452
|
+
if (!this.db) throw new Error("Not connected");
|
|
453
|
+
const now = Date.now();
|
|
454
|
+
const lockout = this.stmt("getLockout").get(userId);
|
|
455
|
+
const attempts = (lockout?.attempts || 0) + 1;
|
|
456
|
+
const lockedUntil = attempts >= 5 ? now + 15 * 60 * 1e3 : lockout?.locked_until || null;
|
|
457
|
+
this.stmt("upsertLockout").run(
|
|
458
|
+
userId,
|
|
459
|
+
attempts,
|
|
460
|
+
now,
|
|
461
|
+
lockedUntil !== null ? now : null,
|
|
462
|
+
lockedUntil
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
async resetAttempts(userId) {
|
|
466
|
+
if (!this.db) throw new Error("Not connected");
|
|
467
|
+
this.stmt("resetLockout").run(userId);
|
|
468
|
+
}
|
|
469
|
+
async checkLockout(userId) {
|
|
470
|
+
if (!this.db) throw new Error("Not connected");
|
|
471
|
+
this.stmt("cleanupExpiredLockouts").run(Date.now());
|
|
472
|
+
const lockout = this.stmt("getLockout").get(userId);
|
|
473
|
+
if (!lockout) {
|
|
474
|
+
return {
|
|
475
|
+
locked: false,
|
|
476
|
+
attemptsRemaining: 5,
|
|
477
|
+
totalAttempts: 0
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
if (lockout.locked_until !== null && lockout.locked_until > Date.now()) {
|
|
481
|
+
return {
|
|
482
|
+
locked: true,
|
|
483
|
+
attemptsRemaining: 0,
|
|
484
|
+
lockedUntil: new Date(lockout.locked_until),
|
|
485
|
+
totalAttempts: lockout.attempts
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
locked: false,
|
|
490
|
+
attemptsRemaining: Math.max(0, 5 - lockout.attempts),
|
|
491
|
+
totalAttempts: lockout.attempts
|
|
211
492
|
};
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
493
|
+
}
|
|
494
|
+
async logAudit(data) {
|
|
495
|
+
if (!this.db) throw new Error("Not connected");
|
|
496
|
+
const id = crypto.randomBytes(16).toString("hex");
|
|
497
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
498
|
+
this.db.prepare(
|
|
499
|
+
`INSERT INTO kyro_audit_logs (
|
|
500
|
+
id, timestamp, action, user_id, user_email, role, resource, resource_id,
|
|
501
|
+
ip_address, user_agent, success, error, metadata, created_at
|
|
502
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
503
|
+
).run(
|
|
504
|
+
id,
|
|
505
|
+
timestamp,
|
|
506
|
+
data.action,
|
|
507
|
+
data.userId || null,
|
|
508
|
+
data.userEmail || null,
|
|
509
|
+
data.role || null,
|
|
510
|
+
data.resource,
|
|
511
|
+
data.resourceId || null,
|
|
512
|
+
data.ipAddress || null,
|
|
513
|
+
data.userAgent || null,
|
|
514
|
+
data.success ? 1 : 0,
|
|
515
|
+
data.error || null,
|
|
516
|
+
data.metadata ? JSON.stringify(data.metadata) : null,
|
|
517
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
518
|
+
);
|
|
519
|
+
return id;
|
|
520
|
+
}
|
|
521
|
+
async queryAuditLogs(options = {}) {
|
|
522
|
+
if (!this.db) throw new Error("Not connected");
|
|
523
|
+
const conditions = [];
|
|
524
|
+
const params = [];
|
|
525
|
+
if (options.action) {
|
|
526
|
+
conditions.push("action = ?");
|
|
527
|
+
params.push(options.action);
|
|
528
|
+
}
|
|
529
|
+
if (options.userId) {
|
|
530
|
+
conditions.push("user_id = ?");
|
|
531
|
+
params.push(options.userId);
|
|
532
|
+
}
|
|
533
|
+
if (options.resource) {
|
|
534
|
+
conditions.push("resource = ?");
|
|
535
|
+
params.push(options.resource);
|
|
536
|
+
}
|
|
537
|
+
if (options.success !== void 0) {
|
|
538
|
+
conditions.push("success = ?");
|
|
539
|
+
params.push(options.success ? 1 : 0);
|
|
540
|
+
}
|
|
541
|
+
if (options.startDate) {
|
|
542
|
+
conditions.push("timestamp >= ?");
|
|
543
|
+
params.push(options.startDate.toISOString());
|
|
544
|
+
}
|
|
545
|
+
if (options.endDate) {
|
|
546
|
+
conditions.push("timestamp <= ?");
|
|
547
|
+
params.push(options.endDate.toISOString());
|
|
548
|
+
}
|
|
549
|
+
const where = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
|
|
550
|
+
const limit = options.limit || 50;
|
|
551
|
+
const offset = options.offset || 0;
|
|
552
|
+
const totalResult = this.db.prepare(`SELECT COUNT(*) as count FROM kyro_audit_logs ${where}`).get(...params);
|
|
553
|
+
const rows = this.db.prepare(
|
|
554
|
+
`SELECT * FROM kyro_audit_logs ${where} ORDER BY timestamp DESC LIMIT ? OFFSET ?`
|
|
555
|
+
).all(...params, limit, offset);
|
|
222
556
|
return {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
557
|
+
total: totalResult.count,
|
|
558
|
+
logs: rows.map((row) => ({
|
|
559
|
+
id: row.id,
|
|
560
|
+
timestamp: new Date(row.timestamp),
|
|
561
|
+
action: row.action,
|
|
562
|
+
userId: row.user_id || void 0,
|
|
563
|
+
userEmail: row.user_email || void 0,
|
|
564
|
+
resource: row.resource,
|
|
565
|
+
resourceId: row.resource_id || void 0,
|
|
566
|
+
ipAddress: row.ip_address || void 0,
|
|
567
|
+
userAgent: row.user_agent || void 0,
|
|
568
|
+
success: row.success === 1,
|
|
569
|
+
error: row.error || void 0,
|
|
570
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0
|
|
571
|
+
}))
|
|
234
572
|
};
|
|
235
573
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
id:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
574
|
+
rowToUser(row) {
|
|
575
|
+
return {
|
|
576
|
+
id: row.id,
|
|
577
|
+
email: row.email,
|
|
578
|
+
passwordHash: row.password_hash,
|
|
579
|
+
role: row.role,
|
|
580
|
+
tenantId: row.tenant_id,
|
|
581
|
+
emailVerified: row.email_verified === 1,
|
|
582
|
+
locked: row.locked === 1,
|
|
583
|
+
lastLogin: row.last_login,
|
|
584
|
+
failedLoginAttempts: row.failed_login_attempts || 0,
|
|
585
|
+
createdAt: row.created_at,
|
|
586
|
+
updatedAt: row.updated_at
|
|
243
587
|
};
|
|
244
|
-
if (session.refreshToken) hash.refreshToken = session.refreshToken;
|
|
245
|
-
if (session.ipAddress) hash.ipAddress = session.ipAddress;
|
|
246
|
-
if (session.userAgent) hash.userAgent = session.userAgent;
|
|
247
|
-
return hash;
|
|
248
588
|
}
|
|
249
|
-
|
|
589
|
+
rowToSession(row) {
|
|
250
590
|
return {
|
|
251
|
-
id:
|
|
252
|
-
userId:
|
|
253
|
-
token:
|
|
254
|
-
refreshToken:
|
|
255
|
-
expiresAt:
|
|
256
|
-
createdAt:
|
|
257
|
-
ipAddress:
|
|
258
|
-
userAgent:
|
|
591
|
+
id: row.id,
|
|
592
|
+
userId: row.user_id,
|
|
593
|
+
token: row.token,
|
|
594
|
+
refreshToken: row.refresh_token,
|
|
595
|
+
expiresAt: row.expires_at,
|
|
596
|
+
createdAt: row.created_at,
|
|
597
|
+
ipAddress: row.ip_address,
|
|
598
|
+
userAgent: row.user_agent
|
|
259
599
|
};
|
|
260
600
|
}
|
|
261
601
|
};
|
|
@@ -751,10 +1091,6 @@ var PasswordPolicy = class {
|
|
|
751
1091
|
// src/auth/bootstrap.ts
|
|
752
1092
|
async function bootstrapAdmin(config) {
|
|
753
1093
|
const {
|
|
754
|
-
redisUrl,
|
|
755
|
-
redisHost,
|
|
756
|
-
redisPort,
|
|
757
|
-
redisPassword,
|
|
758
1094
|
adminEmail,
|
|
759
1095
|
adminPassword,
|
|
760
1096
|
adminRole = "super_admin",
|
|
@@ -762,34 +1098,21 @@ async function bootstrapAdmin(config) {
|
|
|
762
1098
|
emailConfig,
|
|
763
1099
|
sendWelcomeEmail = false
|
|
764
1100
|
} = config;
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
} else {
|
|
769
|
-
redis = new Redis2__default.default({
|
|
770
|
-
host: redisHost || "localhost",
|
|
771
|
-
port: redisPort || 6379,
|
|
772
|
-
password: redisPassword,
|
|
773
|
-
lazyConnect: true
|
|
774
|
-
});
|
|
775
|
-
}
|
|
1101
|
+
const authAdapter = config.authAdapter || new SQLiteAuthAdapter({
|
|
1102
|
+
path: config.authDbPath || "./data/auth.db"
|
|
1103
|
+
});
|
|
776
1104
|
try {
|
|
777
|
-
await
|
|
1105
|
+
await authAdapter.connect?.();
|
|
778
1106
|
} catch (error) {
|
|
779
1107
|
return {
|
|
780
1108
|
success: false,
|
|
781
|
-
error: "Failed to connect to
|
|
1109
|
+
error: "Failed to connect to auth storage"
|
|
782
1110
|
};
|
|
783
1111
|
}
|
|
784
|
-
const authAdapter = new RedisAuthAdapter({
|
|
785
|
-
url: redisUrl,
|
|
786
|
-
host: redisHost,
|
|
787
|
-
port: redisPort,
|
|
788
|
-
password: redisPassword
|
|
789
|
-
});
|
|
790
1112
|
const passwordPolicy = new PasswordPolicy();
|
|
791
1113
|
const passwordValidation = passwordPolicy.validate(adminPassword);
|
|
792
1114
|
if (!passwordValidation.valid) {
|
|
1115
|
+
await authAdapter.disconnect?.();
|
|
793
1116
|
return {
|
|
794
1117
|
success: false,
|
|
795
1118
|
error: `Invalid password: ${passwordValidation.errors.join(", ")}`
|
|
@@ -797,6 +1120,7 @@ async function bootstrapAdmin(config) {
|
|
|
797
1120
|
}
|
|
798
1121
|
const existingUser = await authAdapter.findUserByEmail(adminEmail);
|
|
799
1122
|
if (existingUser) {
|
|
1123
|
+
await authAdapter.disconnect?.();
|
|
800
1124
|
return {
|
|
801
1125
|
success: false,
|
|
802
1126
|
error: "Admin user already exists"
|
|
@@ -819,23 +1143,21 @@ async function bootstrapAdmin(config) {
|
|
|
819
1143
|
...welcomeTemplate
|
|
820
1144
|
});
|
|
821
1145
|
}
|
|
1146
|
+
await authAdapter.disconnect?.();
|
|
822
1147
|
return {
|
|
823
1148
|
success: true,
|
|
824
1149
|
user
|
|
825
1150
|
};
|
|
826
1151
|
} catch (error) {
|
|
1152
|
+
await authAdapter.disconnect?.();
|
|
827
1153
|
return {
|
|
828
1154
|
success: false,
|
|
829
1155
|
error: error instanceof Error ? error.message : "Failed to create admin user"
|
|
830
1156
|
};
|
|
831
|
-
} finally {
|
|
832
|
-
await redis.quit();
|
|
833
1157
|
}
|
|
834
1158
|
}
|
|
835
|
-
async function checkBootstrapRequired(
|
|
836
|
-
const existingUser = await
|
|
837
|
-
`kyro:auth:users:email:${adminEmail.toLowerCase()}`
|
|
838
|
-
);
|
|
1159
|
+
async function checkBootstrapRequired(authAdapter, adminEmail) {
|
|
1160
|
+
const existingUser = await authAdapter.findUserByEmail(adminEmail);
|
|
839
1161
|
return !existingUser;
|
|
840
1162
|
}
|
|
841
1163
|
function getBootstrapFromEnv() {
|
|
@@ -845,10 +1167,7 @@ function getBootstrapFromEnv() {
|
|
|
845
1167
|
return null;
|
|
846
1168
|
}
|
|
847
1169
|
return {
|
|
848
|
-
|
|
849
|
-
redisHost: process.env.REDIS_HOST,
|
|
850
|
-
redisPort: process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT, 10) : void 0,
|
|
851
|
-
redisPassword: process.env.REDIS_PASSWORD,
|
|
1170
|
+
authDbPath: process.env.KYRO_AUTH_DB_PATH || "./data/auth.db",
|
|
852
1171
|
adminEmail: email,
|
|
853
1172
|
adminPassword: password,
|
|
854
1173
|
adminRole: process.env.KYRO_ADMIN_ROLE || "super_admin",
|
|
@@ -904,11 +1223,11 @@ async function bootstrapWithRetry(config, maxRetries = 3, retryDelayMs = 2e3) {
|
|
|
904
1223
|
|
|
905
1224
|
exports.EmailTransport = EmailTransport;
|
|
906
1225
|
exports.PasswordPolicy = PasswordPolicy;
|
|
907
|
-
exports.
|
|
1226
|
+
exports.SQLiteAuthAdapter = SQLiteAuthAdapter;
|
|
908
1227
|
exports.autoBootstrap = autoBootstrap;
|
|
909
1228
|
exports.bootstrapAdmin = bootstrapAdmin;
|
|
910
1229
|
exports.bootstrapWithRetry = bootstrapWithRetry;
|
|
911
1230
|
exports.checkBootstrapRequired = checkBootstrapRequired;
|
|
912
1231
|
exports.getBootstrapFromEnv = getBootstrapFromEnv;
|
|
913
|
-
//# sourceMappingURL=chunk-
|
|
914
|
-
//# sourceMappingURL=chunk-
|
|
1232
|
+
//# sourceMappingURL=chunk-3EVLFWH2.cjs.map
|
|
1233
|
+
//# sourceMappingURL=chunk-3EVLFWH2.cjs.map
|