@spfn/auth 0.1.0-alpha.1 → 0.1.0-alpha.87
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/README.md +250 -0
- package/dist/adapters/nextjs/api.d.ts +446 -0
- package/dist/adapters/nextjs/api.js +3279 -0
- package/dist/adapters/nextjs/api.js.map +1 -0
- package/dist/adapters/nextjs/server.d.ts +246 -0
- package/dist/adapters/nextjs/server.js +3645 -0
- package/dist/adapters/nextjs/server.js.map +1 -0
- package/dist/index.d.ts +3 -46
- package/dist/index.js +7438 -7304
- package/dist/index.js.map +1 -1
- package/dist/lib/api/auth-codes-verify.d.ts +37 -0
- package/dist/lib/api/auth-codes-verify.js +2949 -0
- package/dist/lib/api/auth-codes-verify.js.map +1 -0
- package/dist/lib/api/auth-codes.d.ts +37 -0
- package/dist/lib/api/auth-codes.js +2949 -0
- package/dist/lib/api/auth-codes.js.map +1 -0
- package/dist/lib/api/auth-exists.d.ts +38 -0
- package/dist/lib/api/auth-exists.js +2949 -0
- package/dist/lib/api/auth-exists.js.map +1 -0
- package/dist/lib/api/auth-invitations-accept.d.ts +38 -0
- package/dist/lib/api/auth-invitations-accept.js +2883 -0
- package/dist/lib/api/auth-invitations-accept.js.map +1 -0
- package/dist/lib/api/auth-invitations-cancel.d.ts +37 -0
- package/dist/lib/api/auth-invitations-cancel.js +2883 -0
- package/dist/lib/api/auth-invitations-cancel.js.map +1 -0
- package/dist/lib/api/auth-invitations-delete.d.ts +36 -0
- package/dist/lib/api/auth-invitations-delete.js +2883 -0
- package/dist/lib/api/auth-invitations-delete.js.map +1 -0
- package/dist/lib/api/auth-invitations-resend.d.ts +37 -0
- package/dist/lib/api/auth-invitations-resend.js +2883 -0
- package/dist/lib/api/auth-invitations-resend.js.map +1 -0
- package/dist/lib/api/auth-invitations.d.ts +109 -0
- package/dist/lib/api/auth-invitations.js +2887 -0
- package/dist/lib/api/auth-invitations.js.map +1 -0
- package/dist/lib/api/auth-keys-rotate.d.ts +37 -0
- package/dist/lib/api/auth-keys-rotate.js +2949 -0
- package/dist/lib/api/auth-keys-rotate.js.map +1 -0
- package/dist/lib/api/auth-login.d.ts +39 -0
- package/dist/lib/api/auth-login.js +2949 -0
- package/dist/lib/api/auth-login.js.map +1 -0
- package/dist/lib/api/auth-logout.d.ts +36 -0
- package/dist/lib/api/auth-logout.js +2949 -0
- package/dist/lib/api/auth-logout.js.map +1 -0
- package/dist/lib/api/auth-me.d.ts +50 -0
- package/dist/lib/api/auth-me.js +2949 -0
- package/dist/lib/api/auth-me.js.map +1 -0
- package/dist/lib/api/auth-password.d.ts +36 -0
- package/dist/lib/api/auth-password.js +2949 -0
- package/dist/lib/api/auth-password.js.map +1 -0
- package/dist/lib/api/auth-register.d.ts +38 -0
- package/dist/lib/api/auth-register.js +2949 -0
- package/dist/lib/api/auth-register.js.map +1 -0
- package/dist/lib/api/index.d.ts +356 -0
- package/dist/lib/api/index.js +3261 -0
- package/dist/lib/api/index.js.map +1 -0
- package/dist/lib/config.d.ts +70 -0
- package/dist/lib/config.js +64 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/contracts/auth.d.ts +41 -1
- package/dist/lib/contracts/auth.js +28 -0
- package/dist/lib/contracts/auth.js.map +1 -1
- package/dist/lib/contracts/index.d.ts +1 -1
- package/dist/lib/contracts/index.js +28 -0
- package/dist/lib/contracts/index.js.map +1 -1
- package/dist/lib/crypto.d.ts +76 -0
- package/dist/lib/crypto.js +127 -0
- package/dist/lib/crypto.js.map +1 -0
- package/dist/lib/index.d.ts +4 -0
- package/dist/lib/index.js +313 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/session.d.ts +68 -0
- package/dist/lib/session.js +126 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/{api-BcQM4WKb.d.ts → lib/types/api.d.ts} +2 -2
- package/dist/lib/types/api.js +1 -0
- package/dist/lib/types/api.js.map +1 -0
- package/dist/lib/types/index.d.ts +3 -0
- package/dist/lib/types/index.js +2647 -0
- package/dist/lib/types/index.js.map +1 -0
- package/dist/lib/types/schemas.d.ts +45 -0
- package/dist/lib/types/schemas.js +2647 -0
- package/dist/lib/types/schemas.js.map +1 -0
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +1 -0
- package/dist/lib.js.map +1 -0
- package/dist/plugin.js +7438 -7304
- package/dist/plugin.js.map +1 -1
- package/dist/server/entities/index.d.ts +1 -0
- package/dist/server/entities/index.js +23 -27
- package/dist/server/entities/index.js.map +1 -1
- package/dist/server/entities/invitations.js +12 -9
- package/dist/server/entities/invitations.js.map +1 -1
- package/dist/server/entities/permissions.js +8 -3
- package/dist/server/entities/permissions.js.map +1 -1
- package/dist/server/entities/role-permissions.js +12 -9
- package/dist/server/entities/role-permissions.js.map +1 -1
- package/dist/server/entities/roles.js +8 -3
- package/dist/server/entities/roles.js.map +1 -1
- package/dist/server/entities/schema.d.ts +14 -0
- package/dist/server/entities/schema.js +7 -0
- package/dist/server/entities/schema.js.map +1 -0
- package/dist/server/entities/user-permissions.js +14 -12
- package/dist/server/entities/user-permissions.js.map +1 -1
- package/dist/server/entities/user-public-keys.js +12 -9
- package/dist/server/entities/user-public-keys.js.map +1 -1
- package/dist/server/entities/user-social-accounts.js +12 -9
- package/dist/server/entities/user-social-accounts.js.map +1 -1
- package/dist/server/entities/users.js +10 -6
- package/dist/server/entities/users.js.map +1 -1
- package/dist/server/entities/verification-codes.js +8 -3
- package/dist/server/entities/verification-codes.js.map +1 -1
- package/dist/server/routes/auth/index.js +497 -512
- package/dist/server/routes/auth/index.js.map +1 -1
- package/dist/server/routes/index.js +777 -545
- package/dist/server/routes/index.js.map +1 -1
- package/dist/server/routes/invitations/index.js +416 -230
- package/dist/server/routes/invitations/index.js.map +1 -1
- package/dist/server.d.ts +91 -62
- package/dist/server.js +323 -327
- package/dist/server.js.map +1 -1
- package/migrations/{0000_tired_gambit.sql → 0000_familiar_firebrand.sql} +2 -0
- package/migrations/meta/0000_snapshot.json +4 -2
- package/migrations/meta/_journal.json +2 -2
- package/package.json +41 -7
package/dist/server.js
CHANGED
|
@@ -8,15 +8,25 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// src/server/entities/schema.ts
|
|
12
|
+
import { createFunctionSchema } from "@spfn/core/db";
|
|
13
|
+
var authSchema;
|
|
14
|
+
var init_schema = __esm({
|
|
15
|
+
"src/server/entities/schema.ts"() {
|
|
16
|
+
"use strict";
|
|
17
|
+
authSchema = createFunctionSchema("@spfn/auth");
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
11
21
|
// src/server/entities/roles.ts
|
|
12
22
|
import { text, boolean, integer, index } from "drizzle-orm/pg-core";
|
|
13
|
-
import { id, timestamps
|
|
14
|
-
var
|
|
23
|
+
import { id, timestamps } from "@spfn/core/db";
|
|
24
|
+
var roles;
|
|
15
25
|
var init_roles = __esm({
|
|
16
26
|
"src/server/entities/roles.ts"() {
|
|
17
27
|
"use strict";
|
|
18
|
-
|
|
19
|
-
roles =
|
|
28
|
+
init_schema();
|
|
29
|
+
roles = authSchema.table(
|
|
20
30
|
"roles",
|
|
21
31
|
{
|
|
22
32
|
// Primary key
|
|
@@ -58,15 +68,15 @@ var init_roles = __esm({
|
|
|
58
68
|
|
|
59
69
|
// src/server/entities/users.ts
|
|
60
70
|
import { text as text2, timestamp, check, boolean as boolean2, bigint, index as index2 } from "drizzle-orm/pg-core";
|
|
61
|
-
import { id as id2, timestamps as timestamps2
|
|
71
|
+
import { id as id2, timestamps as timestamps2 } from "@spfn/core/db";
|
|
62
72
|
import { sql } from "drizzle-orm";
|
|
63
|
-
var
|
|
73
|
+
var users;
|
|
64
74
|
var init_users = __esm({
|
|
65
75
|
"src/server/entities/users.ts"() {
|
|
66
76
|
"use strict";
|
|
67
77
|
init_roles();
|
|
68
|
-
|
|
69
|
-
users =
|
|
78
|
+
init_schema();
|
|
79
|
+
users = authSchema.table(
|
|
70
80
|
"users",
|
|
71
81
|
{
|
|
72
82
|
// Identity
|
|
@@ -131,14 +141,14 @@ var init_users = __esm({
|
|
|
131
141
|
|
|
132
142
|
// src/server/entities/user-social-accounts.ts
|
|
133
143
|
import { text as text3, timestamp as timestamp2, uniqueIndex } from "drizzle-orm/pg-core";
|
|
134
|
-
import { id as id3, timestamps as timestamps3, foreignKey
|
|
135
|
-
var
|
|
144
|
+
import { id as id3, timestamps as timestamps3, foreignKey } from "@spfn/core/db";
|
|
145
|
+
var userSocialAccounts;
|
|
136
146
|
var init_user_social_accounts = __esm({
|
|
137
147
|
"src/server/entities/user-social-accounts.ts"() {
|
|
138
148
|
"use strict";
|
|
139
149
|
init_users();
|
|
140
|
-
|
|
141
|
-
userSocialAccounts =
|
|
150
|
+
init_schema();
|
|
151
|
+
userSocialAccounts = authSchema.table(
|
|
142
152
|
"user_social_accounts",
|
|
143
153
|
{
|
|
144
154
|
id: id3(),
|
|
@@ -169,14 +179,14 @@ var init_user_social_accounts = __esm({
|
|
|
169
179
|
|
|
170
180
|
// src/server/entities/user-public-keys.ts
|
|
171
181
|
import { text as text4, timestamp as timestamp3, boolean as boolean3, index as index3 } from "drizzle-orm/pg-core";
|
|
172
|
-
import { id as id4, foreignKey as foreignKey2
|
|
173
|
-
var
|
|
182
|
+
import { id as id4, foreignKey as foreignKey2 } from "@spfn/core/db";
|
|
183
|
+
var userPublicKeys;
|
|
174
184
|
var init_user_public_keys = __esm({
|
|
175
185
|
"src/server/entities/user-public-keys.ts"() {
|
|
176
186
|
"use strict";
|
|
177
187
|
init_users();
|
|
178
|
-
|
|
179
|
-
userPublicKeys =
|
|
188
|
+
init_schema();
|
|
189
|
+
userPublicKeys = authSchema.table(
|
|
180
190
|
"user_public_keys",
|
|
181
191
|
{
|
|
182
192
|
id: id4(),
|
|
@@ -214,13 +224,13 @@ var init_user_public_keys = __esm({
|
|
|
214
224
|
|
|
215
225
|
// src/server/entities/verification-codes.ts
|
|
216
226
|
import { text as text5, timestamp as timestamp4, index as index4 } from "drizzle-orm/pg-core";
|
|
217
|
-
import { id as id5, timestamps as timestamps4
|
|
218
|
-
var
|
|
227
|
+
import { id as id5, timestamps as timestamps4 } from "@spfn/core/db";
|
|
228
|
+
var verificationCodes;
|
|
219
229
|
var init_verification_codes = __esm({
|
|
220
230
|
"src/server/entities/verification-codes.ts"() {
|
|
221
231
|
"use strict";
|
|
222
|
-
|
|
223
|
-
verificationCodes =
|
|
232
|
+
init_schema();
|
|
233
|
+
verificationCodes = authSchema.table(
|
|
224
234
|
"verification_codes",
|
|
225
235
|
{
|
|
226
236
|
id: id5(),
|
|
@@ -261,15 +271,15 @@ var init_verification_codes = __esm({
|
|
|
261
271
|
|
|
262
272
|
// src/server/entities/invitations.ts
|
|
263
273
|
import { text as text6, timestamp as timestamp5, bigint as bigint2, index as index5, jsonb } from "drizzle-orm/pg-core";
|
|
264
|
-
import { id as id6, timestamps as timestamps5
|
|
265
|
-
var
|
|
274
|
+
import { id as id6, timestamps as timestamps5 } from "@spfn/core/db";
|
|
275
|
+
var invitations;
|
|
266
276
|
var init_invitations = __esm({
|
|
267
277
|
"src/server/entities/invitations.ts"() {
|
|
268
278
|
"use strict";
|
|
269
279
|
init_roles();
|
|
270
280
|
init_users();
|
|
271
|
-
|
|
272
|
-
invitations =
|
|
281
|
+
init_schema();
|
|
282
|
+
invitations = authSchema.table(
|
|
273
283
|
"user_invitations",
|
|
274
284
|
{
|
|
275
285
|
// Primary key
|
|
@@ -337,13 +347,13 @@ var init_invitations = __esm({
|
|
|
337
347
|
|
|
338
348
|
// src/server/entities/permissions.ts
|
|
339
349
|
import { text as text7, boolean as boolean4, index as index6 } from "drizzle-orm/pg-core";
|
|
340
|
-
import { id as id7, timestamps as timestamps6
|
|
341
|
-
var
|
|
350
|
+
import { id as id7, timestamps as timestamps6 } from "@spfn/core/db";
|
|
351
|
+
var permissions;
|
|
342
352
|
var init_permissions = __esm({
|
|
343
353
|
"src/server/entities/permissions.ts"() {
|
|
344
354
|
"use strict";
|
|
345
|
-
|
|
346
|
-
permissions =
|
|
355
|
+
init_schema();
|
|
356
|
+
permissions = authSchema.table(
|
|
347
357
|
"permissions",
|
|
348
358
|
{
|
|
349
359
|
// Primary key
|
|
@@ -384,15 +394,15 @@ var init_permissions = __esm({
|
|
|
384
394
|
|
|
385
395
|
// src/server/entities/role-permissions.ts
|
|
386
396
|
import { bigint as bigint3, index as index7, unique } from "drizzle-orm/pg-core";
|
|
387
|
-
import { id as id8, timestamps as timestamps7
|
|
388
|
-
var
|
|
397
|
+
import { id as id8, timestamps as timestamps7 } from "@spfn/core/db";
|
|
398
|
+
var rolePermissions;
|
|
389
399
|
var init_role_permissions = __esm({
|
|
390
400
|
"src/server/entities/role-permissions.ts"() {
|
|
391
401
|
"use strict";
|
|
392
402
|
init_roles();
|
|
393
403
|
init_permissions();
|
|
394
|
-
|
|
395
|
-
rolePermissions =
|
|
404
|
+
init_schema();
|
|
405
|
+
rolePermissions = authSchema.table(
|
|
396
406
|
"role_permissions",
|
|
397
407
|
{
|
|
398
408
|
// Primary key
|
|
@@ -416,15 +426,15 @@ var init_role_permissions = __esm({
|
|
|
416
426
|
|
|
417
427
|
// src/server/entities/user-permissions.ts
|
|
418
428
|
import { bigint as bigint4, boolean as boolean5, text as text8, timestamp as timestamp6, index as index8, unique as unique2 } from "drizzle-orm/pg-core";
|
|
419
|
-
import { id as id9, timestamps as timestamps8
|
|
420
|
-
var
|
|
429
|
+
import { id as id9, timestamps as timestamps8 } from "@spfn/core/db";
|
|
430
|
+
var userPermissions;
|
|
421
431
|
var init_user_permissions = __esm({
|
|
422
432
|
"src/server/entities/user-permissions.ts"() {
|
|
423
433
|
"use strict";
|
|
424
434
|
init_users();
|
|
425
435
|
init_permissions();
|
|
426
|
-
|
|
427
|
-
userPermissions =
|
|
436
|
+
init_schema();
|
|
437
|
+
userPermissions = authSchema.table(
|
|
428
438
|
"user_permissions",
|
|
429
439
|
{
|
|
430
440
|
// Primary key
|
|
@@ -460,6 +470,7 @@ var init_user_permissions = __esm({
|
|
|
460
470
|
// src/server/entities/index.ts
|
|
461
471
|
var entities_exports = {};
|
|
462
472
|
__export(entities_exports, {
|
|
473
|
+
authSchema: () => authSchema,
|
|
463
474
|
invitations: () => invitations,
|
|
464
475
|
permissions: () => permissions,
|
|
465
476
|
rolePermissions: () => rolePermissions,
|
|
@@ -473,6 +484,7 @@ __export(entities_exports, {
|
|
|
473
484
|
var init_entities = __esm({
|
|
474
485
|
"src/server/entities/index.ts"() {
|
|
475
486
|
"use strict";
|
|
487
|
+
init_schema();
|
|
476
488
|
init_users();
|
|
477
489
|
init_user_social_accounts();
|
|
478
490
|
init_user_public_keys();
|
|
@@ -485,6 +497,83 @@ var init_entities = __esm({
|
|
|
485
497
|
}
|
|
486
498
|
});
|
|
487
499
|
|
|
500
|
+
// src/server/helpers/jwt.ts
|
|
501
|
+
var jwt_exports = {};
|
|
502
|
+
__export(jwt_exports, {
|
|
503
|
+
decodeToken: () => decodeToken,
|
|
504
|
+
generateToken: () => generateToken,
|
|
505
|
+
verifyClientToken: () => verifyClientToken,
|
|
506
|
+
verifyKeyFingerprint: () => verifyKeyFingerprint,
|
|
507
|
+
verifyToken: () => verifyToken
|
|
508
|
+
});
|
|
509
|
+
import jwt from "jsonwebtoken";
|
|
510
|
+
import crypto from "crypto";
|
|
511
|
+
function generateToken(payload) {
|
|
512
|
+
return jwt.sign(payload, JWT_SECRET, {
|
|
513
|
+
expiresIn: JWT_EXPIRES_IN
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
function verifyToken(token) {
|
|
517
|
+
return jwt.verify(token, JWT_SECRET);
|
|
518
|
+
}
|
|
519
|
+
function verifyClientToken(token, publicKeyB64, algorithm) {
|
|
520
|
+
try {
|
|
521
|
+
const publicKeyDER = Buffer.from(publicKeyB64, "base64");
|
|
522
|
+
const publicKeyObject = crypto.createPublicKey({
|
|
523
|
+
key: publicKeyDER,
|
|
524
|
+
format: "der",
|
|
525
|
+
type: "spki"
|
|
526
|
+
});
|
|
527
|
+
const decoded = jwt.verify(token, publicKeyObject, {
|
|
528
|
+
algorithms: [algorithm],
|
|
529
|
+
// Prevent algorithm confusion attacks
|
|
530
|
+
issuer: "spfn-client"
|
|
531
|
+
// Validate token issuer
|
|
532
|
+
});
|
|
533
|
+
if (typeof decoded === "string") {
|
|
534
|
+
throw new Error("Invalid token format: expected object payload");
|
|
535
|
+
}
|
|
536
|
+
return decoded;
|
|
537
|
+
} catch (error) {
|
|
538
|
+
if (error instanceof jwt.TokenExpiredError) {
|
|
539
|
+
throw new Error("Token has expired");
|
|
540
|
+
}
|
|
541
|
+
if (error instanceof jwt.JsonWebTokenError) {
|
|
542
|
+
throw new Error("Invalid token signature");
|
|
543
|
+
}
|
|
544
|
+
throw new Error(`Token verification failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function decodeToken(token) {
|
|
548
|
+
try {
|
|
549
|
+
return jwt.decode(token);
|
|
550
|
+
} catch {
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function verifyKeyFingerprint(publicKeyB64, expectedFingerprint) {
|
|
555
|
+
try {
|
|
556
|
+
const publicKeyDER = Buffer.from(publicKeyB64, "base64");
|
|
557
|
+
const fingerprint = crypto.createHash("sha256").update(publicKeyDER).digest("hex");
|
|
558
|
+
return fingerprint === expectedFingerprint;
|
|
559
|
+
} catch (error) {
|
|
560
|
+
console.error("Failed to verify key fingerprint:", error);
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
var JWT_SECRET, JWT_EXPIRES_IN;
|
|
565
|
+
var init_jwt = __esm({
|
|
566
|
+
"src/server/helpers/jwt.ts"() {
|
|
567
|
+
"use strict";
|
|
568
|
+
JWT_SECRET = process.env.SPFN_AUTH_JWT_SECRET || // New prefixed version (recommended)
|
|
569
|
+
process.env.JWT_SECRET || // Legacy fallback
|
|
570
|
+
"dev-secret-key-change-in-production";
|
|
571
|
+
JWT_EXPIRES_IN = process.env.SPFN_AUTH_JWT_EXPIRES_IN || // New prefixed version (recommended)
|
|
572
|
+
process.env.JWT_EXPIRES_IN || // Legacy fallback
|
|
573
|
+
"7d";
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
|
|
488
577
|
// src/server/services/role.service.ts
|
|
489
578
|
var role_service_exports = {};
|
|
490
579
|
__export(role_service_exports, {
|
|
@@ -710,6 +799,14 @@ var BUILTIN_PERMISSIONS = {
|
|
|
710
799
|
isSystem: true,
|
|
711
800
|
isBuiltin: true
|
|
712
801
|
},
|
|
802
|
+
USER_INVITE: {
|
|
803
|
+
name: "user:invite",
|
|
804
|
+
displayName: "Invite Users",
|
|
805
|
+
description: "Create and send user invitations",
|
|
806
|
+
category: "user",
|
|
807
|
+
isSystem: true,
|
|
808
|
+
isBuiltin: true
|
|
809
|
+
},
|
|
713
810
|
// RBAC management (superadmin functions)
|
|
714
811
|
RBAC_ROLE_MANAGE: {
|
|
715
812
|
name: "rbac:role:manage",
|
|
@@ -734,6 +831,7 @@ var BUILTIN_ROLE_PERMISSIONS = {
|
|
|
734
831
|
"user:read",
|
|
735
832
|
"user:write",
|
|
736
833
|
"user:delete",
|
|
834
|
+
"user:invite",
|
|
737
835
|
"rbac:role:manage",
|
|
738
836
|
"rbac:permission:manage"
|
|
739
837
|
],
|
|
@@ -741,113 +839,14 @@ var BUILTIN_ROLE_PERMISSIONS = {
|
|
|
741
839
|
"auth:self:manage",
|
|
742
840
|
"user:read",
|
|
743
841
|
"user:write",
|
|
744
|
-
"user:delete"
|
|
842
|
+
"user:delete",
|
|
843
|
+
"user:invite"
|
|
745
844
|
],
|
|
746
845
|
user: [
|
|
747
846
|
"auth:self:manage"
|
|
748
847
|
]
|
|
749
848
|
};
|
|
750
849
|
|
|
751
|
-
// src/server/rbac/presets.ts
|
|
752
|
-
var PRESET_ROLES = {
|
|
753
|
-
MODERATOR: {
|
|
754
|
-
name: "moderator",
|
|
755
|
-
displayName: "Moderator",
|
|
756
|
-
description: "Content moderation and community management",
|
|
757
|
-
priority: 50,
|
|
758
|
-
isSystem: true
|
|
759
|
-
},
|
|
760
|
-
EDITOR: {
|
|
761
|
-
name: "editor",
|
|
762
|
-
displayName: "Editor",
|
|
763
|
-
description: "Content creation and editing",
|
|
764
|
-
priority: 30,
|
|
765
|
-
isSystem: true
|
|
766
|
-
},
|
|
767
|
-
VIEWER: {
|
|
768
|
-
name: "viewer",
|
|
769
|
-
displayName: "Viewer",
|
|
770
|
-
description: "Read-only access to content",
|
|
771
|
-
priority: 5,
|
|
772
|
-
isSystem: true
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
var PRESET_PERMISSIONS = {
|
|
776
|
-
// Content management
|
|
777
|
-
CONTENT_READ: {
|
|
778
|
-
name: "content:read",
|
|
779
|
-
displayName: "Read Content",
|
|
780
|
-
description: "View all content including drafts",
|
|
781
|
-
category: "content",
|
|
782
|
-
isSystem: true
|
|
783
|
-
},
|
|
784
|
-
CONTENT_WRITE: {
|
|
785
|
-
name: "content:write",
|
|
786
|
-
displayName: "Write Content",
|
|
787
|
-
description: "Create and edit content",
|
|
788
|
-
category: "content",
|
|
789
|
-
isSystem: true
|
|
790
|
-
},
|
|
791
|
-
CONTENT_DELETE: {
|
|
792
|
-
name: "content:delete",
|
|
793
|
-
displayName: "Delete Content",
|
|
794
|
-
description: "Delete any content",
|
|
795
|
-
category: "content",
|
|
796
|
-
isSystem: true
|
|
797
|
-
},
|
|
798
|
-
CONTENT_PUBLISH: {
|
|
799
|
-
name: "content:publish",
|
|
800
|
-
displayName: "Publish Content",
|
|
801
|
-
description: "Publish content to make it public",
|
|
802
|
-
category: "content",
|
|
803
|
-
isSystem: true
|
|
804
|
-
},
|
|
805
|
-
// Moderation
|
|
806
|
-
COMMENT_MODERATE: {
|
|
807
|
-
name: "comment:moderate",
|
|
808
|
-
displayName: "Moderate Comments",
|
|
809
|
-
description: "Review and delete inappropriate comments",
|
|
810
|
-
category: "moderation",
|
|
811
|
-
isSystem: true
|
|
812
|
-
},
|
|
813
|
-
// System
|
|
814
|
-
SYSTEM_CONFIG: {
|
|
815
|
-
name: "system:config",
|
|
816
|
-
displayName: "System Configuration",
|
|
817
|
-
description: "Configure application settings",
|
|
818
|
-
category: "system",
|
|
819
|
-
isSystem: true
|
|
820
|
-
},
|
|
821
|
-
// Analytics
|
|
822
|
-
ANALYTICS_VIEW: {
|
|
823
|
-
name: "analytics:view",
|
|
824
|
-
displayName: "View Analytics",
|
|
825
|
-
description: "Access analytics dashboard and reports",
|
|
826
|
-
category: "analytics",
|
|
827
|
-
isSystem: true
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
|
-
var PRESET_ROLE_PERMISSIONS = {
|
|
831
|
-
moderator: [
|
|
832
|
-
"auth:self:manage",
|
|
833
|
-
"user:read",
|
|
834
|
-
"content:read",
|
|
835
|
-
"content:write",
|
|
836
|
-
"content:delete",
|
|
837
|
-
"comment:moderate"
|
|
838
|
-
],
|
|
839
|
-
editor: [
|
|
840
|
-
"auth:self:manage",
|
|
841
|
-
"content:read",
|
|
842
|
-
"content:write",
|
|
843
|
-
"content:publish"
|
|
844
|
-
],
|
|
845
|
-
viewer: [
|
|
846
|
-
"auth:self:manage",
|
|
847
|
-
"content:read"
|
|
848
|
-
]
|
|
849
|
-
};
|
|
850
|
-
|
|
851
850
|
// src/server/services/auth.service.ts
|
|
852
851
|
init_entities();
|
|
853
852
|
import { findOne as findOne2, create as create3 } from "@spfn/core/db";
|
|
@@ -899,68 +898,8 @@ function validatePasswordStrength(password) {
|
|
|
899
898
|
};
|
|
900
899
|
}
|
|
901
900
|
|
|
902
|
-
// src/server/helpers/
|
|
903
|
-
|
|
904
|
-
import crypto from "crypto";
|
|
905
|
-
var JWT_SECRET = process.env.SPFN_AUTH_JWT_SECRET || // New prefixed version (recommended)
|
|
906
|
-
process.env.JWT_SECRET || // Legacy fallback
|
|
907
|
-
"dev-secret-key-change-in-production";
|
|
908
|
-
var JWT_EXPIRES_IN = process.env.SPFN_AUTH_JWT_EXPIRES_IN || // New prefixed version (recommended)
|
|
909
|
-
process.env.JWT_EXPIRES_IN || // Legacy fallback
|
|
910
|
-
"7d";
|
|
911
|
-
function generateToken(payload) {
|
|
912
|
-
return jwt.sign(payload, JWT_SECRET, {
|
|
913
|
-
expiresIn: JWT_EXPIRES_IN
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
|
-
function verifyToken(token) {
|
|
917
|
-
return jwt.verify(token, JWT_SECRET);
|
|
918
|
-
}
|
|
919
|
-
function verifyClientToken(token, publicKeyB64, algorithm) {
|
|
920
|
-
try {
|
|
921
|
-
const publicKeyDER = Buffer.from(publicKeyB64, "base64");
|
|
922
|
-
const publicKeyObject = crypto.createPublicKey({
|
|
923
|
-
key: publicKeyDER,
|
|
924
|
-
format: "der",
|
|
925
|
-
type: "spki"
|
|
926
|
-
});
|
|
927
|
-
const decoded = jwt.verify(token, publicKeyObject, {
|
|
928
|
-
algorithms: [algorithm],
|
|
929
|
-
// Prevent algorithm confusion attacks
|
|
930
|
-
issuer: "spfn-client"
|
|
931
|
-
// Validate token issuer
|
|
932
|
-
});
|
|
933
|
-
if (typeof decoded === "string") {
|
|
934
|
-
throw new Error("Invalid token format: expected object payload");
|
|
935
|
-
}
|
|
936
|
-
return decoded;
|
|
937
|
-
} catch (error) {
|
|
938
|
-
if (error instanceof jwt.TokenExpiredError) {
|
|
939
|
-
throw new Error("Token has expired");
|
|
940
|
-
}
|
|
941
|
-
if (error instanceof jwt.JsonWebTokenError) {
|
|
942
|
-
throw new Error("Invalid token signature");
|
|
943
|
-
}
|
|
944
|
-
throw new Error(`Token verification failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
function decodeToken(token) {
|
|
948
|
-
try {
|
|
949
|
-
return jwt.decode(token);
|
|
950
|
-
} catch {
|
|
951
|
-
return null;
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
function verifyKeyFingerprint(publicKeyB64, expectedFingerprint) {
|
|
955
|
-
try {
|
|
956
|
-
const publicKeyDER = Buffer.from(publicKeyB64, "base64");
|
|
957
|
-
const fingerprint = crypto.createHash("sha256").update(publicKeyDER).digest("hex");
|
|
958
|
-
return fingerprint === expectedFingerprint;
|
|
959
|
-
} catch (error) {
|
|
960
|
-
console.error("Failed to verify key fingerprint:", error);
|
|
961
|
-
return false;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
901
|
+
// src/server/helpers/index.ts
|
|
902
|
+
init_jwt();
|
|
964
903
|
|
|
965
904
|
// src/server/helpers/verification.ts
|
|
966
905
|
init_verification_codes();
|
|
@@ -1119,16 +1058,14 @@ var KeyExpiredError = class extends UnauthorizedError {
|
|
|
1119
1058
|
};
|
|
1120
1059
|
var AccountDisabledError = class extends ForbiddenError {
|
|
1121
1060
|
constructor(status = "disabled") {
|
|
1122
|
-
super(`Account is ${status}
|
|
1061
|
+
super(`Account is ${status}`, { details: { status } });
|
|
1123
1062
|
this.name = "AccountDisabledError";
|
|
1124
|
-
this.details = { status };
|
|
1125
1063
|
}
|
|
1126
1064
|
};
|
|
1127
1065
|
var AccountAlreadyExistsError = class extends ConflictError {
|
|
1128
1066
|
constructor(identifier, identifierType) {
|
|
1129
|
-
super("Account already exists");
|
|
1067
|
+
super("Account already exists", { details: { identifier, identifierType } });
|
|
1130
1068
|
this.name = "AccountAlreadyExistsError";
|
|
1131
|
-
this.details = { identifier, identifierType };
|
|
1132
1069
|
}
|
|
1133
1070
|
};
|
|
1134
1071
|
var InvalidVerificationCodeError = class extends ValidationError {
|
|
@@ -1151,9 +1088,8 @@ var InvalidKeyFingerprintError = class extends ValidationError {
|
|
|
1151
1088
|
};
|
|
1152
1089
|
var VerificationTokenPurposeMismatchError = class extends ValidationError {
|
|
1153
1090
|
constructor(expected, actual) {
|
|
1154
|
-
super(`Verification token is for ${actual}, but ${expected} was expected
|
|
1091
|
+
super(`Verification token is for ${actual}, but ${expected} was expected`, { details: { expected, actual } });
|
|
1155
1092
|
this.name = "VerificationTokenPurposeMismatchError";
|
|
1156
|
-
this.details = { expected, actual };
|
|
1157
1093
|
}
|
|
1158
1094
|
};
|
|
1159
1095
|
var VerificationTokenTargetMismatchError = class extends ValidationError {
|
|
@@ -1165,6 +1101,7 @@ var VerificationTokenTargetMismatchError = class extends ValidationError {
|
|
|
1165
1101
|
|
|
1166
1102
|
// src/server/services/key.service.ts
|
|
1167
1103
|
init_entities();
|
|
1104
|
+
init_jwt();
|
|
1168
1105
|
import { create as create2, getDatabase as getDatabase2 } from "@spfn/core/db";
|
|
1169
1106
|
import { eq as eq2, and as and2 } from "drizzle-orm";
|
|
1170
1107
|
function getKeyExpiryDate() {
|
|
@@ -1354,6 +1291,8 @@ async function loginService(params) {
|
|
|
1354
1291
|
if (!user || !user.passwordHash) {
|
|
1355
1292
|
throw new InvalidCredentialsError();
|
|
1356
1293
|
}
|
|
1294
|
+
console.log("user", user);
|
|
1295
|
+
console.log("\uD328\uC2A4\uC6CC\uB4DC: ", password);
|
|
1357
1296
|
const isValid = await verifyPassword(password, user.passwordHash);
|
|
1358
1297
|
if (!isValid) {
|
|
1359
1298
|
throw new InvalidCredentialsError();
|
|
@@ -1453,52 +1392,105 @@ async function verifyCodeService(params) {
|
|
|
1453
1392
|
};
|
|
1454
1393
|
}
|
|
1455
1394
|
|
|
1456
|
-
// src/server/services/
|
|
1395
|
+
// src/server/services/me.service.ts
|
|
1457
1396
|
init_entities();
|
|
1458
1397
|
import { getDatabase as getDatabase4 } from "@spfn/core/db";
|
|
1459
|
-
import { eq as eq4, and as and4
|
|
1460
|
-
async function
|
|
1398
|
+
import { eq as eq4, and as and4 } from "drizzle-orm";
|
|
1399
|
+
async function getMeService(userId) {
|
|
1461
1400
|
const db = getDatabase4();
|
|
1462
1401
|
if (!db) {
|
|
1463
|
-
throw new Error("[Auth] Database not initialized
|
|
1402
|
+
throw new Error("[Auth] Database not initialized");
|
|
1464
1403
|
}
|
|
1465
|
-
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1404
|
+
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
1405
|
+
const [userWithRole] = await db.select({
|
|
1406
|
+
userId: users.id,
|
|
1407
|
+
email: users.email,
|
|
1408
|
+
phone: users.phone,
|
|
1409
|
+
roleId: roles.id,
|
|
1410
|
+
roleName: roles.name,
|
|
1411
|
+
roleDisplayName: roles.displayName,
|
|
1412
|
+
rolePriority: roles.priority
|
|
1413
|
+
}).from(users).innerJoin(roles, eq4(users.roleId, roles.id)).where(eq4(users.id, userIdNum)).limit(1);
|
|
1414
|
+
if (!userWithRole) {
|
|
1415
|
+
throw new Error("[Auth] User not found");
|
|
1416
|
+
}
|
|
1417
|
+
const rolePerms = await db.select({
|
|
1418
|
+
id: permissions.id,
|
|
1419
|
+
name: permissions.name,
|
|
1420
|
+
displayName: permissions.displayName,
|
|
1421
|
+
category: permissions.category
|
|
1422
|
+
}).from(rolePermissions).innerJoin(permissions, eq4(rolePermissions.permissionId, permissions.id)).where(
|
|
1423
|
+
and4(
|
|
1424
|
+
eq4(rolePermissions.roleId, userWithRole.roleId),
|
|
1425
|
+
eq4(permissions.isActive, true)
|
|
1426
|
+
)
|
|
1427
|
+
);
|
|
1428
|
+
return {
|
|
1429
|
+
userId: userWithRole.userId.toString(),
|
|
1430
|
+
email: userWithRole.email ?? void 0,
|
|
1431
|
+
phone: userWithRole.phone ?? void 0,
|
|
1432
|
+
role: {
|
|
1433
|
+
id: userWithRole.roleId,
|
|
1434
|
+
name: userWithRole.roleName,
|
|
1435
|
+
displayName: userWithRole.roleDisplayName,
|
|
1436
|
+
priority: userWithRole.rolePriority
|
|
1437
|
+
},
|
|
1438
|
+
permissions: rolePerms.map((perm) => ({
|
|
1439
|
+
id: perm.id,
|
|
1440
|
+
name: perm.name,
|
|
1441
|
+
displayName: perm.displayName,
|
|
1442
|
+
category: perm.category ?? void 0
|
|
1443
|
+
}))
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// src/server/services/rbac.service.ts
|
|
1448
|
+
init_entities();
|
|
1449
|
+
import { getDatabase as getDatabase5 } from "@spfn/core/db";
|
|
1450
|
+
import { logger } from "@spfn/core/logger";
|
|
1451
|
+
import { eq as eq5, and as and5, inArray } from "drizzle-orm";
|
|
1452
|
+
|
|
1453
|
+
// src/lib/config.ts
|
|
1454
|
+
var globalConfig = {
|
|
1455
|
+
sessionTtl: "7d"
|
|
1456
|
+
// Default: 7 days
|
|
1457
|
+
};
|
|
1458
|
+
function configureAuth(config) {
|
|
1459
|
+
globalConfig = {
|
|
1460
|
+
...globalConfig,
|
|
1461
|
+
...config
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// src/server/services/rbac.service.ts
|
|
1466
|
+
var authLogger = logger.child("@spfn/auth");
|
|
1467
|
+
async function initializeAuth(options = {}) {
|
|
1468
|
+
const db = getDatabase5();
|
|
1469
|
+
if (!db) {
|
|
1470
|
+
throw new Error("[Auth] Database not initialized. Call initDatabase() first.");
|
|
1475
1471
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1472
|
+
authLogger.info("\u{1F510} Initializing RBAC system...");
|
|
1473
|
+
if (options.sessionTtl !== void 0) {
|
|
1474
|
+
configureAuth({
|
|
1475
|
+
sessionTtl: options.sessionTtl
|
|
1476
|
+
});
|
|
1477
|
+
authLogger.info(`\u23F1\uFE0F Session TTL: ${options.sessionTtl}`);
|
|
1478
1478
|
}
|
|
1479
|
+
const allRoles = [
|
|
1480
|
+
...Object.values(BUILTIN_ROLES),
|
|
1481
|
+
...options.roles || []
|
|
1482
|
+
];
|
|
1479
1483
|
for (const roleConfig of allRoles) {
|
|
1480
1484
|
await upsertRole(roleConfig);
|
|
1481
1485
|
}
|
|
1482
|
-
const allPermissions = [
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
for (const presetKey of options.presetPermissions) {
|
|
1487
|
-
if (PRESET_PERMISSIONS[presetKey]) {
|
|
1488
|
-
allPermissions.push(PRESET_PERMISSIONS[presetKey]);
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
if (options.permissions) {
|
|
1493
|
-
allPermissions.push(...options.permissions);
|
|
1494
|
-
}
|
|
1486
|
+
const allPermissions = [
|
|
1487
|
+
...Object.values(BUILTIN_PERMISSIONS),
|
|
1488
|
+
...options.permissions || []
|
|
1489
|
+
];
|
|
1495
1490
|
for (const permConfig of allPermissions) {
|
|
1496
1491
|
await upsertPermission(permConfig);
|
|
1497
1492
|
}
|
|
1498
1493
|
const allMappings = { ...BUILTIN_ROLE_PERMISSIONS };
|
|
1499
|
-
if (options.usePresets) {
|
|
1500
|
-
Object.assign(allMappings, PRESET_ROLE_PERMISSIONS);
|
|
1501
|
-
}
|
|
1502
1494
|
if (options.rolePermissions) {
|
|
1503
1495
|
for (const [roleName, permNames] of Object.entries(options.rolePermissions)) {
|
|
1504
1496
|
if (allMappings[roleName]) {
|
|
@@ -1513,13 +1505,13 @@ async function initializeAuth(options = {}) {
|
|
|
1513
1505
|
for (const [roleName, permNames] of Object.entries(allMappings)) {
|
|
1514
1506
|
await assignPermissionsToRole(roleName, permNames);
|
|
1515
1507
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1508
|
+
authLogger.info("\u2705 RBAC initialization complete");
|
|
1509
|
+
authLogger.info(`\u{1F4CA} Roles: ${allRoles.length}, Permissions: ${allPermissions.length}`);
|
|
1510
|
+
authLogger.info("\u{1F512} Built-in roles: user, admin, superadmin");
|
|
1519
1511
|
}
|
|
1520
1512
|
async function upsertRole(config) {
|
|
1521
|
-
const db =
|
|
1522
|
-
const existing = await db.select().from(roles).where(
|
|
1513
|
+
const db = getDatabase5();
|
|
1514
|
+
const existing = await db.select().from(roles).where(eq5(roles.name, config.name)).limit(1);
|
|
1523
1515
|
if (existing.length === 0) {
|
|
1524
1516
|
await db.insert(roles).values({
|
|
1525
1517
|
name: config.name,
|
|
@@ -1529,7 +1521,7 @@ async function upsertRole(config) {
|
|
|
1529
1521
|
isSystem: config.isSystem ?? false,
|
|
1530
1522
|
isBuiltin: config.isBuiltin ?? false
|
|
1531
1523
|
});
|
|
1532
|
-
|
|
1524
|
+
authLogger.info(` \u2705 Created role: ${config.name}`);
|
|
1533
1525
|
} else {
|
|
1534
1526
|
const updateData = {
|
|
1535
1527
|
displayName: config.displayName,
|
|
@@ -1538,12 +1530,12 @@ async function upsertRole(config) {
|
|
|
1538
1530
|
if (!existing[0].isBuiltin) {
|
|
1539
1531
|
updateData.priority = config.priority ?? existing[0].priority;
|
|
1540
1532
|
}
|
|
1541
|
-
await db.update(roles).set(updateData).where(
|
|
1533
|
+
await db.update(roles).set(updateData).where(eq5(roles.id, existing[0].id));
|
|
1542
1534
|
}
|
|
1543
1535
|
}
|
|
1544
1536
|
async function upsertPermission(config) {
|
|
1545
|
-
const db =
|
|
1546
|
-
const existing = await db.select().from(permissions).where(
|
|
1537
|
+
const db = getDatabase5();
|
|
1538
|
+
const existing = await db.select().from(permissions).where(eq5(permissions.name, config.name)).limit(1);
|
|
1547
1539
|
if (existing.length === 0) {
|
|
1548
1540
|
await db.insert(permissions).values({
|
|
1549
1541
|
name: config.name,
|
|
@@ -1553,32 +1545,32 @@ async function upsertPermission(config) {
|
|
|
1553
1545
|
isSystem: config.isSystem ?? false,
|
|
1554
1546
|
isBuiltin: config.isBuiltin ?? false
|
|
1555
1547
|
});
|
|
1556
|
-
|
|
1548
|
+
authLogger.info(` \u2705 Created permission: ${config.name}`);
|
|
1557
1549
|
} else {
|
|
1558
1550
|
await db.update(permissions).set({
|
|
1559
1551
|
displayName: config.displayName,
|
|
1560
1552
|
description: config.description,
|
|
1561
1553
|
category: config.category
|
|
1562
|
-
}).where(
|
|
1554
|
+
}).where(eq5(permissions.id, existing[0].id));
|
|
1563
1555
|
}
|
|
1564
1556
|
}
|
|
1565
1557
|
async function assignPermissionsToRole(roleName, permissionNames) {
|
|
1566
|
-
const db =
|
|
1567
|
-
const [role] = await db.select().from(roles).where(
|
|
1558
|
+
const db = getDatabase5();
|
|
1559
|
+
const [role] = await db.select().from(roles).where(eq5(roles.name, roleName)).limit(1);
|
|
1568
1560
|
if (!role) {
|
|
1569
|
-
|
|
1561
|
+
authLogger.warn(` \u26A0\uFE0F Role not found: ${roleName}, skipping permission assignment`);
|
|
1570
1562
|
return;
|
|
1571
1563
|
}
|
|
1572
1564
|
const perms = await db.select().from(permissions).where(inArray(permissions.name, permissionNames));
|
|
1573
1565
|
if (perms.length === 0) {
|
|
1574
|
-
|
|
1566
|
+
authLogger.warn(` \u26A0\uFE0F No permissions found for role: ${roleName}`);
|
|
1575
1567
|
return;
|
|
1576
1568
|
}
|
|
1577
1569
|
for (const perm of perms) {
|
|
1578
1570
|
const existing = await db.select().from(rolePermissions).where(
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1571
|
+
and5(
|
|
1572
|
+
eq5(rolePermissions.roleId, role.id),
|
|
1573
|
+
eq5(rolePermissions.permissionId, perm.id)
|
|
1582
1574
|
)
|
|
1583
1575
|
).limit(1);
|
|
1584
1576
|
if (existing.length === 0) {
|
|
@@ -1592,23 +1584,23 @@ async function assignPermissionsToRole(roleName, permissionNames) {
|
|
|
1592
1584
|
|
|
1593
1585
|
// src/server/services/permission.service.ts
|
|
1594
1586
|
init_entities();
|
|
1595
|
-
import { getDatabase as
|
|
1596
|
-
import { eq as
|
|
1587
|
+
import { getDatabase as getDatabase6 } from "@spfn/core/db";
|
|
1588
|
+
import { eq as eq6, and as and6 } from "drizzle-orm";
|
|
1597
1589
|
async function getUserPermissions(userId) {
|
|
1598
|
-
const db =
|
|
1590
|
+
const db = getDatabase6();
|
|
1599
1591
|
if (!db) {
|
|
1600
1592
|
throw new Error("[Auth] Database not initialized");
|
|
1601
1593
|
}
|
|
1602
1594
|
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
1603
|
-
const [user] = await db.select({ roleId: users.roleId }).from(users).where(
|
|
1595
|
+
const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
|
|
1604
1596
|
if (!user || !user.roleId) {
|
|
1605
1597
|
return [];
|
|
1606
1598
|
}
|
|
1607
1599
|
const permSet = /* @__PURE__ */ new Set();
|
|
1608
|
-
const rolePerms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions,
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1600
|
+
const rolePerms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions, eq6(rolePermissions.permissionId, permissions.id)).where(
|
|
1601
|
+
and6(
|
|
1602
|
+
eq6(rolePermissions.roleId, user.roleId),
|
|
1603
|
+
eq6(permissions.isActive, true)
|
|
1612
1604
|
)
|
|
1613
1605
|
);
|
|
1614
1606
|
for (const perm of rolePerms) {
|
|
@@ -1618,7 +1610,7 @@ async function getUserPermissions(userId) {
|
|
|
1618
1610
|
name: permissions.name,
|
|
1619
1611
|
granted: userPermissions.granted,
|
|
1620
1612
|
expiresAt: userPermissions.expiresAt
|
|
1621
|
-
}).from(userPermissions).innerJoin(permissions,
|
|
1613
|
+
}).from(userPermissions).innerJoin(permissions, eq6(userPermissions.permissionId, permissions.id)).where(eq6(userPermissions.userId, userIdNum));
|
|
1622
1614
|
const now = /* @__PURE__ */ new Date();
|
|
1623
1615
|
for (const userPerm of userPerms) {
|
|
1624
1616
|
if (userPerm.expiresAt && userPerm.expiresAt < now) {
|
|
@@ -1645,16 +1637,16 @@ async function hasAllPermissions(userId, permissionNames) {
|
|
|
1645
1637
|
return permissionNames.every((p) => perms.includes(p));
|
|
1646
1638
|
}
|
|
1647
1639
|
async function hasRole(userId, roleName) {
|
|
1648
|
-
const db =
|
|
1640
|
+
const db = getDatabase6();
|
|
1649
1641
|
if (!db) {
|
|
1650
1642
|
throw new Error("[Auth] Database not initialized");
|
|
1651
1643
|
}
|
|
1652
1644
|
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
1653
|
-
const [user] = await db.select({ roleId: users.roleId }).from(users).where(
|
|
1645
|
+
const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
|
|
1654
1646
|
if (!user || !user.roleId) {
|
|
1655
1647
|
return false;
|
|
1656
1648
|
}
|
|
1657
|
-
const [role] = await db.select({ name: roles.name }).from(roles).where(
|
|
1649
|
+
const [role] = await db.select({ name: roles.name }).from(roles).where(eq6(roles.id, user.roleId)).limit(1);
|
|
1658
1650
|
return role?.name === roleName;
|
|
1659
1651
|
}
|
|
1660
1652
|
async function hasAnyRole(userId, roleNames) {
|
|
@@ -1671,8 +1663,8 @@ init_role_service();
|
|
|
1671
1663
|
|
|
1672
1664
|
// src/server/services/invitation.service.ts
|
|
1673
1665
|
init_entities();
|
|
1674
|
-
import { getDatabase as
|
|
1675
|
-
import { eq as
|
|
1666
|
+
import { getDatabase as getDatabase7 } from "@spfn/core/db";
|
|
1667
|
+
import { eq as eq7, and as and7, lt, desc, sql as sql2 } from "drizzle-orm";
|
|
1676
1668
|
import crypto2 from "crypto";
|
|
1677
1669
|
function generateInvitationToken() {
|
|
1678
1670
|
return crypto2.randomUUID();
|
|
@@ -1683,7 +1675,7 @@ function calculateExpiresAt(days = 7) {
|
|
|
1683
1675
|
return expiresAt;
|
|
1684
1676
|
}
|
|
1685
1677
|
async function createInvitation(params) {
|
|
1686
|
-
const db =
|
|
1678
|
+
const db = getDatabase7();
|
|
1687
1679
|
if (!db) {
|
|
1688
1680
|
throw new Error("[Auth] Database not initialized");
|
|
1689
1681
|
}
|
|
@@ -1692,24 +1684,24 @@ async function createInvitation(params) {
|
|
|
1692
1684
|
if (!emailRegex.test(email)) {
|
|
1693
1685
|
throw new Error("Invalid email format");
|
|
1694
1686
|
}
|
|
1695
|
-
const existingUser = await db.select().from(users).where(
|
|
1687
|
+
const existingUser = await db.select().from(users).where(eq7(users.email, email)).limit(1);
|
|
1696
1688
|
if (existingUser.length > 0) {
|
|
1697
1689
|
throw new Error("User with this email already exists");
|
|
1698
1690
|
}
|
|
1699
1691
|
const existingInvitation = await db.select().from(invitations).where(
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1692
|
+
and7(
|
|
1693
|
+
eq7(invitations.email, email),
|
|
1694
|
+
eq7(invitations.status, "pending")
|
|
1703
1695
|
)
|
|
1704
1696
|
).limit(1);
|
|
1705
1697
|
if (existingInvitation.length > 0) {
|
|
1706
1698
|
throw new Error("Pending invitation already exists for this email");
|
|
1707
1699
|
}
|
|
1708
|
-
const role = await db.select().from(roles).where(
|
|
1700
|
+
const role = await db.select().from(roles).where(eq7(roles.id, roleId)).limit(1);
|
|
1709
1701
|
if (role.length === 0) {
|
|
1710
1702
|
throw new Error(`Role with id ${roleId} not found`);
|
|
1711
1703
|
}
|
|
1712
|
-
const inviter = await db.select().from(users).where(
|
|
1704
|
+
const inviter = await db.select().from(users).where(eq7(users.id, invitedBy)).limit(1);
|
|
1713
1705
|
if (inviter.length === 0) {
|
|
1714
1706
|
throw new Error(`User with id ${invitedBy} not found`);
|
|
1715
1707
|
}
|
|
@@ -1728,15 +1720,15 @@ async function createInvitation(params) {
|
|
|
1728
1720
|
return invitation;
|
|
1729
1721
|
}
|
|
1730
1722
|
async function getInvitationByToken(token) {
|
|
1731
|
-
const db =
|
|
1723
|
+
const db = getDatabase7();
|
|
1732
1724
|
if (!db) {
|
|
1733
1725
|
throw new Error("[Auth] Database not initialized");
|
|
1734
1726
|
}
|
|
1735
|
-
const result = await db.select().from(invitations).where(
|
|
1727
|
+
const result = await db.select().from(invitations).where(eq7(invitations.token, token)).limit(1);
|
|
1736
1728
|
return result[0] || null;
|
|
1737
1729
|
}
|
|
1738
1730
|
async function getInvitationWithDetails(token) {
|
|
1739
|
-
const db =
|
|
1731
|
+
const db = getDatabase7();
|
|
1740
1732
|
if (!db) {
|
|
1741
1733
|
throw new Error("[Auth] Database not initialized");
|
|
1742
1734
|
}
|
|
@@ -1762,7 +1754,7 @@ async function getInvitationWithDetails(token) {
|
|
|
1762
1754
|
id: users.id,
|
|
1763
1755
|
email: users.email
|
|
1764
1756
|
}
|
|
1765
|
-
}).from(invitations).innerJoin(roles,
|
|
1757
|
+
}).from(invitations).innerJoin(roles, eq7(invitations.roleId, roles.id)).innerJoin(users, eq7(invitations.invitedBy, users.id)).where(eq7(invitations.token, token)).limit(1);
|
|
1766
1758
|
return result[0] || null;
|
|
1767
1759
|
}
|
|
1768
1760
|
async function validateInvitation(token) {
|
|
@@ -1785,7 +1777,7 @@ async function validateInvitation(token) {
|
|
|
1785
1777
|
return { valid: true, invitation };
|
|
1786
1778
|
}
|
|
1787
1779
|
async function acceptInvitation(params) {
|
|
1788
|
-
const db =
|
|
1780
|
+
const db = getDatabase7();
|
|
1789
1781
|
if (!db) {
|
|
1790
1782
|
throw new Error("[Auth] Database not initialized");
|
|
1791
1783
|
}
|
|
@@ -1795,7 +1787,7 @@ async function acceptInvitation(params) {
|
|
|
1795
1787
|
throw new Error(validation.error || "Invalid invitation");
|
|
1796
1788
|
}
|
|
1797
1789
|
const invitation = validation.invitation;
|
|
1798
|
-
const role = await db.select().from(roles).where(
|
|
1790
|
+
const role = await db.select().from(roles).where(eq7(roles.id, invitation.roleId)).limit(1);
|
|
1799
1791
|
if (role.length === 0) {
|
|
1800
1792
|
throw new Error("Role not found");
|
|
1801
1793
|
}
|
|
@@ -1825,7 +1817,7 @@ async function acceptInvitation(params) {
|
|
|
1825
1817
|
status: "accepted",
|
|
1826
1818
|
acceptedAt: /* @__PURE__ */ new Date(),
|
|
1827
1819
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1828
|
-
}).where(
|
|
1820
|
+
}).where(eq7(invitations.id, invitation.id));
|
|
1829
1821
|
return { newUser, role: role[0] };
|
|
1830
1822
|
});
|
|
1831
1823
|
console.log(`[Auth] \u2705 Invitation accepted: ${invitation.email} as ${result.role.name}`);
|
|
@@ -1836,7 +1828,7 @@ async function acceptInvitation(params) {
|
|
|
1836
1828
|
};
|
|
1837
1829
|
}
|
|
1838
1830
|
async function listInvitations(params) {
|
|
1839
|
-
const db =
|
|
1831
|
+
const db = getDatabase7();
|
|
1840
1832
|
if (!db) {
|
|
1841
1833
|
throw new Error("[Auth] Database not initialized");
|
|
1842
1834
|
}
|
|
@@ -1844,12 +1836,12 @@ async function listInvitations(params) {
|
|
|
1844
1836
|
const offset = (page - 1) * limit;
|
|
1845
1837
|
const conditions = [];
|
|
1846
1838
|
if (status) {
|
|
1847
|
-
conditions.push(
|
|
1839
|
+
conditions.push(eq7(invitations.status, status));
|
|
1848
1840
|
}
|
|
1849
1841
|
if (invitedBy) {
|
|
1850
|
-
conditions.push(
|
|
1842
|
+
conditions.push(eq7(invitations.invitedBy, invitedBy));
|
|
1851
1843
|
}
|
|
1852
|
-
const whereClause = conditions.length > 0 ?
|
|
1844
|
+
const whereClause = conditions.length > 0 ? and7(...conditions) : void 0;
|
|
1853
1845
|
const countResult = await db.select({ count: sql2`count(*)` }).from(invitations).where(whereClause);
|
|
1854
1846
|
const total = Number(countResult[0]?.count || 0);
|
|
1855
1847
|
const results = await db.select({
|
|
@@ -1874,7 +1866,7 @@ async function listInvitations(params) {
|
|
|
1874
1866
|
id: users.id,
|
|
1875
1867
|
email: users.email
|
|
1876
1868
|
}
|
|
1877
|
-
}).from(invitations).innerJoin(roles,
|
|
1869
|
+
}).from(invitations).innerJoin(roles, eq7(invitations.roleId, roles.id)).innerJoin(users, eq7(invitations.invitedBy, users.id)).where(whereClause).orderBy(desc(invitations.createdAt)).limit(limit).offset(offset);
|
|
1878
1870
|
return {
|
|
1879
1871
|
invitations: results,
|
|
1880
1872
|
total,
|
|
@@ -1884,11 +1876,11 @@ async function listInvitations(params) {
|
|
|
1884
1876
|
};
|
|
1885
1877
|
}
|
|
1886
1878
|
async function cancelInvitation(id10, cancelledBy, reason) {
|
|
1887
|
-
const db =
|
|
1879
|
+
const db = getDatabase7();
|
|
1888
1880
|
if (!db) {
|
|
1889
1881
|
throw new Error("[Auth] Database not initialized");
|
|
1890
1882
|
}
|
|
1891
|
-
const invitation = await db.select().from(invitations).where(
|
|
1883
|
+
const invitation = await db.select().from(invitations).where(eq7(invitations.id, id10)).limit(1);
|
|
1892
1884
|
if (invitation.length === 0) {
|
|
1893
1885
|
throw new Error("Invitation not found");
|
|
1894
1886
|
}
|
|
@@ -1900,26 +1892,26 @@ async function cancelInvitation(id10, cancelledBy, reason) {
|
|
|
1900
1892
|
cancelledAt: /* @__PURE__ */ new Date(),
|
|
1901
1893
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
1902
1894
|
metadata: invitation[0].metadata ? { ...invitation[0].metadata, cancelReason: reason, cancelledBy } : { cancelReason: reason, cancelledBy }
|
|
1903
|
-
}).where(
|
|
1895
|
+
}).where(eq7(invitations.id, id10));
|
|
1904
1896
|
console.log(`[Auth] \u26A0\uFE0F Invitation cancelled: ${invitation[0].email} (reason: ${reason || "none"})`);
|
|
1905
1897
|
}
|
|
1906
1898
|
async function deleteInvitation(id10) {
|
|
1907
|
-
const db =
|
|
1899
|
+
const db = getDatabase7();
|
|
1908
1900
|
if (!db) {
|
|
1909
1901
|
throw new Error("[Auth] Database not initialized");
|
|
1910
1902
|
}
|
|
1911
|
-
await db.delete(invitations).where(
|
|
1903
|
+
await db.delete(invitations).where(eq7(invitations.id, id10));
|
|
1912
1904
|
console.log(`[Auth] \u{1F5D1}\uFE0F Invitation deleted: ${id10}`);
|
|
1913
1905
|
}
|
|
1914
1906
|
async function expireOldInvitations() {
|
|
1915
|
-
const db =
|
|
1907
|
+
const db = getDatabase7();
|
|
1916
1908
|
if (!db) {
|
|
1917
1909
|
throw new Error("[Auth] Database not initialized");
|
|
1918
1910
|
}
|
|
1919
1911
|
const now = /* @__PURE__ */ new Date();
|
|
1920
1912
|
const expiredInvitations = await db.select().from(invitations).where(
|
|
1921
|
-
|
|
1922
|
-
|
|
1913
|
+
and7(
|
|
1914
|
+
eq7(invitations.status, "pending"),
|
|
1923
1915
|
lt(invitations.expiresAt, now)
|
|
1924
1916
|
)
|
|
1925
1917
|
);
|
|
@@ -1930,8 +1922,8 @@ async function expireOldInvitations() {
|
|
|
1930
1922
|
status: "expired",
|
|
1931
1923
|
updatedAt: now
|
|
1932
1924
|
}).where(
|
|
1933
|
-
|
|
1934
|
-
|
|
1925
|
+
and7(
|
|
1926
|
+
eq7(invitations.status, "pending"),
|
|
1935
1927
|
lt(invitations.expiresAt, now)
|
|
1936
1928
|
)
|
|
1937
1929
|
);
|
|
@@ -1939,11 +1931,11 @@ async function expireOldInvitations() {
|
|
|
1939
1931
|
return expiredInvitations.length;
|
|
1940
1932
|
}
|
|
1941
1933
|
async function resendInvitation(id10, expiresInDays = 7) {
|
|
1942
|
-
const db =
|
|
1934
|
+
const db = getDatabase7();
|
|
1943
1935
|
if (!db) {
|
|
1944
1936
|
throw new Error("[Auth] Database not initialized");
|
|
1945
1937
|
}
|
|
1946
|
-
const invitation = await db.select().from(invitations).where(
|
|
1938
|
+
const invitation = await db.select().from(invitations).where(eq7(invitations.id, id10)).limit(1);
|
|
1947
1939
|
if (invitation.length === 0) {
|
|
1948
1940
|
throw new Error("Invitation not found");
|
|
1949
1941
|
}
|
|
@@ -1955,31 +1947,34 @@ async function resendInvitation(id10, expiresInDays = 7) {
|
|
|
1955
1947
|
status: "pending",
|
|
1956
1948
|
expiresAt: newExpiresAt,
|
|
1957
1949
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1958
|
-
}).where(
|
|
1950
|
+
}).where(eq7(invitations.id, id10)).returning();
|
|
1959
1951
|
console.log(`[Auth] \u{1F4E7} Invitation resent: ${invitation[0].email} (new expiry: ${newExpiresAt.toISOString()})`);
|
|
1960
1952
|
return updated;
|
|
1961
1953
|
}
|
|
1962
1954
|
|
|
1963
1955
|
// src/server/middleware/authenticate.ts
|
|
1956
|
+
init_jwt();
|
|
1964
1957
|
init_entities();
|
|
1965
|
-
import { findOne as findOne3, getDatabase as
|
|
1958
|
+
import { findOne as findOne3, getDatabase as getDatabase8 } from "@spfn/core/db";
|
|
1966
1959
|
import { UnauthorizedError as UnauthorizedError2 } from "@spfn/core/errors";
|
|
1967
|
-
import { eq as
|
|
1960
|
+
import { eq as eq8, and as and8 } from "drizzle-orm";
|
|
1968
1961
|
async function authenticate(c, next) {
|
|
1969
1962
|
const authHeader = c.req.header("Authorization");
|
|
1970
|
-
const keyId = c.req.header("X-Key-Id");
|
|
1971
1963
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
1972
1964
|
throw new UnauthorizedError2("Missing or invalid authorization header");
|
|
1973
1965
|
}
|
|
1974
|
-
if (!keyId) {
|
|
1975
|
-
throw new UnauthorizedError2("Missing X-Key-Id header");
|
|
1976
|
-
}
|
|
1977
1966
|
const token = authHeader.substring(7);
|
|
1978
|
-
const
|
|
1967
|
+
const { decodeToken: decodeToken2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
|
|
1968
|
+
const decoded = decodeToken2(token);
|
|
1969
|
+
if (!decoded || !decoded.keyId) {
|
|
1970
|
+
throw new UnauthorizedError2("Invalid token: missing keyId");
|
|
1971
|
+
}
|
|
1972
|
+
const keyId = decoded.keyId;
|
|
1973
|
+
const db = getDatabase8();
|
|
1979
1974
|
const [keyRecord] = await db.select().from(userPublicKeys).where(
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1975
|
+
and8(
|
|
1976
|
+
eq8(userPublicKeys.keyId, keyId),
|
|
1977
|
+
eq8(userPublicKeys.isActive, true)
|
|
1983
1978
|
)
|
|
1984
1979
|
);
|
|
1985
1980
|
if (!keyRecord) {
|
|
@@ -2012,7 +2007,7 @@ async function authenticate(c, next) {
|
|
|
2012
2007
|
if (user.status !== "active") {
|
|
2013
2008
|
throw new AccountDisabledError(user.status);
|
|
2014
2009
|
}
|
|
2015
|
-
db.update(userPublicKeys).set({ lastUsedAt: /* @__PURE__ */ new Date() }).where(
|
|
2010
|
+
db.update(userPublicKeys).set({ lastUsedAt: /* @__PURE__ */ new Date() }).where(eq8(userPublicKeys.id, keyRecord.id)).execute().catch((err) => console.error("Failed to update lastUsedAt:", err));
|
|
2016
2011
|
c.set("auth", {
|
|
2017
2012
|
user,
|
|
2018
2013
|
userId: String(user.id),
|
|
@@ -2078,7 +2073,9 @@ function requireRole(...roleNames) {
|
|
|
2078
2073
|
// src/server/setup.ts
|
|
2079
2074
|
init_entities();
|
|
2080
2075
|
import { findOne as findOne4, create as create4 } from "@spfn/core/db";
|
|
2076
|
+
import { logger as logger2 } from "@spfn/core/logger";
|
|
2081
2077
|
init_role_service();
|
|
2078
|
+
var authLogger2 = logger2.child("@spfn/auth");
|
|
2082
2079
|
function parseAdminAccounts() {
|
|
2083
2080
|
const accounts = [];
|
|
2084
2081
|
if (process.env.SPFN_AUTH_ADMIN_ACCOUNTS || process.env.ADMIN_ACCOUNTS) {
|
|
@@ -2087,12 +2084,12 @@ function parseAdminAccounts() {
|
|
|
2087
2084
|
process.env.ADMIN_ACCOUNTS;
|
|
2088
2085
|
const parsed = JSON.parse(accountsJson);
|
|
2089
2086
|
if (!Array.isArray(parsed)) {
|
|
2090
|
-
|
|
2087
|
+
authLogger2.error("\u274C SPFN_AUTH_ADMIN_ACCOUNTS must be an array");
|
|
2091
2088
|
return accounts;
|
|
2092
2089
|
}
|
|
2093
2090
|
for (const item of parsed) {
|
|
2094
2091
|
if (!item.email || !item.password) {
|
|
2095
|
-
|
|
2092
|
+
authLogger2.warn("\u26A0\uFE0F Skipping account: missing email or password");
|
|
2096
2093
|
continue;
|
|
2097
2094
|
}
|
|
2098
2095
|
accounts.push({
|
|
@@ -2107,7 +2104,7 @@ function parseAdminAccounts() {
|
|
|
2107
2104
|
return accounts;
|
|
2108
2105
|
} catch (error) {
|
|
2109
2106
|
const err = error;
|
|
2110
|
-
|
|
2107
|
+
authLogger2.error("\u274C Failed to parse SPFN_AUTH_ADMIN_ACCOUNTS:", err);
|
|
2111
2108
|
return accounts;
|
|
2112
2109
|
}
|
|
2113
2110
|
}
|
|
@@ -2122,7 +2119,7 @@ function parseAdminAccounts() {
|
|
|
2122
2119
|
process.env.ADMIN_ROLES || // Legacy fallback
|
|
2123
2120
|
"").split(",").map((s) => s.trim());
|
|
2124
2121
|
if (passwords.length !== emails.length) {
|
|
2125
|
-
|
|
2122
|
+
authLogger2.error("\u274C SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch");
|
|
2126
2123
|
return accounts;
|
|
2127
2124
|
}
|
|
2128
2125
|
for (let i = 0; i < emails.length; i++) {
|
|
@@ -2130,7 +2127,7 @@ function parseAdminAccounts() {
|
|
|
2130
2127
|
const password = passwords[i];
|
|
2131
2128
|
const role = roles2[i] || "user";
|
|
2132
2129
|
if (!email || !password) {
|
|
2133
|
-
|
|
2130
|
+
authLogger2.warn(`\u26A0\uFE0F Skipping account ${i + 1}: missing email or password`);
|
|
2134
2131
|
continue;
|
|
2135
2132
|
}
|
|
2136
2133
|
accounts.push({
|
|
@@ -2161,22 +2158,23 @@ async function ensureAdminExists() {
|
|
|
2161
2158
|
if (accounts.length === 0) {
|
|
2162
2159
|
return;
|
|
2163
2160
|
}
|
|
2164
|
-
|
|
2161
|
+
authLogger2.info(`Creating ${accounts.length} admin account(s)...`);
|
|
2165
2162
|
let created = 0;
|
|
2166
2163
|
let skipped = 0;
|
|
2167
2164
|
let failed = 0;
|
|
2168
2165
|
for (const account of accounts) {
|
|
2166
|
+
authLogger2.info(`Creating ${account.email} admin account(s)...`);
|
|
2169
2167
|
try {
|
|
2170
2168
|
const existing = await findOne4(users, { email: account.email });
|
|
2171
2169
|
if (existing) {
|
|
2172
|
-
|
|
2170
|
+
authLogger2.info(`\u26A0\uFE0F Account already exists: ${account.email} (skipped)`);
|
|
2173
2171
|
skipped++;
|
|
2174
2172
|
continue;
|
|
2175
2173
|
}
|
|
2176
2174
|
const roleName = account.role || "user";
|
|
2177
2175
|
const role = await getRoleByName(roleName);
|
|
2178
2176
|
if (!role) {
|
|
2179
|
-
|
|
2177
|
+
authLogger2.error(`\u274C Role '${roleName}' not found for ${account.email}. Run initializeAuth() first.`);
|
|
2180
2178
|
failed++;
|
|
2181
2179
|
continue;
|
|
2182
2180
|
}
|
|
@@ -2191,26 +2189,23 @@ async function ensureAdminExists() {
|
|
|
2191
2189
|
passwordChangeRequired: account.passwordChangeRequired !== false,
|
|
2192
2190
|
status: "active"
|
|
2193
2191
|
});
|
|
2194
|
-
|
|
2192
|
+
authLogger2.info(`\u2705 Admin account created: ${account.email} (${roleName})`);
|
|
2195
2193
|
created++;
|
|
2196
2194
|
} catch (error) {
|
|
2197
2195
|
const err = error;
|
|
2198
|
-
|
|
2196
|
+
authLogger2.error(`\u274C Failed to create account ${account.email}:`, err);
|
|
2199
2197
|
failed++;
|
|
2200
2198
|
}
|
|
2201
2199
|
}
|
|
2202
|
-
|
|
2200
|
+
authLogger2.info(`\u{1F4CA} Summary: ${created} created, ${skipped} skipped, ${failed} failed`);
|
|
2203
2201
|
if (created > 0) {
|
|
2204
|
-
|
|
2202
|
+
authLogger2.info("\u26A0\uFE0F Please change passwords on first login!");
|
|
2205
2203
|
}
|
|
2206
2204
|
}
|
|
2207
2205
|
export {
|
|
2208
2206
|
BUILTIN_PERMISSIONS,
|
|
2209
2207
|
BUILTIN_ROLES,
|
|
2210
2208
|
BUILTIN_ROLE_PERMISSIONS,
|
|
2211
|
-
PRESET_PERMISSIONS,
|
|
2212
|
-
PRESET_ROLES,
|
|
2213
|
-
PRESET_ROLE_PERMISSIONS,
|
|
2214
2209
|
acceptInvitation,
|
|
2215
2210
|
addPermissionToRole,
|
|
2216
2211
|
authenticate,
|
|
@@ -2232,6 +2227,7 @@ export {
|
|
|
2232
2227
|
getInvitationByToken,
|
|
2233
2228
|
getInvitationWithDetails,
|
|
2234
2229
|
getKeyId,
|
|
2230
|
+
getMeService,
|
|
2235
2231
|
getRoleByName,
|
|
2236
2232
|
getRolePermissions,
|
|
2237
2233
|
getUser,
|