@spfn/auth 0.1.0-alpha.1 → 0.1.0-alpha.86
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 +777 -645
- 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 +777 -645
- 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 +495 -512
- package/dist/server/routes/auth/index.js.map +1 -1
- package/dist/server/routes/index.js +775 -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 +320 -327
- package/dist/server.js.map +1 -1
- package/migrations/{0000_tired_gambit.sql → 0000_complex_swordsman.sql} +2 -0
- package/migrations/meta/0000_snapshot.json +4 -2
- package/migrations/meta/_journal.json +2 -2
- package/package.json +30 -3
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() {
|
|
@@ -1453,52 +1390,105 @@ async function verifyCodeService(params) {
|
|
|
1453
1390
|
};
|
|
1454
1391
|
}
|
|
1455
1392
|
|
|
1456
|
-
// src/server/services/
|
|
1393
|
+
// src/server/services/me.service.ts
|
|
1457
1394
|
init_entities();
|
|
1458
1395
|
import { getDatabase as getDatabase4 } from "@spfn/core/db";
|
|
1459
|
-
import { eq as eq4, and as and4
|
|
1460
|
-
async function
|
|
1396
|
+
import { eq as eq4, and as and4 } from "drizzle-orm";
|
|
1397
|
+
async function getMeService(userId) {
|
|
1461
1398
|
const db = getDatabase4();
|
|
1462
1399
|
if (!db) {
|
|
1463
|
-
throw new Error("[Auth] Database not initialized
|
|
1400
|
+
throw new Error("[Auth] Database not initialized");
|
|
1464
1401
|
}
|
|
1465
|
-
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1402
|
+
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
1403
|
+
const [userWithRole] = await db.select({
|
|
1404
|
+
userId: users.id,
|
|
1405
|
+
email: users.email,
|
|
1406
|
+
phone: users.phone,
|
|
1407
|
+
roleId: roles.id,
|
|
1408
|
+
roleName: roles.name,
|
|
1409
|
+
roleDisplayName: roles.displayName,
|
|
1410
|
+
rolePriority: roles.priority
|
|
1411
|
+
}).from(users).innerJoin(roles, eq4(users.roleId, roles.id)).where(eq4(users.id, userIdNum)).limit(1);
|
|
1412
|
+
if (!userWithRole) {
|
|
1413
|
+
throw new Error("[Auth] User not found");
|
|
1414
|
+
}
|
|
1415
|
+
const rolePerms = await db.select({
|
|
1416
|
+
id: permissions.id,
|
|
1417
|
+
name: permissions.name,
|
|
1418
|
+
displayName: permissions.displayName,
|
|
1419
|
+
category: permissions.category
|
|
1420
|
+
}).from(rolePermissions).innerJoin(permissions, eq4(rolePermissions.permissionId, permissions.id)).where(
|
|
1421
|
+
and4(
|
|
1422
|
+
eq4(rolePermissions.roleId, userWithRole.roleId),
|
|
1423
|
+
eq4(permissions.isActive, true)
|
|
1424
|
+
)
|
|
1425
|
+
);
|
|
1426
|
+
return {
|
|
1427
|
+
userId: userWithRole.userId.toString(),
|
|
1428
|
+
email: userWithRole.email ?? void 0,
|
|
1429
|
+
phone: userWithRole.phone ?? void 0,
|
|
1430
|
+
role: {
|
|
1431
|
+
id: userWithRole.roleId,
|
|
1432
|
+
name: userWithRole.roleName,
|
|
1433
|
+
displayName: userWithRole.roleDisplayName,
|
|
1434
|
+
priority: userWithRole.rolePriority
|
|
1435
|
+
},
|
|
1436
|
+
permissions: rolePerms.map((perm) => ({
|
|
1437
|
+
id: perm.id,
|
|
1438
|
+
name: perm.name,
|
|
1439
|
+
displayName: perm.displayName,
|
|
1440
|
+
category: perm.category ?? void 0
|
|
1441
|
+
}))
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
// src/server/services/rbac.service.ts
|
|
1446
|
+
init_entities();
|
|
1447
|
+
import { getDatabase as getDatabase5 } from "@spfn/core/db";
|
|
1448
|
+
import { logger } from "@spfn/core/logger";
|
|
1449
|
+
import { eq as eq5, and as and5, inArray } from "drizzle-orm";
|
|
1450
|
+
|
|
1451
|
+
// src/lib/config.ts
|
|
1452
|
+
var globalConfig = {
|
|
1453
|
+
sessionTtl: "7d"
|
|
1454
|
+
// Default: 7 days
|
|
1455
|
+
};
|
|
1456
|
+
function configureAuth(config) {
|
|
1457
|
+
globalConfig = {
|
|
1458
|
+
...globalConfig,
|
|
1459
|
+
...config
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// src/server/services/rbac.service.ts
|
|
1464
|
+
var authLogger = logger.child("@spfn/auth");
|
|
1465
|
+
async function initializeAuth(options = {}) {
|
|
1466
|
+
const db = getDatabase5();
|
|
1467
|
+
if (!db) {
|
|
1468
|
+
throw new Error("[Auth] Database not initialized. Call initDatabase() first.");
|
|
1475
1469
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1470
|
+
authLogger.info("\u{1F510} Initializing RBAC system...");
|
|
1471
|
+
if (options.sessionTtl !== void 0) {
|
|
1472
|
+
configureAuth({
|
|
1473
|
+
sessionTtl: options.sessionTtl
|
|
1474
|
+
});
|
|
1475
|
+
authLogger.info(`\u23F1\uFE0F Session TTL: ${options.sessionTtl}`);
|
|
1478
1476
|
}
|
|
1477
|
+
const allRoles = [
|
|
1478
|
+
...Object.values(BUILTIN_ROLES),
|
|
1479
|
+
...options.roles || []
|
|
1480
|
+
];
|
|
1479
1481
|
for (const roleConfig of allRoles) {
|
|
1480
1482
|
await upsertRole(roleConfig);
|
|
1481
1483
|
}
|
|
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
|
-
}
|
|
1484
|
+
const allPermissions = [
|
|
1485
|
+
...Object.values(BUILTIN_PERMISSIONS),
|
|
1486
|
+
...options.permissions || []
|
|
1487
|
+
];
|
|
1495
1488
|
for (const permConfig of allPermissions) {
|
|
1496
1489
|
await upsertPermission(permConfig);
|
|
1497
1490
|
}
|
|
1498
1491
|
const allMappings = { ...BUILTIN_ROLE_PERMISSIONS };
|
|
1499
|
-
if (options.usePresets) {
|
|
1500
|
-
Object.assign(allMappings, PRESET_ROLE_PERMISSIONS);
|
|
1501
|
-
}
|
|
1502
1492
|
if (options.rolePermissions) {
|
|
1503
1493
|
for (const [roleName, permNames] of Object.entries(options.rolePermissions)) {
|
|
1504
1494
|
if (allMappings[roleName]) {
|
|
@@ -1513,13 +1503,13 @@ async function initializeAuth(options = {}) {
|
|
|
1513
1503
|
for (const [roleName, permNames] of Object.entries(allMappings)) {
|
|
1514
1504
|
await assignPermissionsToRole(roleName, permNames);
|
|
1515
1505
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1506
|
+
authLogger.info("\u2705 RBAC initialization complete");
|
|
1507
|
+
authLogger.info(`\u{1F4CA} Roles: ${allRoles.length}, Permissions: ${allPermissions.length}`);
|
|
1508
|
+
authLogger.info("\u{1F512} Built-in roles: user, admin, superadmin");
|
|
1519
1509
|
}
|
|
1520
1510
|
async function upsertRole(config) {
|
|
1521
|
-
const db =
|
|
1522
|
-
const existing = await db.select().from(roles).where(
|
|
1511
|
+
const db = getDatabase5();
|
|
1512
|
+
const existing = await db.select().from(roles).where(eq5(roles.name, config.name)).limit(1);
|
|
1523
1513
|
if (existing.length === 0) {
|
|
1524
1514
|
await db.insert(roles).values({
|
|
1525
1515
|
name: config.name,
|
|
@@ -1529,7 +1519,7 @@ async function upsertRole(config) {
|
|
|
1529
1519
|
isSystem: config.isSystem ?? false,
|
|
1530
1520
|
isBuiltin: config.isBuiltin ?? false
|
|
1531
1521
|
});
|
|
1532
|
-
|
|
1522
|
+
authLogger.info(` \u2705 Created role: ${config.name}`);
|
|
1533
1523
|
} else {
|
|
1534
1524
|
const updateData = {
|
|
1535
1525
|
displayName: config.displayName,
|
|
@@ -1538,12 +1528,12 @@ async function upsertRole(config) {
|
|
|
1538
1528
|
if (!existing[0].isBuiltin) {
|
|
1539
1529
|
updateData.priority = config.priority ?? existing[0].priority;
|
|
1540
1530
|
}
|
|
1541
|
-
await db.update(roles).set(updateData).where(
|
|
1531
|
+
await db.update(roles).set(updateData).where(eq5(roles.id, existing[0].id));
|
|
1542
1532
|
}
|
|
1543
1533
|
}
|
|
1544
1534
|
async function upsertPermission(config) {
|
|
1545
|
-
const db =
|
|
1546
|
-
const existing = await db.select().from(permissions).where(
|
|
1535
|
+
const db = getDatabase5();
|
|
1536
|
+
const existing = await db.select().from(permissions).where(eq5(permissions.name, config.name)).limit(1);
|
|
1547
1537
|
if (existing.length === 0) {
|
|
1548
1538
|
await db.insert(permissions).values({
|
|
1549
1539
|
name: config.name,
|
|
@@ -1553,32 +1543,32 @@ async function upsertPermission(config) {
|
|
|
1553
1543
|
isSystem: config.isSystem ?? false,
|
|
1554
1544
|
isBuiltin: config.isBuiltin ?? false
|
|
1555
1545
|
});
|
|
1556
|
-
|
|
1546
|
+
authLogger.info(` \u2705 Created permission: ${config.name}`);
|
|
1557
1547
|
} else {
|
|
1558
1548
|
await db.update(permissions).set({
|
|
1559
1549
|
displayName: config.displayName,
|
|
1560
1550
|
description: config.description,
|
|
1561
1551
|
category: config.category
|
|
1562
|
-
}).where(
|
|
1552
|
+
}).where(eq5(permissions.id, existing[0].id));
|
|
1563
1553
|
}
|
|
1564
1554
|
}
|
|
1565
1555
|
async function assignPermissionsToRole(roleName, permissionNames) {
|
|
1566
|
-
const db =
|
|
1567
|
-
const [role] = await db.select().from(roles).where(
|
|
1556
|
+
const db = getDatabase5();
|
|
1557
|
+
const [role] = await db.select().from(roles).where(eq5(roles.name, roleName)).limit(1);
|
|
1568
1558
|
if (!role) {
|
|
1569
|
-
|
|
1559
|
+
authLogger.warn(` \u26A0\uFE0F Role not found: ${roleName}, skipping permission assignment`);
|
|
1570
1560
|
return;
|
|
1571
1561
|
}
|
|
1572
1562
|
const perms = await db.select().from(permissions).where(inArray(permissions.name, permissionNames));
|
|
1573
1563
|
if (perms.length === 0) {
|
|
1574
|
-
|
|
1564
|
+
authLogger.warn(` \u26A0\uFE0F No permissions found for role: ${roleName}`);
|
|
1575
1565
|
return;
|
|
1576
1566
|
}
|
|
1577
1567
|
for (const perm of perms) {
|
|
1578
1568
|
const existing = await db.select().from(rolePermissions).where(
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1569
|
+
and5(
|
|
1570
|
+
eq5(rolePermissions.roleId, role.id),
|
|
1571
|
+
eq5(rolePermissions.permissionId, perm.id)
|
|
1582
1572
|
)
|
|
1583
1573
|
).limit(1);
|
|
1584
1574
|
if (existing.length === 0) {
|
|
@@ -1592,23 +1582,23 @@ async function assignPermissionsToRole(roleName, permissionNames) {
|
|
|
1592
1582
|
|
|
1593
1583
|
// src/server/services/permission.service.ts
|
|
1594
1584
|
init_entities();
|
|
1595
|
-
import { getDatabase as
|
|
1596
|
-
import { eq as
|
|
1585
|
+
import { getDatabase as getDatabase6 } from "@spfn/core/db";
|
|
1586
|
+
import { eq as eq6, and as and6 } from "drizzle-orm";
|
|
1597
1587
|
async function getUserPermissions(userId) {
|
|
1598
|
-
const db =
|
|
1588
|
+
const db = getDatabase6();
|
|
1599
1589
|
if (!db) {
|
|
1600
1590
|
throw new Error("[Auth] Database not initialized");
|
|
1601
1591
|
}
|
|
1602
1592
|
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
1603
|
-
const [user] = await db.select({ roleId: users.roleId }).from(users).where(
|
|
1593
|
+
const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
|
|
1604
1594
|
if (!user || !user.roleId) {
|
|
1605
1595
|
return [];
|
|
1606
1596
|
}
|
|
1607
1597
|
const permSet = /* @__PURE__ */ new Set();
|
|
1608
|
-
const rolePerms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions,
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1598
|
+
const rolePerms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions, eq6(rolePermissions.permissionId, permissions.id)).where(
|
|
1599
|
+
and6(
|
|
1600
|
+
eq6(rolePermissions.roleId, user.roleId),
|
|
1601
|
+
eq6(permissions.isActive, true)
|
|
1612
1602
|
)
|
|
1613
1603
|
);
|
|
1614
1604
|
for (const perm of rolePerms) {
|
|
@@ -1618,7 +1608,7 @@ async function getUserPermissions(userId) {
|
|
|
1618
1608
|
name: permissions.name,
|
|
1619
1609
|
granted: userPermissions.granted,
|
|
1620
1610
|
expiresAt: userPermissions.expiresAt
|
|
1621
|
-
}).from(userPermissions).innerJoin(permissions,
|
|
1611
|
+
}).from(userPermissions).innerJoin(permissions, eq6(userPermissions.permissionId, permissions.id)).where(eq6(userPermissions.userId, userIdNum));
|
|
1622
1612
|
const now = /* @__PURE__ */ new Date();
|
|
1623
1613
|
for (const userPerm of userPerms) {
|
|
1624
1614
|
if (userPerm.expiresAt && userPerm.expiresAt < now) {
|
|
@@ -1645,16 +1635,16 @@ async function hasAllPermissions(userId, permissionNames) {
|
|
|
1645
1635
|
return permissionNames.every((p) => perms.includes(p));
|
|
1646
1636
|
}
|
|
1647
1637
|
async function hasRole(userId, roleName) {
|
|
1648
|
-
const db =
|
|
1638
|
+
const db = getDatabase6();
|
|
1649
1639
|
if (!db) {
|
|
1650
1640
|
throw new Error("[Auth] Database not initialized");
|
|
1651
1641
|
}
|
|
1652
1642
|
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
1653
|
-
const [user] = await db.select({ roleId: users.roleId }).from(users).where(
|
|
1643
|
+
const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
|
|
1654
1644
|
if (!user || !user.roleId) {
|
|
1655
1645
|
return false;
|
|
1656
1646
|
}
|
|
1657
|
-
const [role] = await db.select({ name: roles.name }).from(roles).where(
|
|
1647
|
+
const [role] = await db.select({ name: roles.name }).from(roles).where(eq6(roles.id, user.roleId)).limit(1);
|
|
1658
1648
|
return role?.name === roleName;
|
|
1659
1649
|
}
|
|
1660
1650
|
async function hasAnyRole(userId, roleNames) {
|
|
@@ -1671,8 +1661,8 @@ init_role_service();
|
|
|
1671
1661
|
|
|
1672
1662
|
// src/server/services/invitation.service.ts
|
|
1673
1663
|
init_entities();
|
|
1674
|
-
import { getDatabase as
|
|
1675
|
-
import { eq as
|
|
1664
|
+
import { getDatabase as getDatabase7 } from "@spfn/core/db";
|
|
1665
|
+
import { eq as eq7, and as and7, lt, desc, sql as sql2 } from "drizzle-orm";
|
|
1676
1666
|
import crypto2 from "crypto";
|
|
1677
1667
|
function generateInvitationToken() {
|
|
1678
1668
|
return crypto2.randomUUID();
|
|
@@ -1683,7 +1673,7 @@ function calculateExpiresAt(days = 7) {
|
|
|
1683
1673
|
return expiresAt;
|
|
1684
1674
|
}
|
|
1685
1675
|
async function createInvitation(params) {
|
|
1686
|
-
const db =
|
|
1676
|
+
const db = getDatabase7();
|
|
1687
1677
|
if (!db) {
|
|
1688
1678
|
throw new Error("[Auth] Database not initialized");
|
|
1689
1679
|
}
|
|
@@ -1692,24 +1682,24 @@ async function createInvitation(params) {
|
|
|
1692
1682
|
if (!emailRegex.test(email)) {
|
|
1693
1683
|
throw new Error("Invalid email format");
|
|
1694
1684
|
}
|
|
1695
|
-
const existingUser = await db.select().from(users).where(
|
|
1685
|
+
const existingUser = await db.select().from(users).where(eq7(users.email, email)).limit(1);
|
|
1696
1686
|
if (existingUser.length > 0) {
|
|
1697
1687
|
throw new Error("User with this email already exists");
|
|
1698
1688
|
}
|
|
1699
1689
|
const existingInvitation = await db.select().from(invitations).where(
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1690
|
+
and7(
|
|
1691
|
+
eq7(invitations.email, email),
|
|
1692
|
+
eq7(invitations.status, "pending")
|
|
1703
1693
|
)
|
|
1704
1694
|
).limit(1);
|
|
1705
1695
|
if (existingInvitation.length > 0) {
|
|
1706
1696
|
throw new Error("Pending invitation already exists for this email");
|
|
1707
1697
|
}
|
|
1708
|
-
const role = await db.select().from(roles).where(
|
|
1698
|
+
const role = await db.select().from(roles).where(eq7(roles.id, roleId)).limit(1);
|
|
1709
1699
|
if (role.length === 0) {
|
|
1710
1700
|
throw new Error(`Role with id ${roleId} not found`);
|
|
1711
1701
|
}
|
|
1712
|
-
const inviter = await db.select().from(users).where(
|
|
1702
|
+
const inviter = await db.select().from(users).where(eq7(users.id, invitedBy)).limit(1);
|
|
1713
1703
|
if (inviter.length === 0) {
|
|
1714
1704
|
throw new Error(`User with id ${invitedBy} not found`);
|
|
1715
1705
|
}
|
|
@@ -1728,15 +1718,15 @@ async function createInvitation(params) {
|
|
|
1728
1718
|
return invitation;
|
|
1729
1719
|
}
|
|
1730
1720
|
async function getInvitationByToken(token) {
|
|
1731
|
-
const db =
|
|
1721
|
+
const db = getDatabase7();
|
|
1732
1722
|
if (!db) {
|
|
1733
1723
|
throw new Error("[Auth] Database not initialized");
|
|
1734
1724
|
}
|
|
1735
|
-
const result = await db.select().from(invitations).where(
|
|
1725
|
+
const result = await db.select().from(invitations).where(eq7(invitations.token, token)).limit(1);
|
|
1736
1726
|
return result[0] || null;
|
|
1737
1727
|
}
|
|
1738
1728
|
async function getInvitationWithDetails(token) {
|
|
1739
|
-
const db =
|
|
1729
|
+
const db = getDatabase7();
|
|
1740
1730
|
if (!db) {
|
|
1741
1731
|
throw new Error("[Auth] Database not initialized");
|
|
1742
1732
|
}
|
|
@@ -1762,7 +1752,7 @@ async function getInvitationWithDetails(token) {
|
|
|
1762
1752
|
id: users.id,
|
|
1763
1753
|
email: users.email
|
|
1764
1754
|
}
|
|
1765
|
-
}).from(invitations).innerJoin(roles,
|
|
1755
|
+
}).from(invitations).innerJoin(roles, eq7(invitations.roleId, roles.id)).innerJoin(users, eq7(invitations.invitedBy, users.id)).where(eq7(invitations.token, token)).limit(1);
|
|
1766
1756
|
return result[0] || null;
|
|
1767
1757
|
}
|
|
1768
1758
|
async function validateInvitation(token) {
|
|
@@ -1785,7 +1775,7 @@ async function validateInvitation(token) {
|
|
|
1785
1775
|
return { valid: true, invitation };
|
|
1786
1776
|
}
|
|
1787
1777
|
async function acceptInvitation(params) {
|
|
1788
|
-
const db =
|
|
1778
|
+
const db = getDatabase7();
|
|
1789
1779
|
if (!db) {
|
|
1790
1780
|
throw new Error("[Auth] Database not initialized");
|
|
1791
1781
|
}
|
|
@@ -1795,7 +1785,7 @@ async function acceptInvitation(params) {
|
|
|
1795
1785
|
throw new Error(validation.error || "Invalid invitation");
|
|
1796
1786
|
}
|
|
1797
1787
|
const invitation = validation.invitation;
|
|
1798
|
-
const role = await db.select().from(roles).where(
|
|
1788
|
+
const role = await db.select().from(roles).where(eq7(roles.id, invitation.roleId)).limit(1);
|
|
1799
1789
|
if (role.length === 0) {
|
|
1800
1790
|
throw new Error("Role not found");
|
|
1801
1791
|
}
|
|
@@ -1825,7 +1815,7 @@ async function acceptInvitation(params) {
|
|
|
1825
1815
|
status: "accepted",
|
|
1826
1816
|
acceptedAt: /* @__PURE__ */ new Date(),
|
|
1827
1817
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1828
|
-
}).where(
|
|
1818
|
+
}).where(eq7(invitations.id, invitation.id));
|
|
1829
1819
|
return { newUser, role: role[0] };
|
|
1830
1820
|
});
|
|
1831
1821
|
console.log(`[Auth] \u2705 Invitation accepted: ${invitation.email} as ${result.role.name}`);
|
|
@@ -1836,7 +1826,7 @@ async function acceptInvitation(params) {
|
|
|
1836
1826
|
};
|
|
1837
1827
|
}
|
|
1838
1828
|
async function listInvitations(params) {
|
|
1839
|
-
const db =
|
|
1829
|
+
const db = getDatabase7();
|
|
1840
1830
|
if (!db) {
|
|
1841
1831
|
throw new Error("[Auth] Database not initialized");
|
|
1842
1832
|
}
|
|
@@ -1844,12 +1834,12 @@ async function listInvitations(params) {
|
|
|
1844
1834
|
const offset = (page - 1) * limit;
|
|
1845
1835
|
const conditions = [];
|
|
1846
1836
|
if (status) {
|
|
1847
|
-
conditions.push(
|
|
1837
|
+
conditions.push(eq7(invitations.status, status));
|
|
1848
1838
|
}
|
|
1849
1839
|
if (invitedBy) {
|
|
1850
|
-
conditions.push(
|
|
1840
|
+
conditions.push(eq7(invitations.invitedBy, invitedBy));
|
|
1851
1841
|
}
|
|
1852
|
-
const whereClause = conditions.length > 0 ?
|
|
1842
|
+
const whereClause = conditions.length > 0 ? and7(...conditions) : void 0;
|
|
1853
1843
|
const countResult = await db.select({ count: sql2`count(*)` }).from(invitations).where(whereClause);
|
|
1854
1844
|
const total = Number(countResult[0]?.count || 0);
|
|
1855
1845
|
const results = await db.select({
|
|
@@ -1874,7 +1864,7 @@ async function listInvitations(params) {
|
|
|
1874
1864
|
id: users.id,
|
|
1875
1865
|
email: users.email
|
|
1876
1866
|
}
|
|
1877
|
-
}).from(invitations).innerJoin(roles,
|
|
1867
|
+
}).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
1868
|
return {
|
|
1879
1869
|
invitations: results,
|
|
1880
1870
|
total,
|
|
@@ -1884,11 +1874,11 @@ async function listInvitations(params) {
|
|
|
1884
1874
|
};
|
|
1885
1875
|
}
|
|
1886
1876
|
async function cancelInvitation(id10, cancelledBy, reason) {
|
|
1887
|
-
const db =
|
|
1877
|
+
const db = getDatabase7();
|
|
1888
1878
|
if (!db) {
|
|
1889
1879
|
throw new Error("[Auth] Database not initialized");
|
|
1890
1880
|
}
|
|
1891
|
-
const invitation = await db.select().from(invitations).where(
|
|
1881
|
+
const invitation = await db.select().from(invitations).where(eq7(invitations.id, id10)).limit(1);
|
|
1892
1882
|
if (invitation.length === 0) {
|
|
1893
1883
|
throw new Error("Invitation not found");
|
|
1894
1884
|
}
|
|
@@ -1900,26 +1890,26 @@ async function cancelInvitation(id10, cancelledBy, reason) {
|
|
|
1900
1890
|
cancelledAt: /* @__PURE__ */ new Date(),
|
|
1901
1891
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
1902
1892
|
metadata: invitation[0].metadata ? { ...invitation[0].metadata, cancelReason: reason, cancelledBy } : { cancelReason: reason, cancelledBy }
|
|
1903
|
-
}).where(
|
|
1893
|
+
}).where(eq7(invitations.id, id10));
|
|
1904
1894
|
console.log(`[Auth] \u26A0\uFE0F Invitation cancelled: ${invitation[0].email} (reason: ${reason || "none"})`);
|
|
1905
1895
|
}
|
|
1906
1896
|
async function deleteInvitation(id10) {
|
|
1907
|
-
const db =
|
|
1897
|
+
const db = getDatabase7();
|
|
1908
1898
|
if (!db) {
|
|
1909
1899
|
throw new Error("[Auth] Database not initialized");
|
|
1910
1900
|
}
|
|
1911
|
-
await db.delete(invitations).where(
|
|
1901
|
+
await db.delete(invitations).where(eq7(invitations.id, id10));
|
|
1912
1902
|
console.log(`[Auth] \u{1F5D1}\uFE0F Invitation deleted: ${id10}`);
|
|
1913
1903
|
}
|
|
1914
1904
|
async function expireOldInvitations() {
|
|
1915
|
-
const db =
|
|
1905
|
+
const db = getDatabase7();
|
|
1916
1906
|
if (!db) {
|
|
1917
1907
|
throw new Error("[Auth] Database not initialized");
|
|
1918
1908
|
}
|
|
1919
1909
|
const now = /* @__PURE__ */ new Date();
|
|
1920
1910
|
const expiredInvitations = await db.select().from(invitations).where(
|
|
1921
|
-
|
|
1922
|
-
|
|
1911
|
+
and7(
|
|
1912
|
+
eq7(invitations.status, "pending"),
|
|
1923
1913
|
lt(invitations.expiresAt, now)
|
|
1924
1914
|
)
|
|
1925
1915
|
);
|
|
@@ -1930,8 +1920,8 @@ async function expireOldInvitations() {
|
|
|
1930
1920
|
status: "expired",
|
|
1931
1921
|
updatedAt: now
|
|
1932
1922
|
}).where(
|
|
1933
|
-
|
|
1934
|
-
|
|
1923
|
+
and7(
|
|
1924
|
+
eq7(invitations.status, "pending"),
|
|
1935
1925
|
lt(invitations.expiresAt, now)
|
|
1936
1926
|
)
|
|
1937
1927
|
);
|
|
@@ -1939,11 +1929,11 @@ async function expireOldInvitations() {
|
|
|
1939
1929
|
return expiredInvitations.length;
|
|
1940
1930
|
}
|
|
1941
1931
|
async function resendInvitation(id10, expiresInDays = 7) {
|
|
1942
|
-
const db =
|
|
1932
|
+
const db = getDatabase7();
|
|
1943
1933
|
if (!db) {
|
|
1944
1934
|
throw new Error("[Auth] Database not initialized");
|
|
1945
1935
|
}
|
|
1946
|
-
const invitation = await db.select().from(invitations).where(
|
|
1936
|
+
const invitation = await db.select().from(invitations).where(eq7(invitations.id, id10)).limit(1);
|
|
1947
1937
|
if (invitation.length === 0) {
|
|
1948
1938
|
throw new Error("Invitation not found");
|
|
1949
1939
|
}
|
|
@@ -1955,31 +1945,34 @@ async function resendInvitation(id10, expiresInDays = 7) {
|
|
|
1955
1945
|
status: "pending",
|
|
1956
1946
|
expiresAt: newExpiresAt,
|
|
1957
1947
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1958
|
-
}).where(
|
|
1948
|
+
}).where(eq7(invitations.id, id10)).returning();
|
|
1959
1949
|
console.log(`[Auth] \u{1F4E7} Invitation resent: ${invitation[0].email} (new expiry: ${newExpiresAt.toISOString()})`);
|
|
1960
1950
|
return updated;
|
|
1961
1951
|
}
|
|
1962
1952
|
|
|
1963
1953
|
// src/server/middleware/authenticate.ts
|
|
1954
|
+
init_jwt();
|
|
1964
1955
|
init_entities();
|
|
1965
|
-
import { findOne as findOne3, getDatabase as
|
|
1956
|
+
import { findOne as findOne3, getDatabase as getDatabase8 } from "@spfn/core/db";
|
|
1966
1957
|
import { UnauthorizedError as UnauthorizedError2 } from "@spfn/core/errors";
|
|
1967
|
-
import { eq as
|
|
1958
|
+
import { eq as eq8, and as and8 } from "drizzle-orm";
|
|
1968
1959
|
async function authenticate(c, next) {
|
|
1969
1960
|
const authHeader = c.req.header("Authorization");
|
|
1970
|
-
const keyId = c.req.header("X-Key-Id");
|
|
1971
1961
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
1972
1962
|
throw new UnauthorizedError2("Missing or invalid authorization header");
|
|
1973
1963
|
}
|
|
1974
|
-
if (!keyId) {
|
|
1975
|
-
throw new UnauthorizedError2("Missing X-Key-Id header");
|
|
1976
|
-
}
|
|
1977
1964
|
const token = authHeader.substring(7);
|
|
1978
|
-
const
|
|
1965
|
+
const { decodeToken: decodeToken2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
|
|
1966
|
+
const decoded = decodeToken2(token);
|
|
1967
|
+
if (!decoded || !decoded.keyId) {
|
|
1968
|
+
throw new UnauthorizedError2("Invalid token: missing keyId");
|
|
1969
|
+
}
|
|
1970
|
+
const keyId = decoded.keyId;
|
|
1971
|
+
const db = getDatabase8();
|
|
1979
1972
|
const [keyRecord] = await db.select().from(userPublicKeys).where(
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1973
|
+
and8(
|
|
1974
|
+
eq8(userPublicKeys.keyId, keyId),
|
|
1975
|
+
eq8(userPublicKeys.isActive, true)
|
|
1983
1976
|
)
|
|
1984
1977
|
);
|
|
1985
1978
|
if (!keyRecord) {
|
|
@@ -2012,7 +2005,7 @@ async function authenticate(c, next) {
|
|
|
2012
2005
|
if (user.status !== "active") {
|
|
2013
2006
|
throw new AccountDisabledError(user.status);
|
|
2014
2007
|
}
|
|
2015
|
-
db.update(userPublicKeys).set({ lastUsedAt: /* @__PURE__ */ new Date() }).where(
|
|
2008
|
+
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
2009
|
c.set("auth", {
|
|
2017
2010
|
user,
|
|
2018
2011
|
userId: String(user.id),
|
|
@@ -2078,7 +2071,9 @@ function requireRole(...roleNames) {
|
|
|
2078
2071
|
// src/server/setup.ts
|
|
2079
2072
|
init_entities();
|
|
2080
2073
|
import { findOne as findOne4, create as create4 } from "@spfn/core/db";
|
|
2074
|
+
import { logger as logger2 } from "@spfn/core/logger";
|
|
2081
2075
|
init_role_service();
|
|
2076
|
+
var authLogger2 = logger2.child("@spfn/auth");
|
|
2082
2077
|
function parseAdminAccounts() {
|
|
2083
2078
|
const accounts = [];
|
|
2084
2079
|
if (process.env.SPFN_AUTH_ADMIN_ACCOUNTS || process.env.ADMIN_ACCOUNTS) {
|
|
@@ -2087,12 +2082,12 @@ function parseAdminAccounts() {
|
|
|
2087
2082
|
process.env.ADMIN_ACCOUNTS;
|
|
2088
2083
|
const parsed = JSON.parse(accountsJson);
|
|
2089
2084
|
if (!Array.isArray(parsed)) {
|
|
2090
|
-
|
|
2085
|
+
authLogger2.error("\u274C SPFN_AUTH_ADMIN_ACCOUNTS must be an array");
|
|
2091
2086
|
return accounts;
|
|
2092
2087
|
}
|
|
2093
2088
|
for (const item of parsed) {
|
|
2094
2089
|
if (!item.email || !item.password) {
|
|
2095
|
-
|
|
2090
|
+
authLogger2.warn("\u26A0\uFE0F Skipping account: missing email or password");
|
|
2096
2091
|
continue;
|
|
2097
2092
|
}
|
|
2098
2093
|
accounts.push({
|
|
@@ -2107,7 +2102,7 @@ function parseAdminAccounts() {
|
|
|
2107
2102
|
return accounts;
|
|
2108
2103
|
} catch (error) {
|
|
2109
2104
|
const err = error;
|
|
2110
|
-
|
|
2105
|
+
authLogger2.error("\u274C Failed to parse SPFN_AUTH_ADMIN_ACCOUNTS:", err);
|
|
2111
2106
|
return accounts;
|
|
2112
2107
|
}
|
|
2113
2108
|
}
|
|
@@ -2122,7 +2117,7 @@ function parseAdminAccounts() {
|
|
|
2122
2117
|
process.env.ADMIN_ROLES || // Legacy fallback
|
|
2123
2118
|
"").split(",").map((s) => s.trim());
|
|
2124
2119
|
if (passwords.length !== emails.length) {
|
|
2125
|
-
|
|
2120
|
+
authLogger2.error("\u274C SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch");
|
|
2126
2121
|
return accounts;
|
|
2127
2122
|
}
|
|
2128
2123
|
for (let i = 0; i < emails.length; i++) {
|
|
@@ -2130,7 +2125,7 @@ function parseAdminAccounts() {
|
|
|
2130
2125
|
const password = passwords[i];
|
|
2131
2126
|
const role = roles2[i] || "user";
|
|
2132
2127
|
if (!email || !password) {
|
|
2133
|
-
|
|
2128
|
+
authLogger2.warn(`\u26A0\uFE0F Skipping account ${i + 1}: missing email or password`);
|
|
2134
2129
|
continue;
|
|
2135
2130
|
}
|
|
2136
2131
|
accounts.push({
|
|
@@ -2161,7 +2156,7 @@ async function ensureAdminExists() {
|
|
|
2161
2156
|
if (accounts.length === 0) {
|
|
2162
2157
|
return;
|
|
2163
2158
|
}
|
|
2164
|
-
|
|
2159
|
+
authLogger2.info(`Creating ${accounts.length} admin account(s)...`);
|
|
2165
2160
|
let created = 0;
|
|
2166
2161
|
let skipped = 0;
|
|
2167
2162
|
let failed = 0;
|
|
@@ -2169,14 +2164,14 @@ async function ensureAdminExists() {
|
|
|
2169
2164
|
try {
|
|
2170
2165
|
const existing = await findOne4(users, { email: account.email });
|
|
2171
2166
|
if (existing) {
|
|
2172
|
-
|
|
2167
|
+
authLogger2.info(`\u26A0\uFE0F Account already exists: ${account.email} (skipped)`);
|
|
2173
2168
|
skipped++;
|
|
2174
2169
|
continue;
|
|
2175
2170
|
}
|
|
2176
2171
|
const roleName = account.role || "user";
|
|
2177
2172
|
const role = await getRoleByName(roleName);
|
|
2178
2173
|
if (!role) {
|
|
2179
|
-
|
|
2174
|
+
authLogger2.error(`\u274C Role '${roleName}' not found for ${account.email}. Run initializeAuth() first.`);
|
|
2180
2175
|
failed++;
|
|
2181
2176
|
continue;
|
|
2182
2177
|
}
|
|
@@ -2191,26 +2186,23 @@ async function ensureAdminExists() {
|
|
|
2191
2186
|
passwordChangeRequired: account.passwordChangeRequired !== false,
|
|
2192
2187
|
status: "active"
|
|
2193
2188
|
});
|
|
2194
|
-
|
|
2189
|
+
authLogger2.info(`\u2705 Admin account created: ${account.email} (${roleName})`);
|
|
2195
2190
|
created++;
|
|
2196
2191
|
} catch (error) {
|
|
2197
2192
|
const err = error;
|
|
2198
|
-
|
|
2193
|
+
authLogger2.error(`\u274C Failed to create account ${account.email}:`, err);
|
|
2199
2194
|
failed++;
|
|
2200
2195
|
}
|
|
2201
2196
|
}
|
|
2202
|
-
|
|
2197
|
+
authLogger2.info(`\u{1F4CA} Summary: ${created} created, ${skipped} skipped, ${failed} failed`);
|
|
2203
2198
|
if (created > 0) {
|
|
2204
|
-
|
|
2199
|
+
authLogger2.info("\u26A0\uFE0F Please change passwords on first login!");
|
|
2205
2200
|
}
|
|
2206
2201
|
}
|
|
2207
2202
|
export {
|
|
2208
2203
|
BUILTIN_PERMISSIONS,
|
|
2209
2204
|
BUILTIN_ROLES,
|
|
2210
2205
|
BUILTIN_ROLE_PERMISSIONS,
|
|
2211
|
-
PRESET_PERMISSIONS,
|
|
2212
|
-
PRESET_ROLES,
|
|
2213
|
-
PRESET_ROLE_PERMISSIONS,
|
|
2214
2206
|
acceptInvitation,
|
|
2215
2207
|
addPermissionToRole,
|
|
2216
2208
|
authenticate,
|
|
@@ -2232,6 +2224,7 @@ export {
|
|
|
2232
2224
|
getInvitationByToken,
|
|
2233
2225
|
getInvitationWithDetails,
|
|
2234
2226
|
getKeyId,
|
|
2227
|
+
getMeService,
|
|
2235
2228
|
getRoleByName,
|
|
2236
2229
|
getRolePermissions,
|
|
2237
2230
|
getUser,
|