@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
|
@@ -8,26 +8,160 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// src/server/helpers/jwt.ts
|
|
12
|
+
var jwt_exports = {};
|
|
13
|
+
__export(jwt_exports, {
|
|
14
|
+
decodeToken: () => decodeToken,
|
|
15
|
+
generateToken: () => generateToken,
|
|
16
|
+
verifyClientToken: () => verifyClientToken,
|
|
17
|
+
verifyKeyFingerprint: () => verifyKeyFingerprint,
|
|
18
|
+
verifyToken: () => verifyToken
|
|
19
|
+
});
|
|
20
|
+
import jwt from "jsonwebtoken";
|
|
21
|
+
import crypto from "crypto";
|
|
22
|
+
function generateToken(payload) {
|
|
23
|
+
return jwt.sign(payload, JWT_SECRET, {
|
|
24
|
+
expiresIn: JWT_EXPIRES_IN
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function verifyToken(token) {
|
|
28
|
+
return jwt.verify(token, JWT_SECRET);
|
|
29
|
+
}
|
|
30
|
+
function verifyClientToken(token, publicKeyB64, algorithm) {
|
|
31
|
+
try {
|
|
32
|
+
const publicKeyDER = Buffer.from(publicKeyB64, "base64");
|
|
33
|
+
const publicKeyObject = crypto.createPublicKey({
|
|
34
|
+
key: publicKeyDER,
|
|
35
|
+
format: "der",
|
|
36
|
+
type: "spki"
|
|
37
|
+
});
|
|
38
|
+
const decoded = jwt.verify(token, publicKeyObject, {
|
|
39
|
+
algorithms: [algorithm],
|
|
40
|
+
// Prevent algorithm confusion attacks
|
|
41
|
+
issuer: "spfn-client"
|
|
42
|
+
// Validate token issuer
|
|
43
|
+
});
|
|
44
|
+
if (typeof decoded === "string") {
|
|
45
|
+
throw new Error("Invalid token format: expected object payload");
|
|
46
|
+
}
|
|
47
|
+
return decoded;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof jwt.TokenExpiredError) {
|
|
50
|
+
throw new Error("Token has expired");
|
|
51
|
+
}
|
|
52
|
+
if (error instanceof jwt.JsonWebTokenError) {
|
|
53
|
+
throw new Error("Invalid token signature");
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`Token verification failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function decodeToken(token) {
|
|
59
|
+
try {
|
|
60
|
+
return jwt.decode(token);
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function verifyKeyFingerprint(publicKeyB64, expectedFingerprint) {
|
|
66
|
+
try {
|
|
67
|
+
const publicKeyDER = Buffer.from(publicKeyB64, "base64");
|
|
68
|
+
const fingerprint = crypto.createHash("sha256").update(publicKeyDER).digest("hex");
|
|
69
|
+
return fingerprint === expectedFingerprint;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error("Failed to verify key fingerprint:", error);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
var JWT_SECRET, JWT_EXPIRES_IN;
|
|
76
|
+
var init_jwt = __esm({
|
|
77
|
+
"src/server/helpers/jwt.ts"() {
|
|
78
|
+
"use strict";
|
|
79
|
+
JWT_SECRET = process.env.SPFN_AUTH_JWT_SECRET || // New prefixed version (recommended)
|
|
80
|
+
process.env.JWT_SECRET || // Legacy fallback
|
|
81
|
+
"dev-secret-key-change-in-production";
|
|
82
|
+
JWT_EXPIRES_IN = process.env.SPFN_AUTH_JWT_EXPIRES_IN || // New prefixed version (recommended)
|
|
83
|
+
process.env.JWT_EXPIRES_IN || // Legacy fallback
|
|
84
|
+
"7d";
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// src/server/entities/schema.ts
|
|
89
|
+
import { createFunctionSchema } from "@spfn/core/db";
|
|
90
|
+
var authSchema;
|
|
91
|
+
var init_schema = __esm({
|
|
92
|
+
"src/server/entities/schema.ts"() {
|
|
93
|
+
"use strict";
|
|
94
|
+
authSchema = createFunctionSchema("@spfn/auth");
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// src/server/entities/verification-codes.ts
|
|
99
|
+
import { text, timestamp, index } from "drizzle-orm/pg-core";
|
|
100
|
+
import { id, timestamps } from "@spfn/core/db";
|
|
101
|
+
var verificationCodes;
|
|
102
|
+
var init_verification_codes = __esm({
|
|
103
|
+
"src/server/entities/verification-codes.ts"() {
|
|
104
|
+
"use strict";
|
|
105
|
+
init_schema();
|
|
106
|
+
verificationCodes = authSchema.table(
|
|
107
|
+
"verification_codes",
|
|
108
|
+
{
|
|
109
|
+
id: id(),
|
|
110
|
+
// Target (email or phone)
|
|
111
|
+
target: text("target").notNull(),
|
|
112
|
+
// Email address or E.164 phone number
|
|
113
|
+
targetType: text(
|
|
114
|
+
"target_type",
|
|
115
|
+
{
|
|
116
|
+
enum: ["email", "phone"]
|
|
117
|
+
}
|
|
118
|
+
).notNull(),
|
|
119
|
+
// Code
|
|
120
|
+
code: text("code").notNull(),
|
|
121
|
+
// 6-digit code by default (configurable)
|
|
122
|
+
// Purpose
|
|
123
|
+
purpose: text(
|
|
124
|
+
"purpose",
|
|
125
|
+
{
|
|
126
|
+
enum: ["registration", "login", "password_reset", "email_change", "phone_change"]
|
|
127
|
+
}
|
|
128
|
+
).notNull(),
|
|
129
|
+
// Expiry
|
|
130
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
131
|
+
// Usage tracking
|
|
132
|
+
usedAt: timestamp("used_at", { withTimezone: true }),
|
|
133
|
+
attempts: text("attempts").notNull().default("0"),
|
|
134
|
+
// Track failed verification attempts
|
|
135
|
+
...timestamps()
|
|
136
|
+
},
|
|
137
|
+
(table) => [
|
|
138
|
+
// Index for quick lookup by target and purpose
|
|
139
|
+
index("target_purpose_idx").on(table.target, table.purpose, table.expiresAt)
|
|
140
|
+
]
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
11
145
|
// src/server/entities/roles.ts
|
|
12
|
-
import { text, boolean, integer, index } from "drizzle-orm/pg-core";
|
|
13
|
-
import { id, timestamps
|
|
14
|
-
var
|
|
146
|
+
import { text as text2, boolean, integer, index as index2 } from "drizzle-orm/pg-core";
|
|
147
|
+
import { id as id2, timestamps as timestamps2 } from "@spfn/core/db";
|
|
148
|
+
var roles;
|
|
15
149
|
var init_roles = __esm({
|
|
16
150
|
"src/server/entities/roles.ts"() {
|
|
17
151
|
"use strict";
|
|
18
|
-
|
|
19
|
-
roles =
|
|
152
|
+
init_schema();
|
|
153
|
+
roles = authSchema.table(
|
|
20
154
|
"roles",
|
|
21
155
|
{
|
|
22
156
|
// Primary key
|
|
23
|
-
id:
|
|
157
|
+
id: id2(),
|
|
24
158
|
// Role identifier (used in code, e.g., 'admin', 'editor')
|
|
25
159
|
// Must be unique, lowercase, kebab-case recommended
|
|
26
|
-
name:
|
|
160
|
+
name: text2("name").notNull().unique(),
|
|
27
161
|
// Display name for UI (e.g., 'Administrator', 'Content Editor')
|
|
28
|
-
displayName:
|
|
162
|
+
displayName: text2("display_name").notNull(),
|
|
29
163
|
// Role description
|
|
30
|
-
description:
|
|
164
|
+
description: text2("description"),
|
|
31
165
|
// Built-in role flag
|
|
32
166
|
// true: Core package roles (user, admin, superadmin) - cannot be deleted
|
|
33
167
|
// false: Custom or preset roles - can be deleted
|
|
@@ -43,45 +177,45 @@ var init_roles = __esm({
|
|
|
43
177
|
// superadmin: 100, admin: 80, user: 10
|
|
44
178
|
// Used for role hierarchy and conflict resolution
|
|
45
179
|
priority: integer("priority").notNull().default(10),
|
|
46
|
-
...
|
|
180
|
+
...timestamps2()
|
|
47
181
|
},
|
|
48
182
|
(table) => [
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
183
|
+
index2("roles_name_idx").on(table.name),
|
|
184
|
+
index2("roles_is_system_idx").on(table.isSystem),
|
|
185
|
+
index2("roles_is_active_idx").on(table.isActive),
|
|
186
|
+
index2("roles_is_builtin_idx").on(table.isBuiltin),
|
|
187
|
+
index2("roles_priority_idx").on(table.priority)
|
|
54
188
|
]
|
|
55
189
|
);
|
|
56
190
|
}
|
|
57
191
|
});
|
|
58
192
|
|
|
59
193
|
// src/server/entities/users.ts
|
|
60
|
-
import { text as
|
|
61
|
-
import { id as
|
|
194
|
+
import { text as text3, timestamp as timestamp2, check, boolean as boolean2, bigint, index as index3 } from "drizzle-orm/pg-core";
|
|
195
|
+
import { id as id3, timestamps as timestamps3 } from "@spfn/core/db";
|
|
62
196
|
import { sql } from "drizzle-orm";
|
|
63
|
-
var
|
|
197
|
+
var users;
|
|
64
198
|
var init_users = __esm({
|
|
65
199
|
"src/server/entities/users.ts"() {
|
|
66
200
|
"use strict";
|
|
67
201
|
init_roles();
|
|
68
|
-
|
|
69
|
-
users =
|
|
202
|
+
init_schema();
|
|
203
|
+
users = authSchema.table(
|
|
70
204
|
"users",
|
|
71
205
|
{
|
|
72
206
|
// Identity
|
|
73
|
-
id:
|
|
207
|
+
id: id3(),
|
|
74
208
|
// Email address (unique identifier)
|
|
75
209
|
// Used for: login, password reset, notifications
|
|
76
|
-
email:
|
|
210
|
+
email: text3("email").unique(),
|
|
77
211
|
// Phone number in E.164 international format
|
|
78
212
|
// Format: +[country code][number] (e.g., +821012345678)
|
|
79
213
|
// Used for: SMS login, 2FA, notifications
|
|
80
|
-
phone:
|
|
214
|
+
phone: text3("phone").unique(),
|
|
81
215
|
// Authentication
|
|
82
216
|
// Bcrypt password hash ($2b$10$[salt][hash], 60 chars)
|
|
83
217
|
// Nullable to support OAuth-only accounts
|
|
84
|
-
passwordHash:
|
|
218
|
+
passwordHash: text3("password_hash"),
|
|
85
219
|
// Force password change on next login
|
|
86
220
|
// Use cases: initial setup, security breach, policy violation
|
|
87
221
|
passwordChangeRequired: boolean2("password_change_required").notNull().default(false),
|
|
@@ -94,7 +228,7 @@ var init_users = __esm({
|
|
|
94
228
|
// - active: Normal operation (default)
|
|
95
229
|
// - inactive: Deactivated (user request, dormant)
|
|
96
230
|
// - suspended: Locked (security incident, ToS violation)
|
|
97
|
-
status:
|
|
231
|
+
status: text3(
|
|
98
232
|
"status",
|
|
99
233
|
{
|
|
100
234
|
enum: ["active", "inactive", "suspended"]
|
|
@@ -103,14 +237,14 @@ var init_users = __esm({
|
|
|
103
237
|
// Verification timestamps
|
|
104
238
|
// null = unverified, timestamp = verified at this time
|
|
105
239
|
// Email verification (via verification code or magic link)
|
|
106
|
-
emailVerifiedAt:
|
|
240
|
+
emailVerifiedAt: timestamp2("email_verified_at", { withTimezone: true }),
|
|
107
241
|
// Phone verification (via SMS OTP)
|
|
108
|
-
phoneVerifiedAt:
|
|
242
|
+
phoneVerifiedAt: timestamp2("phone_verified_at", { withTimezone: true }),
|
|
109
243
|
// Metadata
|
|
110
244
|
// Last successful login timestamp
|
|
111
245
|
// Used for: security auditing, dormant account detection
|
|
112
|
-
lastLoginAt:
|
|
113
|
-
...
|
|
246
|
+
lastLoginAt: timestamp2("last_login_at", { withTimezone: true }),
|
|
247
|
+
...timestamps3()
|
|
114
248
|
},
|
|
115
249
|
(table) => [
|
|
116
250
|
// Database constraints
|
|
@@ -120,44 +254,44 @@ var init_users = __esm({
|
|
|
120
254
|
sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`
|
|
121
255
|
),
|
|
122
256
|
// Indexes for query optimization
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
257
|
+
index3("users_email_idx").on(table.email),
|
|
258
|
+
index3("users_phone_idx").on(table.phone),
|
|
259
|
+
index3("users_status_idx").on(table.status),
|
|
260
|
+
index3("users_role_id_idx").on(table.roleId)
|
|
127
261
|
]
|
|
128
262
|
);
|
|
129
263
|
}
|
|
130
264
|
});
|
|
131
265
|
|
|
132
266
|
// src/server/entities/user-social-accounts.ts
|
|
133
|
-
import { text as
|
|
134
|
-
import { id as
|
|
135
|
-
var
|
|
267
|
+
import { text as text4, timestamp as timestamp3, uniqueIndex } from "drizzle-orm/pg-core";
|
|
268
|
+
import { id as id4, timestamps as timestamps4, foreignKey } from "@spfn/core/db";
|
|
269
|
+
var userSocialAccounts;
|
|
136
270
|
var init_user_social_accounts = __esm({
|
|
137
271
|
"src/server/entities/user-social-accounts.ts"() {
|
|
138
272
|
"use strict";
|
|
139
273
|
init_users();
|
|
140
|
-
|
|
141
|
-
userSocialAccounts =
|
|
274
|
+
init_schema();
|
|
275
|
+
userSocialAccounts = authSchema.table(
|
|
142
276
|
"user_social_accounts",
|
|
143
277
|
{
|
|
144
|
-
id:
|
|
278
|
+
id: id4(),
|
|
145
279
|
// Foreign key to users
|
|
146
280
|
userId: foreignKey("user", () => users.id),
|
|
147
281
|
// Provider info
|
|
148
|
-
provider:
|
|
282
|
+
provider: text4(
|
|
149
283
|
"provider",
|
|
150
284
|
{
|
|
151
285
|
enum: ["google", "github", "kakao", "naver"]
|
|
152
286
|
}
|
|
153
287
|
).notNull(),
|
|
154
|
-
providerUserId:
|
|
155
|
-
providerEmail:
|
|
288
|
+
providerUserId: text4("provider_user_id").notNull(),
|
|
289
|
+
providerEmail: text4("provider_email"),
|
|
156
290
|
// OAuth tokens (encrypted in production)
|
|
157
|
-
accessToken:
|
|
158
|
-
refreshToken:
|
|
159
|
-
tokenExpiresAt:
|
|
160
|
-
...
|
|
291
|
+
accessToken: text4("access_token"),
|
|
292
|
+
refreshToken: text4("refresh_token"),
|
|
293
|
+
tokenExpiresAt: timestamp3("token_expires_at", { withTimezone: true }),
|
|
294
|
+
...timestamps4()
|
|
161
295
|
},
|
|
162
296
|
(table) => [
|
|
163
297
|
// Unique constraint: one provider account per provider
|
|
@@ -168,92 +302,45 @@ var init_user_social_accounts = __esm({
|
|
|
168
302
|
});
|
|
169
303
|
|
|
170
304
|
// src/server/entities/user-public-keys.ts
|
|
171
|
-
import { text as
|
|
172
|
-
import { id as
|
|
173
|
-
var
|
|
305
|
+
import { text as text5, timestamp as timestamp4, boolean as boolean3, index as index4 } from "drizzle-orm/pg-core";
|
|
306
|
+
import { id as id5, foreignKey as foreignKey2 } from "@spfn/core/db";
|
|
307
|
+
var userPublicKeys;
|
|
174
308
|
var init_user_public_keys = __esm({
|
|
175
309
|
"src/server/entities/user-public-keys.ts"() {
|
|
176
310
|
"use strict";
|
|
177
311
|
init_users();
|
|
178
|
-
|
|
179
|
-
userPublicKeys =
|
|
312
|
+
init_schema();
|
|
313
|
+
userPublicKeys = authSchema.table(
|
|
180
314
|
"user_public_keys",
|
|
181
315
|
{
|
|
182
|
-
id:
|
|
316
|
+
id: id5(),
|
|
183
317
|
// User reference
|
|
184
318
|
userId: foreignKey2("user", () => users.id),
|
|
185
319
|
// Key identification (client-generated UUID)
|
|
186
|
-
keyId:
|
|
320
|
+
keyId: text5("key_id").notNull().unique(),
|
|
187
321
|
// Public key in Base64-encoded DER format (SPKI)
|
|
188
|
-
publicKey:
|
|
322
|
+
publicKey: text5("public_key").notNull(),
|
|
189
323
|
// Algorithm used (ES256 recommended, RS256 fallback)
|
|
190
|
-
algorithm:
|
|
324
|
+
algorithm: text5("algorithm", {
|
|
191
325
|
enum: ["ES256", "RS256"]
|
|
192
326
|
}).notNull().default("ES256"),
|
|
193
327
|
// Key fingerprint (SHA-256 hash for quick identification)
|
|
194
|
-
fingerprint:
|
|
328
|
+
fingerprint: text5("fingerprint").notNull(),
|
|
195
329
|
// Key status
|
|
196
330
|
isActive: boolean3("is_active").notNull().default(true),
|
|
197
331
|
// Timestamps
|
|
198
|
-
createdAt:
|
|
199
|
-
lastUsedAt:
|
|
200
|
-
expiresAt:
|
|
332
|
+
createdAt: timestamp4("created_at", { mode: "date", withTimezone: true }).notNull().defaultNow(),
|
|
333
|
+
lastUsedAt: timestamp4("last_used_at", { mode: "date", withTimezone: true }),
|
|
334
|
+
expiresAt: timestamp4("expires_at", { mode: "date", withTimezone: true }),
|
|
201
335
|
// Revocation
|
|
202
|
-
revokedAt:
|
|
203
|
-
revokedReason:
|
|
204
|
-
},
|
|
205
|
-
(table) => [
|
|
206
|
-
index3("user_public_keys_user_id_idx").on(table.userId),
|
|
207
|
-
index3("user_public_keys_key_id_idx").on(table.keyId),
|
|
208
|
-
index3("user_public_keys_active_idx").on(table.isActive),
|
|
209
|
-
index3("user_public_keys_fingerprint_idx").on(table.fingerprint)
|
|
210
|
-
]
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// src/server/entities/verification-codes.ts
|
|
216
|
-
import { text as text5, timestamp as timestamp4, index as index4 } from "drizzle-orm/pg-core";
|
|
217
|
-
import { id as id5, timestamps as timestamps4, createFunctionSchema as createFunctionSchema5 } from "@spfn/core/db";
|
|
218
|
-
var schema5, verificationCodes;
|
|
219
|
-
var init_verification_codes = __esm({
|
|
220
|
-
"src/server/entities/verification-codes.ts"() {
|
|
221
|
-
"use strict";
|
|
222
|
-
schema5 = createFunctionSchema5("@spfn/auth");
|
|
223
|
-
verificationCodes = schema5.table(
|
|
224
|
-
"verification_codes",
|
|
225
|
-
{
|
|
226
|
-
id: id5(),
|
|
227
|
-
// Target (email or phone)
|
|
228
|
-
target: text5("target").notNull(),
|
|
229
|
-
// Email address or E.164 phone number
|
|
230
|
-
targetType: text5(
|
|
231
|
-
"target_type",
|
|
232
|
-
{
|
|
233
|
-
enum: ["email", "phone"]
|
|
234
|
-
}
|
|
235
|
-
).notNull(),
|
|
236
|
-
// Code
|
|
237
|
-
code: text5("code").notNull(),
|
|
238
|
-
// 6-digit code by default (configurable)
|
|
239
|
-
// Purpose
|
|
240
|
-
purpose: text5(
|
|
241
|
-
"purpose",
|
|
242
|
-
{
|
|
243
|
-
enum: ["registration", "login", "password_reset", "email_change", "phone_change"]
|
|
244
|
-
}
|
|
245
|
-
).notNull(),
|
|
246
|
-
// Expiry
|
|
247
|
-
expiresAt: timestamp4("expires_at", { withTimezone: true }).notNull(),
|
|
248
|
-
// Usage tracking
|
|
249
|
-
usedAt: timestamp4("used_at", { withTimezone: true }),
|
|
250
|
-
attempts: text5("attempts").notNull().default("0"),
|
|
251
|
-
// Track failed verification attempts
|
|
252
|
-
...timestamps4()
|
|
336
|
+
revokedAt: timestamp4("revoked_at", { mode: "date", withTimezone: true }),
|
|
337
|
+
revokedReason: text5("revoked_reason")
|
|
253
338
|
},
|
|
254
339
|
(table) => [
|
|
255
|
-
|
|
256
|
-
index4("
|
|
340
|
+
index4("user_public_keys_user_id_idx").on(table.userId),
|
|
341
|
+
index4("user_public_keys_key_id_idx").on(table.keyId),
|
|
342
|
+
index4("user_public_keys_active_idx").on(table.isActive),
|
|
343
|
+
index4("user_public_keys_fingerprint_idx").on(table.fingerprint)
|
|
257
344
|
]
|
|
258
345
|
);
|
|
259
346
|
}
|
|
@@ -261,15 +348,15 @@ var init_verification_codes = __esm({
|
|
|
261
348
|
|
|
262
349
|
// src/server/entities/invitations.ts
|
|
263
350
|
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
|
|
351
|
+
import { id as id6, timestamps as timestamps5 } from "@spfn/core/db";
|
|
352
|
+
var invitations;
|
|
266
353
|
var init_invitations = __esm({
|
|
267
354
|
"src/server/entities/invitations.ts"() {
|
|
268
355
|
"use strict";
|
|
269
356
|
init_roles();
|
|
270
357
|
init_users();
|
|
271
|
-
|
|
272
|
-
invitations =
|
|
358
|
+
init_schema();
|
|
359
|
+
invitations = authSchema.table(
|
|
273
360
|
"user_invitations",
|
|
274
361
|
{
|
|
275
362
|
// Primary key
|
|
@@ -337,13 +424,13 @@ var init_invitations = __esm({
|
|
|
337
424
|
|
|
338
425
|
// src/server/entities/permissions.ts
|
|
339
426
|
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
|
|
427
|
+
import { id as id7, timestamps as timestamps6 } from "@spfn/core/db";
|
|
428
|
+
var permissions;
|
|
342
429
|
var init_permissions = __esm({
|
|
343
430
|
"src/server/entities/permissions.ts"() {
|
|
344
431
|
"use strict";
|
|
345
|
-
|
|
346
|
-
permissions =
|
|
432
|
+
init_schema();
|
|
433
|
+
permissions = authSchema.table(
|
|
347
434
|
"permissions",
|
|
348
435
|
{
|
|
349
436
|
// Primary key
|
|
@@ -384,15 +471,15 @@ var init_permissions = __esm({
|
|
|
384
471
|
|
|
385
472
|
// src/server/entities/role-permissions.ts
|
|
386
473
|
import { bigint as bigint3, index as index7, unique } from "drizzle-orm/pg-core";
|
|
387
|
-
import { id as id8, timestamps as timestamps7
|
|
388
|
-
var
|
|
474
|
+
import { id as id8, timestamps as timestamps7 } from "@spfn/core/db";
|
|
475
|
+
var rolePermissions;
|
|
389
476
|
var init_role_permissions = __esm({
|
|
390
477
|
"src/server/entities/role-permissions.ts"() {
|
|
391
478
|
"use strict";
|
|
392
479
|
init_roles();
|
|
393
480
|
init_permissions();
|
|
394
|
-
|
|
395
|
-
rolePermissions =
|
|
481
|
+
init_schema();
|
|
482
|
+
rolePermissions = authSchema.table(
|
|
396
483
|
"role_permissions",
|
|
397
484
|
{
|
|
398
485
|
// Primary key
|
|
@@ -416,15 +503,15 @@ var init_role_permissions = __esm({
|
|
|
416
503
|
|
|
417
504
|
// src/server/entities/user-permissions.ts
|
|
418
505
|
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
|
|
506
|
+
import { id as id9, timestamps as timestamps8 } from "@spfn/core/db";
|
|
507
|
+
var userPermissions;
|
|
421
508
|
var init_user_permissions = __esm({
|
|
422
509
|
"src/server/entities/user-permissions.ts"() {
|
|
423
510
|
"use strict";
|
|
424
511
|
init_users();
|
|
425
512
|
init_permissions();
|
|
426
|
-
|
|
427
|
-
userPermissions =
|
|
513
|
+
init_schema();
|
|
514
|
+
userPermissions = authSchema.table(
|
|
428
515
|
"user_permissions",
|
|
429
516
|
{
|
|
430
517
|
// Primary key
|
|
@@ -460,6 +547,7 @@ var init_user_permissions = __esm({
|
|
|
460
547
|
// src/server/entities/index.ts
|
|
461
548
|
var entities_exports = {};
|
|
462
549
|
__export(entities_exports, {
|
|
550
|
+
authSchema: () => authSchema,
|
|
463
551
|
invitations: () => invitations,
|
|
464
552
|
permissions: () => permissions,
|
|
465
553
|
rolePermissions: () => rolePermissions,
|
|
@@ -473,6 +561,7 @@ __export(entities_exports, {
|
|
|
473
561
|
var init_entities = __esm({
|
|
474
562
|
"src/server/entities/index.ts"() {
|
|
475
563
|
"use strict";
|
|
564
|
+
init_schema();
|
|
476
565
|
init_users();
|
|
477
566
|
init_user_social_accounts();
|
|
478
567
|
init_user_public_keys();
|
|
@@ -498,14 +587,14 @@ __export(role_service_exports, {
|
|
|
498
587
|
setRolePermissions: () => setRolePermissions,
|
|
499
588
|
updateRole: () => updateRole
|
|
500
589
|
});
|
|
501
|
-
import { getDatabase as
|
|
502
|
-
import { eq as
|
|
590
|
+
import { getDatabase as getDatabase3 } from "@spfn/core/db";
|
|
591
|
+
import { eq as eq3, and as and3 } from "drizzle-orm";
|
|
503
592
|
async function createRole(data) {
|
|
504
|
-
const db =
|
|
593
|
+
const db = getDatabase3();
|
|
505
594
|
if (!db) {
|
|
506
595
|
throw new Error("[Auth] Database not initialized");
|
|
507
596
|
}
|
|
508
|
-
const existing = await db.select().from(roles).where(
|
|
597
|
+
const existing = await db.select().from(roles).where(eq3(roles.name, data.name)).limit(1);
|
|
509
598
|
if (existing.length > 0) {
|
|
510
599
|
throw new Error(`Role with name '${data.name}' already exists`);
|
|
511
600
|
}
|
|
@@ -529,28 +618,28 @@ async function createRole(data) {
|
|
|
529
618
|
return newRole;
|
|
530
619
|
}
|
|
531
620
|
async function updateRole(roleId, data) {
|
|
532
|
-
const db =
|
|
621
|
+
const db = getDatabase3();
|
|
533
622
|
if (!db) {
|
|
534
623
|
throw new Error("[Auth] Database not initialized");
|
|
535
624
|
}
|
|
536
625
|
const roleIdNum = Number(roleId);
|
|
537
|
-
const [role] = await db.select().from(roles).where(
|
|
626
|
+
const [role] = await db.select().from(roles).where(eq3(roles.id, roleIdNum)).limit(1);
|
|
538
627
|
if (!role) {
|
|
539
628
|
throw new Error("Role not found");
|
|
540
629
|
}
|
|
541
630
|
if (role.isBuiltin && data.priority !== void 0) {
|
|
542
631
|
throw new Error("Cannot modify priority of built-in roles");
|
|
543
632
|
}
|
|
544
|
-
const [updated] = await db.update(roles).set(data).where(
|
|
633
|
+
const [updated] = await db.update(roles).set(data).where(eq3(roles.id, roleIdNum)).returning();
|
|
545
634
|
return updated;
|
|
546
635
|
}
|
|
547
636
|
async function deleteRole(roleId) {
|
|
548
|
-
const db =
|
|
637
|
+
const db = getDatabase3();
|
|
549
638
|
if (!db) {
|
|
550
639
|
throw new Error("[Auth] Database not initialized");
|
|
551
640
|
}
|
|
552
641
|
const roleIdNum = Number(roleId);
|
|
553
|
-
const [role] = await db.select().from(roles).where(
|
|
642
|
+
const [role] = await db.select().from(roles).where(eq3(roles.id, roleIdNum)).limit(1);
|
|
554
643
|
if (!role) {
|
|
555
644
|
throw new Error("Role not found");
|
|
556
645
|
}
|
|
@@ -560,20 +649,20 @@ async function deleteRole(roleId) {
|
|
|
560
649
|
if (role.isSystem) {
|
|
561
650
|
throw new Error(`Cannot delete system role: ${role.name}. Deactivate it instead.`);
|
|
562
651
|
}
|
|
563
|
-
await db.delete(roles).where(
|
|
652
|
+
await db.delete(roles).where(eq3(roles.id, roleIdNum));
|
|
564
653
|
console.log(`[Auth] \u{1F5D1}\uFE0F Deleted role: ${role.name}`);
|
|
565
654
|
}
|
|
566
655
|
async function addPermissionToRole(roleId, permissionId) {
|
|
567
|
-
const db =
|
|
656
|
+
const db = getDatabase3();
|
|
568
657
|
if (!db) {
|
|
569
658
|
throw new Error("[Auth] Database not initialized");
|
|
570
659
|
}
|
|
571
660
|
const roleIdNum = Number(roleId);
|
|
572
661
|
const permissionIdNum = Number(permissionId);
|
|
573
662
|
const existing = await db.select().from(rolePermissions).where(
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
663
|
+
and3(
|
|
664
|
+
eq3(rolePermissions.roleId, roleIdNum),
|
|
665
|
+
eq3(rolePermissions.permissionId, permissionIdNum)
|
|
577
666
|
)
|
|
578
667
|
).limit(1);
|
|
579
668
|
if (existing.length > 0) {
|
|
@@ -585,26 +674,26 @@ async function addPermissionToRole(roleId, permissionId) {
|
|
|
585
674
|
});
|
|
586
675
|
}
|
|
587
676
|
async function removePermissionFromRole(roleId, permissionId) {
|
|
588
|
-
const db =
|
|
677
|
+
const db = getDatabase3();
|
|
589
678
|
if (!db) {
|
|
590
679
|
throw new Error("[Auth] Database not initialized");
|
|
591
680
|
}
|
|
592
681
|
const roleIdNum = Number(roleId);
|
|
593
682
|
const permissionIdNum = Number(permissionId);
|
|
594
683
|
await db.delete(rolePermissions).where(
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
684
|
+
and3(
|
|
685
|
+
eq3(rolePermissions.roleId, roleIdNum),
|
|
686
|
+
eq3(rolePermissions.permissionId, permissionIdNum)
|
|
598
687
|
)
|
|
599
688
|
);
|
|
600
689
|
}
|
|
601
690
|
async function setRolePermissions(roleId, permissionIds) {
|
|
602
|
-
const db =
|
|
691
|
+
const db = getDatabase3();
|
|
603
692
|
if (!db) {
|
|
604
693
|
throw new Error("[Auth] Database not initialized");
|
|
605
694
|
}
|
|
606
695
|
const roleIdNum = Number(roleId);
|
|
607
|
-
await db.delete(rolePermissions).where(
|
|
696
|
+
await db.delete(rolePermissions).where(eq3(rolePermissions.roleId, roleIdNum));
|
|
608
697
|
if (permissionIds.length > 0) {
|
|
609
698
|
const mappings = permissionIds.map((permId) => ({
|
|
610
699
|
roleId: roleIdNum,
|
|
@@ -614,31 +703,31 @@ async function setRolePermissions(roleId, permissionIds) {
|
|
|
614
703
|
}
|
|
615
704
|
}
|
|
616
705
|
async function getAllRoles(includeInactive = false) {
|
|
617
|
-
const db =
|
|
706
|
+
const db = getDatabase3();
|
|
618
707
|
if (!db) {
|
|
619
708
|
throw new Error("[Auth] Database not initialized");
|
|
620
709
|
}
|
|
621
710
|
const query = db.select().from(roles);
|
|
622
711
|
if (!includeInactive) {
|
|
623
|
-
return query.where(
|
|
712
|
+
return query.where(eq3(roles.isActive, true));
|
|
624
713
|
}
|
|
625
714
|
return query;
|
|
626
715
|
}
|
|
627
716
|
async function getRoleByName(name) {
|
|
628
|
-
const db =
|
|
717
|
+
const db = getDatabase3();
|
|
629
718
|
if (!db) {
|
|
630
719
|
throw new Error("[Auth] Database not initialized");
|
|
631
720
|
}
|
|
632
|
-
const [role] = await db.select().from(roles).where(
|
|
721
|
+
const [role] = await db.select().from(roles).where(eq3(roles.name, name)).limit(1);
|
|
633
722
|
return role || null;
|
|
634
723
|
}
|
|
635
724
|
async function getRolePermissions(roleId) {
|
|
636
|
-
const db =
|
|
725
|
+
const db = getDatabase3();
|
|
637
726
|
if (!db) {
|
|
638
727
|
throw new Error("[Auth] Database not initialized");
|
|
639
728
|
}
|
|
640
729
|
const roleIdNum = Number(roleId);
|
|
641
|
-
const perms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions,
|
|
730
|
+
const perms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions, eq3(rolePermissions.permissionId, permissions.id)).where(eq3(rolePermissions.roleId, roleIdNum));
|
|
642
731
|
return perms.map((p) => p.name);
|
|
643
732
|
}
|
|
644
733
|
var init_role_service = __esm({
|
|
@@ -2243,8 +2332,8 @@ function Clone(value) {
|
|
|
2243
2332
|
}
|
|
2244
2333
|
|
|
2245
2334
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/clone/type.mjs
|
|
2246
|
-
function CloneType(
|
|
2247
|
-
return options === void 0 ? Clone(
|
|
2335
|
+
function CloneType(schema, options) {
|
|
2336
|
+
return options === void 0 ? Clone(schema) : Clone({ ...options, ...schema });
|
|
2248
2337
|
}
|
|
2249
2338
|
|
|
2250
2339
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/value/guard/guard.mjs
|
|
@@ -2321,8 +2410,8 @@ function Immutable(value) {
|
|
|
2321
2410
|
}
|
|
2322
2411
|
|
|
2323
2412
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/create/type.mjs
|
|
2324
|
-
function CreateType(
|
|
2325
|
-
const result = options !== void 0 ? { ...options, ...
|
|
2413
|
+
function CreateType(schema, options) {
|
|
2414
|
+
const result = options !== void 0 ? { ...options, ...schema } : schema;
|
|
2326
2415
|
switch (TypeSystemPolicy.InstanceMode) {
|
|
2327
2416
|
case "freeze":
|
|
2328
2417
|
return Immutable(result);
|
|
@@ -2640,16 +2729,16 @@ function IsBoolean3(value) {
|
|
|
2640
2729
|
return IsKindOf2(value, "Boolean") && value.type === "boolean" && IsOptionalString(value.$id);
|
|
2641
2730
|
}
|
|
2642
2731
|
function IsComputed2(value) {
|
|
2643
|
-
return IsKindOf2(value, "Computed") && IsString(value.target) && IsArray(value.parameters) && value.parameters.every((
|
|
2732
|
+
return IsKindOf2(value, "Computed") && IsString(value.target) && IsArray(value.parameters) && value.parameters.every((schema) => IsSchema2(schema));
|
|
2644
2733
|
}
|
|
2645
2734
|
function IsConstructor2(value) {
|
|
2646
|
-
return IsKindOf2(value, "Constructor") && value.type === "Constructor" && IsOptionalString(value.$id) && IsArray(value.parameters) && value.parameters.every((
|
|
2735
|
+
return IsKindOf2(value, "Constructor") && value.type === "Constructor" && IsOptionalString(value.$id) && IsArray(value.parameters) && value.parameters.every((schema) => IsSchema2(schema)) && IsSchema2(value.returns);
|
|
2647
2736
|
}
|
|
2648
2737
|
function IsDate3(value) {
|
|
2649
2738
|
return IsKindOf2(value, "Date") && value.type === "Date" && IsOptionalString(value.$id) && IsOptionalNumber(value.exclusiveMaximumTimestamp) && IsOptionalNumber(value.exclusiveMinimumTimestamp) && IsOptionalNumber(value.maximumTimestamp) && IsOptionalNumber(value.minimumTimestamp) && IsOptionalNumber(value.multipleOfTimestamp);
|
|
2650
2739
|
}
|
|
2651
2740
|
function IsFunction3(value) {
|
|
2652
|
-
return IsKindOf2(value, "Function") && value.type === "Function" && IsOptionalString(value.$id) && IsArray(value.parameters) && value.parameters.every((
|
|
2741
|
+
return IsKindOf2(value, "Function") && value.type === "Function" && IsOptionalString(value.$id) && IsArray(value.parameters) && value.parameters.every((schema) => IsSchema2(schema)) && IsSchema2(value.returns);
|
|
2653
2742
|
}
|
|
2654
2743
|
function IsImport(value) {
|
|
2655
2744
|
return IsKindOf2(value, "Import") && HasPropertyKey(value, "$defs") && IsObject(value.$defs) && IsProperties(value.$defs) && HasPropertyKey(value, "$ref") && IsString(value.$ref) && value.$ref in value.$defs;
|
|
@@ -2658,10 +2747,10 @@ function IsInteger2(value) {
|
|
|
2658
2747
|
return IsKindOf2(value, "Integer") && value.type === "integer" && IsOptionalString(value.$id) && IsOptionalNumber(value.exclusiveMaximum) && IsOptionalNumber(value.exclusiveMinimum) && IsOptionalNumber(value.maximum) && IsOptionalNumber(value.minimum) && IsOptionalNumber(value.multipleOf);
|
|
2659
2748
|
}
|
|
2660
2749
|
function IsProperties(value) {
|
|
2661
|
-
return IsObject(value) && Object.entries(value).every(([key,
|
|
2750
|
+
return IsObject(value) && Object.entries(value).every(([key, schema]) => IsControlCharacterFree(key) && IsSchema2(schema));
|
|
2662
2751
|
}
|
|
2663
2752
|
function IsIntersect2(value) {
|
|
2664
|
-
return IsKindOf2(value, "Intersect") && (IsString(value.type) && value.type !== "object" ? false : true) && IsArray(value.allOf) && value.allOf.every((
|
|
2753
|
+
return IsKindOf2(value, "Intersect") && (IsString(value.type) && value.type !== "object" ? false : true) && IsArray(value.allOf) && value.allOf.every((schema) => IsSchema2(schema) && !IsTransform2(schema)) && IsOptionalString(value.type) && (IsOptionalBoolean(value.unevaluatedProperties) || IsOptionalSchema(value.unevaluatedProperties)) && IsOptionalString(value.$id);
|
|
2665
2754
|
}
|
|
2666
2755
|
function IsIterator3(value) {
|
|
2667
2756
|
return IsKindOf2(value, "Iterator") && value.type === "Iterator" && IsOptionalString(value.$id) && IsSchema2(value.items);
|
|
@@ -2709,9 +2798,9 @@ function IsPromise2(value) {
|
|
|
2709
2798
|
return IsKindOf2(value, "Promise") && value.type === "Promise" && IsOptionalString(value.$id) && IsSchema2(value.item);
|
|
2710
2799
|
}
|
|
2711
2800
|
function IsRecord2(value) {
|
|
2712
|
-
return IsKindOf2(value, "Record") && value.type === "object" && IsOptionalString(value.$id) && IsAdditionalProperties(value.additionalProperties) && IsObject(value.patternProperties) && ((
|
|
2713
|
-
const keys = Object.getOwnPropertyNames(
|
|
2714
|
-
return keys.length === 1 && IsPattern(keys[0]) && IsObject(
|
|
2801
|
+
return IsKindOf2(value, "Record") && value.type === "object" && IsOptionalString(value.$id) && IsAdditionalProperties(value.additionalProperties) && IsObject(value.patternProperties) && ((schema) => {
|
|
2802
|
+
const keys = Object.getOwnPropertyNames(schema.patternProperties);
|
|
2803
|
+
return keys.length === 1 && IsPattern(keys[0]) && IsObject(schema.patternProperties) && IsSchema2(schema.patternProperties[keys[0]]);
|
|
2715
2804
|
})(value);
|
|
2716
2805
|
}
|
|
2717
2806
|
function IsRecursive(value) {
|
|
@@ -2740,16 +2829,16 @@ function IsTransform2(value) {
|
|
|
2740
2829
|
}
|
|
2741
2830
|
function IsTuple2(value) {
|
|
2742
2831
|
return IsKindOf2(value, "Tuple") && value.type === "array" && IsOptionalString(value.$id) && IsNumber(value.minItems) && IsNumber(value.maxItems) && value.minItems === value.maxItems && // empty
|
|
2743
|
-
(IsUndefined(value.items) && IsUndefined(value.additionalItems) && value.minItems === 0 || IsArray(value.items) && value.items.every((
|
|
2832
|
+
(IsUndefined(value.items) && IsUndefined(value.additionalItems) && value.minItems === 0 || IsArray(value.items) && value.items.every((schema) => IsSchema2(schema)));
|
|
2744
2833
|
}
|
|
2745
2834
|
function IsUndefined4(value) {
|
|
2746
2835
|
return IsKindOf2(value, "Undefined") && value.type === "undefined" && IsOptionalString(value.$id);
|
|
2747
2836
|
}
|
|
2748
2837
|
function IsUnionLiteral(value) {
|
|
2749
|
-
return IsUnion2(value) && value.anyOf.every((
|
|
2838
|
+
return IsUnion2(value) && value.anyOf.every((schema) => IsLiteralString(schema) || IsLiteralNumber(schema));
|
|
2750
2839
|
}
|
|
2751
2840
|
function IsUnion2(value) {
|
|
2752
|
-
return IsKindOf2(value, "Union") && IsOptionalString(value.$id) && IsObject(value) && IsArray(value.anyOf) && value.anyOf.every((
|
|
2841
|
+
return IsKindOf2(value, "Union") && IsOptionalString(value.$id) && IsObject(value) && IsArray(value.anyOf) && value.anyOf.every((schema) => IsSchema2(schema));
|
|
2753
2842
|
}
|
|
2754
2843
|
function IsUint8Array3(value) {
|
|
2755
2844
|
return IsKindOf2(value, "Uint8Array") && value.type === "Uint8Array" && IsOptionalString(value.$id) && IsOptionalNumber(value.minByteLength) && IsOptionalNumber(value.maxByteLength);
|
|
@@ -3031,8 +3120,8 @@ function IsTemplateLiteralExpressionFinite(expression) {
|
|
|
3031
3120
|
throw new TemplateLiteralFiniteError(`Unknown expression type`);
|
|
3032
3121
|
})();
|
|
3033
3122
|
}
|
|
3034
|
-
function IsTemplateLiteralFinite(
|
|
3035
|
-
const expression = TemplateLiteralParseExact(
|
|
3123
|
+
function IsTemplateLiteralFinite(schema) {
|
|
3124
|
+
const expression = TemplateLiteralParseExact(schema.pattern);
|
|
3036
3125
|
return IsTemplateLiteralExpressionFinite(expression);
|
|
3037
3126
|
}
|
|
3038
3127
|
|
|
@@ -3063,8 +3152,8 @@ function* TemplateLiteralExpressionGenerate(expression) {
|
|
|
3063
3152
|
throw new TemplateLiteralGenerateError("Unknown expression");
|
|
3064
3153
|
})();
|
|
3065
3154
|
}
|
|
3066
|
-
function TemplateLiteralGenerate(
|
|
3067
|
-
const expression = TemplateLiteralParseExact(
|
|
3155
|
+
function TemplateLiteralGenerate(schema) {
|
|
3156
|
+
const expression = TemplateLiteralParseExact(schema.pattern);
|
|
3068
3157
|
return IsTemplateLiteralExpressionFinite(expression) ? [...TemplateLiteralExpressionGenerate(expression)] : [];
|
|
3069
3158
|
}
|
|
3070
3159
|
|
|
@@ -3140,18 +3229,18 @@ var TemplateLiteralPatternError = class extends TypeBoxError {
|
|
|
3140
3229
|
function Escape(value) {
|
|
3141
3230
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3142
3231
|
}
|
|
3143
|
-
function Visit2(
|
|
3144
|
-
return IsTemplateLiteral(
|
|
3145
|
-
throw new TemplateLiteralPatternError(`Unexpected Kind '${
|
|
3232
|
+
function Visit2(schema, acc) {
|
|
3233
|
+
return IsTemplateLiteral(schema) ? schema.pattern.slice(1, schema.pattern.length - 1) : IsUnion(schema) ? `(${schema.anyOf.map((schema2) => Visit2(schema2, acc)).join("|")})` : IsNumber3(schema) ? `${acc}${PatternNumber}` : IsInteger(schema) ? `${acc}${PatternNumber}` : IsBigInt2(schema) ? `${acc}${PatternNumber}` : IsString2(schema) ? `${acc}${PatternString}` : IsLiteral(schema) ? `${acc}${Escape(schema.const.toString())}` : IsBoolean2(schema) ? `${acc}${PatternBoolean}` : (() => {
|
|
3234
|
+
throw new TemplateLiteralPatternError(`Unexpected Kind '${schema[Kind]}'`);
|
|
3146
3235
|
})();
|
|
3147
3236
|
}
|
|
3148
3237
|
function TemplateLiteralPattern(kinds) {
|
|
3149
|
-
return `^${kinds.map((
|
|
3238
|
+
return `^${kinds.map((schema) => Visit2(schema, "")).join("")}$`;
|
|
3150
3239
|
}
|
|
3151
3240
|
|
|
3152
3241
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/template-literal/union.mjs
|
|
3153
|
-
function TemplateLiteralToUnion(
|
|
3154
|
-
const R = TemplateLiteralGenerate(
|
|
3242
|
+
function TemplateLiteralToUnion(schema) {
|
|
3243
|
+
const R = TemplateLiteralGenerate(schema);
|
|
3155
3244
|
const L = R.map((S) => Literal(S));
|
|
3156
3245
|
return UnionEvaluated(L);
|
|
3157
3246
|
}
|
|
@@ -3288,18 +3377,18 @@ function Promise2(item, options) {
|
|
|
3288
3377
|
}
|
|
3289
3378
|
|
|
3290
3379
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/readonly/readonly.mjs
|
|
3291
|
-
function RemoveReadonly(
|
|
3292
|
-
return CreateType(Discard(
|
|
3380
|
+
function RemoveReadonly(schema) {
|
|
3381
|
+
return CreateType(Discard(schema, [ReadonlyKind]));
|
|
3293
3382
|
}
|
|
3294
|
-
function AddReadonly(
|
|
3295
|
-
return CreateType({ ...
|
|
3383
|
+
function AddReadonly(schema) {
|
|
3384
|
+
return CreateType({ ...schema, [ReadonlyKind]: "Readonly" });
|
|
3296
3385
|
}
|
|
3297
|
-
function ReadonlyWithFlag(
|
|
3298
|
-
return F === false ? RemoveReadonly(
|
|
3386
|
+
function ReadonlyWithFlag(schema, F) {
|
|
3387
|
+
return F === false ? RemoveReadonly(schema) : AddReadonly(schema);
|
|
3299
3388
|
}
|
|
3300
|
-
function Readonly(
|
|
3389
|
+
function Readonly(schema, enable) {
|
|
3301
3390
|
const F = enable ?? true;
|
|
3302
|
-
return IsMappedResult(
|
|
3391
|
+
return IsMappedResult(schema) ? ReadonlyFromMappedResult(schema, F) : ReadonlyWithFlag(schema, F);
|
|
3303
3392
|
}
|
|
3304
3393
|
|
|
3305
3394
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/readonly/readonly-from-mapped-result.mjs
|
|
@@ -3378,18 +3467,18 @@ function Mapped(key, map, options) {
|
|
|
3378
3467
|
}
|
|
3379
3468
|
|
|
3380
3469
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/optional/optional.mjs
|
|
3381
|
-
function RemoveOptional(
|
|
3382
|
-
return CreateType(Discard(
|
|
3470
|
+
function RemoveOptional(schema) {
|
|
3471
|
+
return CreateType(Discard(schema, [OptionalKind]));
|
|
3383
3472
|
}
|
|
3384
|
-
function AddOptional(
|
|
3385
|
-
return CreateType({ ...
|
|
3473
|
+
function AddOptional(schema) {
|
|
3474
|
+
return CreateType({ ...schema, [OptionalKind]: "Optional" });
|
|
3386
3475
|
}
|
|
3387
|
-
function OptionalWithFlag(
|
|
3388
|
-
return F === false ? RemoveOptional(
|
|
3476
|
+
function OptionalWithFlag(schema, F) {
|
|
3477
|
+
return F === false ? RemoveOptional(schema) : AddOptional(schema);
|
|
3389
3478
|
}
|
|
3390
|
-
function Optional(
|
|
3479
|
+
function Optional(schema, enable) {
|
|
3391
3480
|
const F = enable ?? true;
|
|
3392
|
-
return IsMappedResult(
|
|
3481
|
+
return IsMappedResult(schema) ? OptionalFromMappedResult(schema, F) : OptionalWithFlag(schema, F);
|
|
3393
3482
|
}
|
|
3394
3483
|
|
|
3395
3484
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/optional/optional-from-mapped-result.mjs
|
|
@@ -3409,7 +3498,7 @@ function OptionalFromMappedResult(R, F) {
|
|
|
3409
3498
|
|
|
3410
3499
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/intersect/intersect-create.mjs
|
|
3411
3500
|
function IntersectCreate(T, options = {}) {
|
|
3412
|
-
const allObjects = T.every((
|
|
3501
|
+
const allObjects = T.every((schema) => IsObject3(schema));
|
|
3413
3502
|
const clonedUnevaluatedProperties = IsSchema(options.unevaluatedProperties) ? { unevaluatedProperties: options.unevaluatedProperties } : {};
|
|
3414
3503
|
return CreateType(options.unevaluatedProperties === false || IsSchema(options.unevaluatedProperties) || allObjects ? { ...clonedUnevaluatedProperties, [Kind]: "Intersect", type: "object", allOf: T } : { ...clonedUnevaluatedProperties, [Kind]: "Intersect", allOf: T }, options);
|
|
3415
3504
|
}
|
|
@@ -3432,7 +3521,7 @@ function IntersectEvaluated(types, options = {}) {
|
|
|
3432
3521
|
return CreateType(types[0], options);
|
|
3433
3522
|
if (types.length === 0)
|
|
3434
3523
|
return Never(options);
|
|
3435
|
-
if (types.some((
|
|
3524
|
+
if (types.some((schema) => IsTransform(schema)))
|
|
3436
3525
|
throw new Error("Cannot intersect transform types");
|
|
3437
3526
|
return ResolveIntersect(types, options);
|
|
3438
3527
|
}
|
|
@@ -3443,7 +3532,7 @@ function Intersect(types, options) {
|
|
|
3443
3532
|
return CreateType(types[0], options);
|
|
3444
3533
|
if (types.length === 0)
|
|
3445
3534
|
return Never(options);
|
|
3446
|
-
if (types.some((
|
|
3535
|
+
if (types.some((schema) => IsTransform(schema)))
|
|
3447
3536
|
throw new Error("Cannot intersect transform types");
|
|
3448
3537
|
return IntersectCreate(types, options);
|
|
3449
3538
|
}
|
|
@@ -3634,8 +3723,8 @@ function Const(T, options) {
|
|
|
3634
3723
|
}
|
|
3635
3724
|
|
|
3636
3725
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/constructor-parameters/constructor-parameters.mjs
|
|
3637
|
-
function ConstructorParameters(
|
|
3638
|
-
return IsConstructor(
|
|
3726
|
+
function ConstructorParameters(schema, options) {
|
|
3727
|
+
return IsConstructor(schema) ? Tuple(schema.parameters, options) : Never(options);
|
|
3639
3728
|
}
|
|
3640
3729
|
|
|
3641
3730
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/enum/enum.mjs
|
|
@@ -3673,7 +3762,7 @@ function FromAnyRight(left, right) {
|
|
|
3673
3762
|
return ExtendsResult.True;
|
|
3674
3763
|
}
|
|
3675
3764
|
function FromAny(left, right) {
|
|
3676
|
-
return type_exports.IsIntersect(right) ? FromIntersectRight(left, right) : type_exports.IsUnion(right) && right.anyOf.some((
|
|
3765
|
+
return type_exports.IsIntersect(right) ? FromIntersectRight(left, right) : type_exports.IsUnion(right) && right.anyOf.some((schema) => type_exports.IsAny(schema) || type_exports.IsUnknown(schema)) ? ExtendsResult.True : type_exports.IsUnion(right) ? ExtendsResult.Union : type_exports.IsUnknown(right) ? ExtendsResult.True : type_exports.IsAny(right) ? ExtendsResult.True : ExtendsResult.Union;
|
|
3677
3766
|
}
|
|
3678
3767
|
function FromArrayRight(left, right) {
|
|
3679
3768
|
return type_exports.IsUnknown(left) ? ExtendsResult.False : type_exports.IsAny(left) ? ExtendsResult.Union : type_exports.IsNever(left) ? ExtendsResult.True : ExtendsResult.False;
|
|
@@ -3694,13 +3783,13 @@ function FromBoolean(left, right) {
|
|
|
3694
3783
|
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : type_exports.IsRecord(right) ? FromRecordRight(left, right) : type_exports.IsBoolean(right) ? ExtendsResult.True : ExtendsResult.False;
|
|
3695
3784
|
}
|
|
3696
3785
|
function FromConstructor(left, right) {
|
|
3697
|
-
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : !type_exports.IsConstructor(right) ? ExtendsResult.False : left.parameters.length > right.parameters.length ? ExtendsResult.False : !left.parameters.every((
|
|
3786
|
+
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : !type_exports.IsConstructor(right) ? ExtendsResult.False : left.parameters.length > right.parameters.length ? ExtendsResult.False : !left.parameters.every((schema, index9) => IntoBooleanResult(Visit3(right.parameters[index9], schema)) === ExtendsResult.True) ? ExtendsResult.False : IntoBooleanResult(Visit3(left.returns, right.returns));
|
|
3698
3787
|
}
|
|
3699
3788
|
function FromDate(left, right) {
|
|
3700
3789
|
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : type_exports.IsRecord(right) ? FromRecordRight(left, right) : type_exports.IsDate(right) ? ExtendsResult.True : ExtendsResult.False;
|
|
3701
3790
|
}
|
|
3702
3791
|
function FromFunction(left, right) {
|
|
3703
|
-
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : !type_exports.IsFunction(right) ? ExtendsResult.False : left.parameters.length > right.parameters.length ? ExtendsResult.False : !left.parameters.every((
|
|
3792
|
+
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : !type_exports.IsFunction(right) ? ExtendsResult.False : left.parameters.length > right.parameters.length ? ExtendsResult.False : !left.parameters.every((schema, index9) => IntoBooleanResult(Visit3(right.parameters[index9], schema)) === ExtendsResult.True) ? ExtendsResult.False : IntoBooleanResult(Visit3(left.returns, right.returns));
|
|
3704
3793
|
}
|
|
3705
3794
|
function FromIntegerRight(left, right) {
|
|
3706
3795
|
return type_exports.IsLiteral(left) && value_exports.IsNumber(left.const) ? ExtendsResult.True : type_exports.IsNumber(left) || type_exports.IsInteger(left) ? ExtendsResult.True : ExtendsResult.False;
|
|
@@ -3709,10 +3798,10 @@ function FromInteger(left, right) {
|
|
|
3709
3798
|
return type_exports.IsInteger(right) || type_exports.IsNumber(right) ? ExtendsResult.True : IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : type_exports.IsRecord(right) ? FromRecordRight(left, right) : ExtendsResult.False;
|
|
3710
3799
|
}
|
|
3711
3800
|
function FromIntersectRight(left, right) {
|
|
3712
|
-
return right.allOf.every((
|
|
3801
|
+
return right.allOf.every((schema) => Visit3(left, schema) === ExtendsResult.True) ? ExtendsResult.True : ExtendsResult.False;
|
|
3713
3802
|
}
|
|
3714
3803
|
function FromIntersect4(left, right) {
|
|
3715
|
-
return left.allOf.some((
|
|
3804
|
+
return left.allOf.some((schema) => Visit3(schema, right) === ExtendsResult.True) ? ExtendsResult.True : ExtendsResult.False;
|
|
3716
3805
|
}
|
|
3717
3806
|
function FromIterator(left, right) {
|
|
3718
3807
|
return IsStructuralRight(right) ? StructuralRight(left, right) : !type_exports.IsIterator(right) ? ExtendsResult.False : IntoBooleanResult(Visit3(left.items, right.items));
|
|
@@ -3726,8 +3815,8 @@ function FromNeverRight(left, right) {
|
|
|
3726
3815
|
function FromNever(left, right) {
|
|
3727
3816
|
return ExtendsResult.True;
|
|
3728
3817
|
}
|
|
3729
|
-
function UnwrapTNot(
|
|
3730
|
-
let [current, depth] = [
|
|
3818
|
+
function UnwrapTNot(schema) {
|
|
3819
|
+
let [current, depth] = [schema, 0];
|
|
3731
3820
|
while (true) {
|
|
3732
3821
|
if (!type_exports.IsNot(current))
|
|
3733
3822
|
break;
|
|
@@ -3748,44 +3837,44 @@ function FromNumberRight(left, right) {
|
|
|
3748
3837
|
function FromNumber(left, right) {
|
|
3749
3838
|
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : type_exports.IsRecord(right) ? FromRecordRight(left, right) : type_exports.IsInteger(right) || type_exports.IsNumber(right) ? ExtendsResult.True : ExtendsResult.False;
|
|
3750
3839
|
}
|
|
3751
|
-
function IsObjectPropertyCount(
|
|
3752
|
-
return Object.getOwnPropertyNames(
|
|
3840
|
+
function IsObjectPropertyCount(schema, count) {
|
|
3841
|
+
return Object.getOwnPropertyNames(schema.properties).length === count;
|
|
3753
3842
|
}
|
|
3754
|
-
function IsObjectStringLike(
|
|
3755
|
-
return IsObjectArrayLike(
|
|
3843
|
+
function IsObjectStringLike(schema) {
|
|
3844
|
+
return IsObjectArrayLike(schema);
|
|
3756
3845
|
}
|
|
3757
|
-
function IsObjectSymbolLike(
|
|
3758
|
-
return IsObjectPropertyCount(
|
|
3846
|
+
function IsObjectSymbolLike(schema) {
|
|
3847
|
+
return IsObjectPropertyCount(schema, 0) || IsObjectPropertyCount(schema, 1) && "description" in schema.properties && type_exports.IsUnion(schema.properties.description) && schema.properties.description.anyOf.length === 2 && (type_exports.IsString(schema.properties.description.anyOf[0]) && type_exports.IsUndefined(schema.properties.description.anyOf[1]) || type_exports.IsString(schema.properties.description.anyOf[1]) && type_exports.IsUndefined(schema.properties.description.anyOf[0]));
|
|
3759
3848
|
}
|
|
3760
|
-
function IsObjectNumberLike(
|
|
3761
|
-
return IsObjectPropertyCount(
|
|
3849
|
+
function IsObjectNumberLike(schema) {
|
|
3850
|
+
return IsObjectPropertyCount(schema, 0);
|
|
3762
3851
|
}
|
|
3763
|
-
function IsObjectBooleanLike(
|
|
3764
|
-
return IsObjectPropertyCount(
|
|
3852
|
+
function IsObjectBooleanLike(schema) {
|
|
3853
|
+
return IsObjectPropertyCount(schema, 0);
|
|
3765
3854
|
}
|
|
3766
|
-
function IsObjectBigIntLike(
|
|
3767
|
-
return IsObjectPropertyCount(
|
|
3855
|
+
function IsObjectBigIntLike(schema) {
|
|
3856
|
+
return IsObjectPropertyCount(schema, 0);
|
|
3768
3857
|
}
|
|
3769
|
-
function IsObjectDateLike(
|
|
3770
|
-
return IsObjectPropertyCount(
|
|
3858
|
+
function IsObjectDateLike(schema) {
|
|
3859
|
+
return IsObjectPropertyCount(schema, 0);
|
|
3771
3860
|
}
|
|
3772
|
-
function IsObjectUint8ArrayLike(
|
|
3773
|
-
return IsObjectArrayLike(
|
|
3861
|
+
function IsObjectUint8ArrayLike(schema) {
|
|
3862
|
+
return IsObjectArrayLike(schema);
|
|
3774
3863
|
}
|
|
3775
|
-
function IsObjectFunctionLike(
|
|
3864
|
+
function IsObjectFunctionLike(schema) {
|
|
3776
3865
|
const length = Number2();
|
|
3777
|
-
return IsObjectPropertyCount(
|
|
3866
|
+
return IsObjectPropertyCount(schema, 0) || IsObjectPropertyCount(schema, 1) && "length" in schema.properties && IntoBooleanResult(Visit3(schema.properties["length"], length)) === ExtendsResult.True;
|
|
3778
3867
|
}
|
|
3779
|
-
function IsObjectConstructorLike(
|
|
3780
|
-
return IsObjectPropertyCount(
|
|
3868
|
+
function IsObjectConstructorLike(schema) {
|
|
3869
|
+
return IsObjectPropertyCount(schema, 0);
|
|
3781
3870
|
}
|
|
3782
|
-
function IsObjectArrayLike(
|
|
3871
|
+
function IsObjectArrayLike(schema) {
|
|
3783
3872
|
const length = Number2();
|
|
3784
|
-
return IsObjectPropertyCount(
|
|
3873
|
+
return IsObjectPropertyCount(schema, 0) || IsObjectPropertyCount(schema, 1) && "length" in schema.properties && IntoBooleanResult(Visit3(schema.properties["length"], length)) === ExtendsResult.True;
|
|
3785
3874
|
}
|
|
3786
|
-
function IsObjectPromiseLike(
|
|
3875
|
+
function IsObjectPromiseLike(schema) {
|
|
3787
3876
|
const then = Function([Any()], Any());
|
|
3788
|
-
return IsObjectPropertyCount(
|
|
3877
|
+
return IsObjectPropertyCount(schema, 0) || IsObjectPropertyCount(schema, 1) && "then" in schema.properties && IntoBooleanResult(Visit3(schema.properties["then"], then)) === ExtendsResult.True;
|
|
3789
3878
|
}
|
|
3790
3879
|
function Property(left, right) {
|
|
3791
3880
|
return Visit3(left, right) === ExtendsResult.False ? ExtendsResult.False : type_exports.IsOptional(left) && !type_exports.IsOptional(right) ? ExtendsResult.False : ExtendsResult.True;
|
|
@@ -3816,11 +3905,11 @@ function FromObject(left, right) {
|
|
|
3816
3905
|
function FromPromise2(left, right) {
|
|
3817
3906
|
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) && IsObjectPromiseLike(right) ? ExtendsResult.True : !type_exports.IsPromise(right) ? ExtendsResult.False : IntoBooleanResult(Visit3(left.item, right.item));
|
|
3818
3907
|
}
|
|
3819
|
-
function RecordKey(
|
|
3820
|
-
return PatternNumberExact in
|
|
3908
|
+
function RecordKey(schema) {
|
|
3909
|
+
return PatternNumberExact in schema.patternProperties ? Number2() : PatternStringExact in schema.patternProperties ? String2() : Throw("Unknown record key pattern");
|
|
3821
3910
|
}
|
|
3822
|
-
function RecordValue(
|
|
3823
|
-
return PatternNumberExact in
|
|
3911
|
+
function RecordValue(schema) {
|
|
3912
|
+
return PatternNumberExact in schema.patternProperties ? schema.patternProperties[PatternNumberExact] : PatternStringExact in schema.patternProperties ? schema.patternProperties[PatternStringExact] : Throw("Unable to get record value schema");
|
|
3824
3913
|
}
|
|
3825
3914
|
function FromRecordRight(left, right) {
|
|
3826
3915
|
const [Key, Value] = [RecordKey(right), RecordValue(right)];
|
|
@@ -3854,13 +3943,13 @@ function FromTemplateLiteral2(left, right) {
|
|
|
3854
3943
|
return type_exports.IsTemplateLiteral(left) ? Visit3(TemplateLiteralToUnion(left), right) : type_exports.IsTemplateLiteral(right) ? Visit3(left, TemplateLiteralToUnion(right)) : Throw("Invalid fallthrough for TemplateLiteral");
|
|
3855
3944
|
}
|
|
3856
3945
|
function IsArrayOfTuple(left, right) {
|
|
3857
|
-
return type_exports.IsArray(right) && left.items !== void 0 && left.items.every((
|
|
3946
|
+
return type_exports.IsArray(right) && left.items !== void 0 && left.items.every((schema) => Visit3(schema, right.items) === ExtendsResult.True);
|
|
3858
3947
|
}
|
|
3859
3948
|
function FromTupleRight(left, right) {
|
|
3860
3949
|
return type_exports.IsNever(left) ? ExtendsResult.True : type_exports.IsUnknown(left) ? ExtendsResult.False : type_exports.IsAny(left) ? ExtendsResult.Union : ExtendsResult.False;
|
|
3861
3950
|
}
|
|
3862
3951
|
function FromTuple3(left, right) {
|
|
3863
|
-
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) && IsObjectArrayLike(right) ? ExtendsResult.True : type_exports.IsArray(right) && IsArrayOfTuple(left, right) ? ExtendsResult.True : !type_exports.IsTuple(right) ? ExtendsResult.False : value_exports.IsUndefined(left.items) && !value_exports.IsUndefined(right.items) || !value_exports.IsUndefined(left.items) && value_exports.IsUndefined(right.items) ? ExtendsResult.False : value_exports.IsUndefined(left.items) && !value_exports.IsUndefined(right.items) ? ExtendsResult.True : left.items.every((
|
|
3952
|
+
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) && IsObjectArrayLike(right) ? ExtendsResult.True : type_exports.IsArray(right) && IsArrayOfTuple(left, right) ? ExtendsResult.True : !type_exports.IsTuple(right) ? ExtendsResult.False : value_exports.IsUndefined(left.items) && !value_exports.IsUndefined(right.items) || !value_exports.IsUndefined(left.items) && value_exports.IsUndefined(right.items) ? ExtendsResult.False : value_exports.IsUndefined(left.items) && !value_exports.IsUndefined(right.items) ? ExtendsResult.True : left.items.every((schema, index9) => Visit3(schema, right.items[index9]) === ExtendsResult.True) ? ExtendsResult.True : ExtendsResult.False;
|
|
3864
3953
|
}
|
|
3865
3954
|
function FromUint8Array(left, right) {
|
|
3866
3955
|
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : type_exports.IsRecord(right) ? FromRecordRight(left, right) : type_exports.IsUint8Array(right) ? ExtendsResult.True : ExtendsResult.False;
|
|
@@ -3869,10 +3958,10 @@ function FromUndefined(left, right) {
|
|
|
3869
3958
|
return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : type_exports.IsRecord(right) ? FromRecordRight(left, right) : type_exports.IsVoid(right) ? FromVoidRight(left, right) : type_exports.IsUndefined(right) ? ExtendsResult.True : ExtendsResult.False;
|
|
3870
3959
|
}
|
|
3871
3960
|
function FromUnionRight(left, right) {
|
|
3872
|
-
return right.anyOf.some((
|
|
3961
|
+
return right.anyOf.some((schema) => Visit3(left, schema) === ExtendsResult.True) ? ExtendsResult.True : ExtendsResult.False;
|
|
3873
3962
|
}
|
|
3874
3963
|
function FromUnion6(left, right) {
|
|
3875
|
-
return left.anyOf.every((
|
|
3964
|
+
return left.anyOf.every((schema) => Visit3(schema, right) === ExtendsResult.True) ? ExtendsResult.True : ExtendsResult.False;
|
|
3876
3965
|
}
|
|
3877
3966
|
function FromUnknownRight(left, right) {
|
|
3878
3967
|
return ExtendsResult.True;
|
|
@@ -4009,13 +4098,13 @@ function ExtractFromMappedResult(R, T) {
|
|
|
4009
4098
|
}
|
|
4010
4099
|
|
|
4011
4100
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/instance-type/instance-type.mjs
|
|
4012
|
-
function InstanceType(
|
|
4013
|
-
return IsConstructor(
|
|
4101
|
+
function InstanceType(schema, options) {
|
|
4102
|
+
return IsConstructor(schema) ? CreateType(schema.returns, options) : Never(options);
|
|
4014
4103
|
}
|
|
4015
4104
|
|
|
4016
4105
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/readonly-optional/readonly-optional.mjs
|
|
4017
|
-
function ReadonlyOptional(
|
|
4018
|
-
return Readonly(Optional(
|
|
4106
|
+
function ReadonlyOptional(schema) {
|
|
4107
|
+
return Readonly(Optional(schema));
|
|
4019
4108
|
}
|
|
4020
4109
|
|
|
4021
4110
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/record/record.mjs
|
|
@@ -4188,11 +4277,11 @@ function ApplyUppercase(value) {
|
|
|
4188
4277
|
function ApplyLowercase(value) {
|
|
4189
4278
|
return value.toLowerCase();
|
|
4190
4279
|
}
|
|
4191
|
-
function FromTemplateLiteral3(
|
|
4192
|
-
const expression = TemplateLiteralParseExact(
|
|
4280
|
+
function FromTemplateLiteral3(schema, mode, options) {
|
|
4281
|
+
const expression = TemplateLiteralParseExact(schema.pattern);
|
|
4193
4282
|
const finite = IsTemplateLiteralExpressionFinite(expression);
|
|
4194
4283
|
if (!finite)
|
|
4195
|
-
return { ...
|
|
4284
|
+
return { ...schema, pattern: FromLiteralValue(schema.pattern, mode) };
|
|
4196
4285
|
const strings = [...TemplateLiteralExpressionGenerate(expression)];
|
|
4197
4286
|
const literals = strings.map((value) => Literal(value));
|
|
4198
4287
|
const mapped = FromRest5(literals, mode);
|
|
@@ -4205,14 +4294,14 @@ function FromLiteralValue(value, mode) {
|
|
|
4205
4294
|
function FromRest5(T, M) {
|
|
4206
4295
|
return T.map((L) => Intrinsic(L, M));
|
|
4207
4296
|
}
|
|
4208
|
-
function Intrinsic(
|
|
4297
|
+
function Intrinsic(schema, mode, options = {}) {
|
|
4209
4298
|
return (
|
|
4210
4299
|
// Intrinsic-Mapped-Inference
|
|
4211
|
-
IsMappedKey(
|
|
4300
|
+
IsMappedKey(schema) ? IntrinsicFromMappedKey(schema, mode, options) : (
|
|
4212
4301
|
// Standard-Inference
|
|
4213
|
-
IsTemplateLiteral(
|
|
4302
|
+
IsTemplateLiteral(schema) ? FromTemplateLiteral3(schema, mode, options) : IsUnion(schema) ? Union(FromRest5(schema.anyOf, mode), options) : IsLiteral(schema) ? Literal(FromLiteralValue(schema.const, mode), options) : (
|
|
4214
4303
|
// Default Type
|
|
4215
|
-
CreateType(
|
|
4304
|
+
CreateType(schema, options)
|
|
4216
4305
|
)
|
|
4217
4306
|
)
|
|
4218
4307
|
);
|
|
@@ -4609,8 +4698,8 @@ function Not(type, options) {
|
|
|
4609
4698
|
}
|
|
4610
4699
|
|
|
4611
4700
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/parameters/parameters.mjs
|
|
4612
|
-
function Parameters(
|
|
4613
|
-
return IsFunction2(
|
|
4701
|
+
function Parameters(schema, options) {
|
|
4702
|
+
return IsFunction2(schema) ? Tuple(schema.parameters, options) : Never();
|
|
4614
4703
|
}
|
|
4615
4704
|
|
|
4616
4705
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/recursive/recursive.mjs
|
|
@@ -4638,40 +4727,40 @@ function Rest(T) {
|
|
|
4638
4727
|
}
|
|
4639
4728
|
|
|
4640
4729
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/return-type/return-type.mjs
|
|
4641
|
-
function ReturnType(
|
|
4642
|
-
return IsFunction2(
|
|
4730
|
+
function ReturnType(schema, options) {
|
|
4731
|
+
return IsFunction2(schema) ? CreateType(schema.returns, options) : Never(options);
|
|
4643
4732
|
}
|
|
4644
4733
|
|
|
4645
4734
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/transform/transform.mjs
|
|
4646
4735
|
var TransformDecodeBuilder = class {
|
|
4647
|
-
constructor(
|
|
4648
|
-
this.schema =
|
|
4736
|
+
constructor(schema) {
|
|
4737
|
+
this.schema = schema;
|
|
4649
4738
|
}
|
|
4650
4739
|
Decode(decode) {
|
|
4651
4740
|
return new TransformEncodeBuilder(this.schema, decode);
|
|
4652
4741
|
}
|
|
4653
4742
|
};
|
|
4654
4743
|
var TransformEncodeBuilder = class {
|
|
4655
|
-
constructor(
|
|
4656
|
-
this.schema =
|
|
4744
|
+
constructor(schema, decode) {
|
|
4745
|
+
this.schema = schema;
|
|
4657
4746
|
this.decode = decode;
|
|
4658
4747
|
}
|
|
4659
|
-
EncodeTransform(encode,
|
|
4660
|
-
const Encode = (value) =>
|
|
4661
|
-
const Decode = (value) => this.decode(
|
|
4748
|
+
EncodeTransform(encode, schema) {
|
|
4749
|
+
const Encode = (value) => schema[TransformKind].Encode(encode(value));
|
|
4750
|
+
const Decode = (value) => this.decode(schema[TransformKind].Decode(value));
|
|
4662
4751
|
const Codec = { Encode, Decode };
|
|
4663
|
-
return { ...
|
|
4752
|
+
return { ...schema, [TransformKind]: Codec };
|
|
4664
4753
|
}
|
|
4665
|
-
EncodeSchema(encode,
|
|
4754
|
+
EncodeSchema(encode, schema) {
|
|
4666
4755
|
const Codec = { Decode: this.decode, Encode: encode };
|
|
4667
|
-
return { ...
|
|
4756
|
+
return { ...schema, [TransformKind]: Codec };
|
|
4668
4757
|
}
|
|
4669
4758
|
Encode(encode) {
|
|
4670
4759
|
return IsTransform(this.schema) ? this.EncodeTransform(encode, this.schema) : this.EncodeSchema(encode, this.schema);
|
|
4671
4760
|
}
|
|
4672
4761
|
};
|
|
4673
|
-
function Transform(
|
|
4674
|
-
return new TransformDecodeBuilder(
|
|
4762
|
+
function Transform(schema) {
|
|
4763
|
+
return new TransformDecodeBuilder(schema);
|
|
4675
4764
|
}
|
|
4676
4765
|
|
|
4677
4766
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/unsafe/unsafe.mjs
|
|
@@ -5049,6 +5138,33 @@ var changePasswordContract = {
|
|
|
5049
5138
|
})
|
|
5050
5139
|
)
|
|
5051
5140
|
};
|
|
5141
|
+
var getMeContract = {
|
|
5142
|
+
method: "GET",
|
|
5143
|
+
path: "/_auth/me",
|
|
5144
|
+
body: Type.Object({}),
|
|
5145
|
+
response: ApiResponseSchema(
|
|
5146
|
+
Type.Object({
|
|
5147
|
+
userId: Type.String({ description: "User ID" }),
|
|
5148
|
+
email: Type.Optional(Type.String({ description: "User email address" })),
|
|
5149
|
+
phone: Type.Optional(Type.String({ description: "User phone number" })),
|
|
5150
|
+
role: Type.Object({
|
|
5151
|
+
id: Type.Number({ description: "Role ID" }),
|
|
5152
|
+
name: Type.String({ description: "Role name (e.g., user, admin)" }),
|
|
5153
|
+
displayName: Type.String({ description: "Display name for UI" }),
|
|
5154
|
+
priority: Type.Number({ description: "Role priority level" })
|
|
5155
|
+
}, { description: "User role information" }),
|
|
5156
|
+
permissions: Type.Array(
|
|
5157
|
+
Type.Object({
|
|
5158
|
+
id: Type.Number({ description: "Permission ID" }),
|
|
5159
|
+
name: Type.String({ description: "Permission name (e.g., user:delete)" }),
|
|
5160
|
+
displayName: Type.String({ description: "Display name for UI" }),
|
|
5161
|
+
category: Type.Optional(Type.String({ description: "Permission category" }))
|
|
5162
|
+
}),
|
|
5163
|
+
{ description: "List of permissions granted through role" }
|
|
5164
|
+
)
|
|
5165
|
+
})
|
|
5166
|
+
)
|
|
5167
|
+
};
|
|
5052
5168
|
|
|
5053
5169
|
// src/lib/contracts/invitation.ts
|
|
5054
5170
|
var UUID_PATTERN2 = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
|
|
@@ -5282,252 +5398,45 @@ var deleteInvitationContract = {
|
|
|
5282
5398
|
)
|
|
5283
5399
|
};
|
|
5284
5400
|
|
|
5285
|
-
// src/server/helpers/
|
|
5286
|
-
import
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
process.env.
|
|
5290
|
-
"
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
const publicKeyDER = Buffer.from(publicKeyB64, "base64");
|
|
5297
|
-
const publicKeyObject = crypto.createPublicKey({
|
|
5298
|
-
key: publicKeyDER,
|
|
5299
|
-
format: "der",
|
|
5300
|
-
type: "spki"
|
|
5301
|
-
});
|
|
5302
|
-
const decoded = jwt.verify(token, publicKeyObject, {
|
|
5303
|
-
algorithms: [algorithm],
|
|
5304
|
-
// Prevent algorithm confusion attacks
|
|
5305
|
-
issuer: "spfn-client"
|
|
5306
|
-
// Validate token issuer
|
|
5307
|
-
});
|
|
5308
|
-
if (typeof decoded === "string") {
|
|
5309
|
-
throw new Error("Invalid token format: expected object payload");
|
|
5310
|
-
}
|
|
5311
|
-
return decoded;
|
|
5312
|
-
} catch (error) {
|
|
5313
|
-
if (error instanceof jwt.TokenExpiredError) {
|
|
5314
|
-
throw new Error("Token has expired");
|
|
5315
|
-
}
|
|
5316
|
-
if (error instanceof jwt.JsonWebTokenError) {
|
|
5317
|
-
throw new Error("Invalid token signature");
|
|
5318
|
-
}
|
|
5319
|
-
throw new Error(`Token verification failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
5401
|
+
// src/server/helpers/password.ts
|
|
5402
|
+
import bcrypt from "bcrypt";
|
|
5403
|
+
var SALT_ROUNDS = parseInt(
|
|
5404
|
+
process.env.SPFN_AUTH_BCRYPT_SALT_ROUNDS || // New prefixed version (recommended)
|
|
5405
|
+
process.env.BCRYPT_SALT_ROUNDS || // Legacy fallback
|
|
5406
|
+
"10",
|
|
5407
|
+
10
|
|
5408
|
+
);
|
|
5409
|
+
async function hashPassword(password) {
|
|
5410
|
+
if (!password || password.length === 0) {
|
|
5411
|
+
throw new Error("Password cannot be empty");
|
|
5320
5412
|
}
|
|
5413
|
+
return bcrypt.hash(password, SALT_ROUNDS);
|
|
5321
5414
|
}
|
|
5322
|
-
function
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
console.error("Failed to verify key fingerprint:", error);
|
|
5329
|
-
return false;
|
|
5415
|
+
async function verifyPassword(password, hash) {
|
|
5416
|
+
if (!password || password.length === 0) {
|
|
5417
|
+
throw new Error("Password cannot be empty");
|
|
5418
|
+
}
|
|
5419
|
+
if (!hash || hash.length === 0) {
|
|
5420
|
+
throw new Error("Hash cannot be empty");
|
|
5330
5421
|
}
|
|
5422
|
+
return bcrypt.compare(password, hash);
|
|
5331
5423
|
}
|
|
5332
5424
|
|
|
5333
|
-
// src/server/
|
|
5334
|
-
|
|
5335
|
-
import { findOne, getDatabase } from "@spfn/core/db";
|
|
5425
|
+
// src/server/helpers/index.ts
|
|
5426
|
+
init_jwt();
|
|
5336
5427
|
|
|
5337
|
-
// src/server/
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
};
|
|
5350
|
-
var InvalidTokenError = class extends UnauthorizedError {
|
|
5351
|
-
constructor(message = "Invalid authentication token") {
|
|
5352
|
-
super(message);
|
|
5353
|
-
this.name = "InvalidTokenError";
|
|
5354
|
-
}
|
|
5355
|
-
};
|
|
5356
|
-
var TokenExpiredError = class extends UnauthorizedError {
|
|
5357
|
-
constructor(message = "Authentication token has expired") {
|
|
5358
|
-
super(message);
|
|
5359
|
-
this.name = "TokenExpiredError";
|
|
5360
|
-
}
|
|
5361
|
-
};
|
|
5362
|
-
var KeyExpiredError = class extends UnauthorizedError {
|
|
5363
|
-
constructor(message = "Public key has expired") {
|
|
5364
|
-
super(message);
|
|
5365
|
-
this.name = "KeyExpiredError";
|
|
5366
|
-
}
|
|
5367
|
-
};
|
|
5368
|
-
var AccountDisabledError = class extends ForbiddenError {
|
|
5369
|
-
constructor(status = "disabled") {
|
|
5370
|
-
super(`Account is ${status}`);
|
|
5371
|
-
this.name = "AccountDisabledError";
|
|
5372
|
-
this.details = { status };
|
|
5373
|
-
}
|
|
5374
|
-
};
|
|
5375
|
-
var AccountAlreadyExistsError = class extends ConflictError {
|
|
5376
|
-
constructor(identifier, identifierType) {
|
|
5377
|
-
super("Account already exists");
|
|
5378
|
-
this.name = "AccountAlreadyExistsError";
|
|
5379
|
-
this.details = { identifier, identifierType };
|
|
5380
|
-
}
|
|
5381
|
-
};
|
|
5382
|
-
var InvalidVerificationCodeError = class extends ValidationError {
|
|
5383
|
-
constructor(reason = "Invalid verification code") {
|
|
5384
|
-
super(reason);
|
|
5385
|
-
this.name = "InvalidVerificationCodeError";
|
|
5386
|
-
}
|
|
5387
|
-
};
|
|
5388
|
-
var InvalidVerificationTokenError = class extends ValidationError {
|
|
5389
|
-
constructor(message = "Invalid or expired verification token") {
|
|
5390
|
-
super(message);
|
|
5391
|
-
this.name = "InvalidVerificationTokenError";
|
|
5392
|
-
}
|
|
5393
|
-
};
|
|
5394
|
-
var InvalidKeyFingerprintError = class extends ValidationError {
|
|
5395
|
-
constructor(message = "Invalid key fingerprint") {
|
|
5396
|
-
super(message);
|
|
5397
|
-
this.name = "InvalidKeyFingerprintError";
|
|
5398
|
-
}
|
|
5399
|
-
};
|
|
5400
|
-
var VerificationTokenPurposeMismatchError = class extends ValidationError {
|
|
5401
|
-
constructor(expected, actual) {
|
|
5402
|
-
super(`Verification token is for ${actual}, but ${expected} was expected`);
|
|
5403
|
-
this.name = "VerificationTokenPurposeMismatchError";
|
|
5404
|
-
this.details = { expected, actual };
|
|
5405
|
-
}
|
|
5406
|
-
};
|
|
5407
|
-
var VerificationTokenTargetMismatchError = class extends ValidationError {
|
|
5408
|
-
constructor() {
|
|
5409
|
-
super("Verification token does not match provided email/phone");
|
|
5410
|
-
this.name = "VerificationTokenTargetMismatchError";
|
|
5411
|
-
}
|
|
5412
|
-
};
|
|
5413
|
-
|
|
5414
|
-
// src/server/middleware/authenticate.ts
|
|
5415
|
-
import { UnauthorizedError as UnauthorizedError2 } from "@spfn/core/errors";
|
|
5416
|
-
import { eq, and } from "drizzle-orm";
|
|
5417
|
-
async function authenticate(c, next) {
|
|
5418
|
-
const authHeader = c.req.header("Authorization");
|
|
5419
|
-
const keyId = c.req.header("X-Key-Id");
|
|
5420
|
-
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
5421
|
-
throw new UnauthorizedError2("Missing or invalid authorization header");
|
|
5422
|
-
}
|
|
5423
|
-
if (!keyId) {
|
|
5424
|
-
throw new UnauthorizedError2("Missing X-Key-Id header");
|
|
5425
|
-
}
|
|
5426
|
-
const token = authHeader.substring(7);
|
|
5427
|
-
const db = getDatabase();
|
|
5428
|
-
const [keyRecord] = await db.select().from(userPublicKeys).where(
|
|
5429
|
-
and(
|
|
5430
|
-
eq(userPublicKeys.keyId, keyId),
|
|
5431
|
-
eq(userPublicKeys.isActive, true)
|
|
5432
|
-
)
|
|
5433
|
-
);
|
|
5434
|
-
if (!keyRecord) {
|
|
5435
|
-
throw new UnauthorizedError2("Invalid or revoked key");
|
|
5436
|
-
}
|
|
5437
|
-
if (keyRecord.expiresAt && /* @__PURE__ */ new Date() > keyRecord.expiresAt) {
|
|
5438
|
-
throw new KeyExpiredError();
|
|
5439
|
-
}
|
|
5440
|
-
try {
|
|
5441
|
-
verifyClientToken(
|
|
5442
|
-
token,
|
|
5443
|
-
keyRecord.publicKey,
|
|
5444
|
-
keyRecord.algorithm
|
|
5445
|
-
);
|
|
5446
|
-
} catch (err) {
|
|
5447
|
-
if (err instanceof Error) {
|
|
5448
|
-
if (err.name === "TokenExpiredError") {
|
|
5449
|
-
throw new TokenExpiredError();
|
|
5450
|
-
}
|
|
5451
|
-
if (err.name === "JsonWebTokenError") {
|
|
5452
|
-
throw new InvalidTokenError("Invalid token signature");
|
|
5453
|
-
}
|
|
5454
|
-
}
|
|
5455
|
-
throw new UnauthorizedError2("Authentication failed");
|
|
5456
|
-
}
|
|
5457
|
-
const user = await findOne(users, { id: keyRecord.userId });
|
|
5458
|
-
if (!user) {
|
|
5459
|
-
throw new UnauthorizedError2("User not found");
|
|
5460
|
-
}
|
|
5461
|
-
if (user.status !== "active") {
|
|
5462
|
-
throw new AccountDisabledError(user.status);
|
|
5463
|
-
}
|
|
5464
|
-
db.update(userPublicKeys).set({ lastUsedAt: /* @__PURE__ */ new Date() }).where(eq(userPublicKeys.id, keyRecord.id)).execute().catch((err) => console.error("Failed to update lastUsedAt:", err));
|
|
5465
|
-
c.set("auth", {
|
|
5466
|
-
user,
|
|
5467
|
-
userId: String(user.id),
|
|
5468
|
-
keyId
|
|
5469
|
-
});
|
|
5470
|
-
await next();
|
|
5471
|
-
}
|
|
5472
|
-
|
|
5473
|
-
// src/server/helpers/context.ts
|
|
5474
|
-
function getAuth(c) {
|
|
5475
|
-
if ("raw" in c && c.raw) {
|
|
5476
|
-
return c.raw.get("auth");
|
|
5477
|
-
}
|
|
5478
|
-
return c.get("auth");
|
|
5479
|
-
}
|
|
5480
|
-
function getUser(c) {
|
|
5481
|
-
return getAuth(c).user;
|
|
5482
|
-
}
|
|
5483
|
-
|
|
5484
|
-
// src/server/services/permission.service.ts
|
|
5485
|
-
init_entities();
|
|
5486
|
-
import { getDatabase as getDatabase2 } from "@spfn/core/db";
|
|
5487
|
-
import { eq as eq2, and as and2 } from "drizzle-orm";
|
|
5488
|
-
|
|
5489
|
-
// src/server/middleware/require-permission.ts
|
|
5490
|
-
import { ForbiddenError as ForbiddenError2 } from "@spfn/core/errors";
|
|
5491
|
-
|
|
5492
|
-
// src/server/middleware/require-role.ts
|
|
5493
|
-
import { ForbiddenError as ForbiddenError3 } from "@spfn/core/errors";
|
|
5494
|
-
|
|
5495
|
-
// src/server/helpers/password.ts
|
|
5496
|
-
import bcrypt from "bcrypt";
|
|
5497
|
-
var SALT_ROUNDS = parseInt(
|
|
5498
|
-
process.env.SPFN_AUTH_BCRYPT_SALT_ROUNDS || // New prefixed version (recommended)
|
|
5499
|
-
process.env.BCRYPT_SALT_ROUNDS || // Legacy fallback
|
|
5500
|
-
"10",
|
|
5501
|
-
10
|
|
5502
|
-
);
|
|
5503
|
-
async function hashPassword(password) {
|
|
5504
|
-
if (!password || password.length === 0) {
|
|
5505
|
-
throw new Error("Password cannot be empty");
|
|
5506
|
-
}
|
|
5507
|
-
return bcrypt.hash(password, SALT_ROUNDS);
|
|
5508
|
-
}
|
|
5509
|
-
async function verifyPassword(password, hash) {
|
|
5510
|
-
if (!password || password.length === 0) {
|
|
5511
|
-
throw new Error("Password cannot be empty");
|
|
5512
|
-
}
|
|
5513
|
-
if (!hash || hash.length === 0) {
|
|
5514
|
-
throw new Error("Hash cannot be empty");
|
|
5515
|
-
}
|
|
5516
|
-
return bcrypt.compare(password, hash);
|
|
5517
|
-
}
|
|
5518
|
-
|
|
5519
|
-
// src/server/helpers/verification.ts
|
|
5520
|
-
init_verification_codes();
|
|
5521
|
-
import jwt2 from "jsonwebtoken";
|
|
5522
|
-
import { getDatabase as getDatabase3, create } from "@spfn/core/db";
|
|
5523
|
-
import { eq as eq3, and as and3 } from "drizzle-orm";
|
|
5524
|
-
function getVerificationTokenSecret() {
|
|
5525
|
-
const secret = process.env.SPFN_AUTH_VERIFICATION_TOKEN_SECRET || // New prefixed version (recommended)
|
|
5526
|
-
process.env.VERIFICATION_TOKEN_SECRET || // Legacy fallback
|
|
5527
|
-
process.env.SPFN_AUTH_JWT_SECRET || // New JWT secret fallback
|
|
5528
|
-
process.env.JWT_SECRET;
|
|
5529
|
-
if (!secret || secret.length < 32) {
|
|
5530
|
-
throw new Error("SPFN_AUTH_VERIFICATION_TOKEN_SECRET must be at least 32 characters long");
|
|
5428
|
+
// src/server/helpers/verification.ts
|
|
5429
|
+
init_verification_codes();
|
|
5430
|
+
import jwt2 from "jsonwebtoken";
|
|
5431
|
+
import { getDatabase, create } from "@spfn/core/db";
|
|
5432
|
+
import { eq, and } from "drizzle-orm";
|
|
5433
|
+
function getVerificationTokenSecret() {
|
|
5434
|
+
const secret = process.env.SPFN_AUTH_VERIFICATION_TOKEN_SECRET || // New prefixed version (recommended)
|
|
5435
|
+
process.env.VERIFICATION_TOKEN_SECRET || // Legacy fallback
|
|
5436
|
+
process.env.SPFN_AUTH_JWT_SECRET || // New JWT secret fallback
|
|
5437
|
+
process.env.JWT_SECRET;
|
|
5438
|
+
if (!secret || secret.length < 32) {
|
|
5439
|
+
throw new Error("SPFN_AUTH_VERIFICATION_TOKEN_SECRET must be at least 32 characters long");
|
|
5531
5440
|
}
|
|
5532
5441
|
return secret;
|
|
5533
5442
|
}
|
|
@@ -5539,7 +5448,7 @@ function generateVerificationCode() {
|
|
|
5539
5448
|
return code;
|
|
5540
5449
|
}
|
|
5541
5450
|
async function storeVerificationCode(target, targetType, code, purpose) {
|
|
5542
|
-
const db =
|
|
5451
|
+
const db = getDatabase();
|
|
5543
5452
|
if (!db) {
|
|
5544
5453
|
throw new Error("Database not initialized");
|
|
5545
5454
|
}
|
|
@@ -5556,16 +5465,16 @@ async function storeVerificationCode(target, targetType, code, purpose) {
|
|
|
5556
5465
|
return record;
|
|
5557
5466
|
}
|
|
5558
5467
|
async function validateVerificationCode(target, targetType, code, purpose) {
|
|
5559
|
-
const db =
|
|
5468
|
+
const db = getDatabase();
|
|
5560
5469
|
if (!db) {
|
|
5561
5470
|
throw new Error("Database not initialized");
|
|
5562
5471
|
}
|
|
5563
5472
|
const records = await db.select().from(verificationCodes).where(
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5473
|
+
and(
|
|
5474
|
+
eq(verificationCodes.target, target),
|
|
5475
|
+
eq(verificationCodes.targetType, targetType),
|
|
5476
|
+
eq(verificationCodes.code, code),
|
|
5477
|
+
eq(verificationCodes.purpose, purpose)
|
|
5569
5478
|
)
|
|
5570
5479
|
).limit(1);
|
|
5571
5480
|
if (records.length === 0) {
|
|
@@ -5582,15 +5491,15 @@ async function validateVerificationCode(target, targetType, code, purpose) {
|
|
|
5582
5491
|
if (attempts >= MAX_VERIFICATION_ATTEMPTS) {
|
|
5583
5492
|
return { valid: false, error: "Too many attempts, please request a new code" };
|
|
5584
5493
|
}
|
|
5585
|
-
await db.update(verificationCodes).set({ attempts: (attempts + 1).toString() }).where(
|
|
5494
|
+
await db.update(verificationCodes).set({ attempts: (attempts + 1).toString() }).where(eq(verificationCodes.id, record.id));
|
|
5586
5495
|
return { valid: true, codeId: record.id };
|
|
5587
5496
|
}
|
|
5588
5497
|
async function markCodeAsUsed(codeId) {
|
|
5589
|
-
const db =
|
|
5498
|
+
const db = getDatabase();
|
|
5590
5499
|
if (!db) {
|
|
5591
5500
|
throw new Error("Database not initialized");
|
|
5592
5501
|
}
|
|
5593
|
-
await db.update(verificationCodes).set({ usedAt: /* @__PURE__ */ new Date() }).where(
|
|
5502
|
+
await db.update(verificationCodes).set({ usedAt: /* @__PURE__ */ new Date() }).where(eq(verificationCodes.id, codeId));
|
|
5594
5503
|
}
|
|
5595
5504
|
function createVerificationToken(payload) {
|
|
5596
5505
|
const secret = getVerificationTokenSecret();
|
|
@@ -5623,15 +5532,101 @@ async function sendVerificationSMS(phone, code, purpose) {
|
|
|
5623
5532
|
console.log(`[VERIFICATION SMS] To: ${phone}, Code: ${code}, Purpose: ${purpose}`);
|
|
5624
5533
|
}
|
|
5625
5534
|
|
|
5535
|
+
// src/server/helpers/context.ts
|
|
5536
|
+
function getAuth(c) {
|
|
5537
|
+
if ("raw" in c && c.raw) {
|
|
5538
|
+
return c.raw.get("auth");
|
|
5539
|
+
}
|
|
5540
|
+
return c.get("auth");
|
|
5541
|
+
}
|
|
5542
|
+
function getUser(c) {
|
|
5543
|
+
return getAuth(c).user;
|
|
5544
|
+
}
|
|
5545
|
+
|
|
5626
5546
|
// src/server/services/auth.service.ts
|
|
5627
5547
|
init_entities();
|
|
5628
|
-
import { findOne as
|
|
5548
|
+
import { findOne as findOne2, create as create3 } from "@spfn/core/db";
|
|
5629
5549
|
import { ValidationError as ValidationError2 } from "@spfn/core/errors";
|
|
5630
5550
|
|
|
5551
|
+
// src/server/errors/auth-errors.ts
|
|
5552
|
+
import {
|
|
5553
|
+
ValidationError,
|
|
5554
|
+
UnauthorizedError,
|
|
5555
|
+
ForbiddenError,
|
|
5556
|
+
ConflictError
|
|
5557
|
+
} from "@spfn/core/errors";
|
|
5558
|
+
var InvalidCredentialsError = class extends UnauthorizedError {
|
|
5559
|
+
constructor(message = "Invalid credentials") {
|
|
5560
|
+
super(message);
|
|
5561
|
+
this.name = "InvalidCredentialsError";
|
|
5562
|
+
}
|
|
5563
|
+
};
|
|
5564
|
+
var InvalidTokenError = class extends UnauthorizedError {
|
|
5565
|
+
constructor(message = "Invalid authentication token") {
|
|
5566
|
+
super(message);
|
|
5567
|
+
this.name = "InvalidTokenError";
|
|
5568
|
+
}
|
|
5569
|
+
};
|
|
5570
|
+
var TokenExpiredError = class extends UnauthorizedError {
|
|
5571
|
+
constructor(message = "Authentication token has expired") {
|
|
5572
|
+
super(message);
|
|
5573
|
+
this.name = "TokenExpiredError";
|
|
5574
|
+
}
|
|
5575
|
+
};
|
|
5576
|
+
var KeyExpiredError = class extends UnauthorizedError {
|
|
5577
|
+
constructor(message = "Public key has expired") {
|
|
5578
|
+
super(message);
|
|
5579
|
+
this.name = "KeyExpiredError";
|
|
5580
|
+
}
|
|
5581
|
+
};
|
|
5582
|
+
var AccountDisabledError = class extends ForbiddenError {
|
|
5583
|
+
constructor(status = "disabled") {
|
|
5584
|
+
super(`Account is ${status}`, { details: { status } });
|
|
5585
|
+
this.name = "AccountDisabledError";
|
|
5586
|
+
}
|
|
5587
|
+
};
|
|
5588
|
+
var AccountAlreadyExistsError = class extends ConflictError {
|
|
5589
|
+
constructor(identifier, identifierType) {
|
|
5590
|
+
super("Account already exists", { details: { identifier, identifierType } });
|
|
5591
|
+
this.name = "AccountAlreadyExistsError";
|
|
5592
|
+
}
|
|
5593
|
+
};
|
|
5594
|
+
var InvalidVerificationCodeError = class extends ValidationError {
|
|
5595
|
+
constructor(reason = "Invalid verification code") {
|
|
5596
|
+
super(reason);
|
|
5597
|
+
this.name = "InvalidVerificationCodeError";
|
|
5598
|
+
}
|
|
5599
|
+
};
|
|
5600
|
+
var InvalidVerificationTokenError = class extends ValidationError {
|
|
5601
|
+
constructor(message = "Invalid or expired verification token") {
|
|
5602
|
+
super(message);
|
|
5603
|
+
this.name = "InvalidVerificationTokenError";
|
|
5604
|
+
}
|
|
5605
|
+
};
|
|
5606
|
+
var InvalidKeyFingerprintError = class extends ValidationError {
|
|
5607
|
+
constructor(message = "Invalid key fingerprint") {
|
|
5608
|
+
super(message);
|
|
5609
|
+
this.name = "InvalidKeyFingerprintError";
|
|
5610
|
+
}
|
|
5611
|
+
};
|
|
5612
|
+
var VerificationTokenPurposeMismatchError = class extends ValidationError {
|
|
5613
|
+
constructor(expected, actual) {
|
|
5614
|
+
super(`Verification token is for ${actual}, but ${expected} was expected`, { details: { expected, actual } });
|
|
5615
|
+
this.name = "VerificationTokenPurposeMismatchError";
|
|
5616
|
+
}
|
|
5617
|
+
};
|
|
5618
|
+
var VerificationTokenTargetMismatchError = class extends ValidationError {
|
|
5619
|
+
constructor() {
|
|
5620
|
+
super("Verification token does not match provided email/phone");
|
|
5621
|
+
this.name = "VerificationTokenTargetMismatchError";
|
|
5622
|
+
}
|
|
5623
|
+
};
|
|
5624
|
+
|
|
5631
5625
|
// src/server/services/key.service.ts
|
|
5632
5626
|
init_entities();
|
|
5633
|
-
|
|
5634
|
-
import {
|
|
5627
|
+
init_jwt();
|
|
5628
|
+
import { create as create2, getDatabase as getDatabase2 } from "@spfn/core/db";
|
|
5629
|
+
import { eq as eq2, and as and2 } from "drizzle-orm";
|
|
5635
5630
|
function getKeyExpiryDate() {
|
|
5636
5631
|
const expiresAt = /* @__PURE__ */ new Date();
|
|
5637
5632
|
expiresAt.setDate(expiresAt.getDate() + 90);
|
|
@@ -5660,15 +5655,15 @@ async function rotateKeyService(params) {
|
|
|
5660
5655
|
if (!isValidFingerprint) {
|
|
5661
5656
|
throw new InvalidKeyFingerprintError();
|
|
5662
5657
|
}
|
|
5663
|
-
const db =
|
|
5658
|
+
const db = getDatabase2();
|
|
5664
5659
|
await db.update(userPublicKeys).set({
|
|
5665
5660
|
isActive: false,
|
|
5666
5661
|
revokedAt: /* @__PURE__ */ new Date(),
|
|
5667
5662
|
revokedReason: "Replaced by key rotation"
|
|
5668
5663
|
}).where(
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5664
|
+
and2(
|
|
5665
|
+
eq2(userPublicKeys.keyId, oldKeyId),
|
|
5666
|
+
eq2(userPublicKeys.userId, userId)
|
|
5672
5667
|
)
|
|
5673
5668
|
);
|
|
5674
5669
|
await create2(userPublicKeys, {
|
|
@@ -5688,22 +5683,22 @@ async function rotateKeyService(params) {
|
|
|
5688
5683
|
}
|
|
5689
5684
|
async function revokeKeyService(params) {
|
|
5690
5685
|
const { userId, keyId, reason } = params;
|
|
5691
|
-
const db =
|
|
5686
|
+
const db = getDatabase2();
|
|
5692
5687
|
await db.update(userPublicKeys).set({
|
|
5693
5688
|
isActive: false,
|
|
5694
5689
|
revokedAt: /* @__PURE__ */ new Date(),
|
|
5695
5690
|
revokedReason: reason
|
|
5696
5691
|
}).where(
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5692
|
+
and2(
|
|
5693
|
+
eq2(userPublicKeys.keyId, keyId),
|
|
5694
|
+
eq2(userPublicKeys.userId, userId)
|
|
5700
5695
|
)
|
|
5701
5696
|
);
|
|
5702
5697
|
}
|
|
5703
5698
|
|
|
5704
5699
|
// src/server/services/user.service.ts
|
|
5705
5700
|
init_entities();
|
|
5706
|
-
import { findOne
|
|
5701
|
+
import { findOne, updateOne } from "@spfn/core/db";
|
|
5707
5702
|
async function updateLastLoginService(userId) {
|
|
5708
5703
|
await updateOne(users, { id: userId }, {
|
|
5709
5704
|
lastLoginAt: /* @__PURE__ */ new Date()
|
|
@@ -5719,11 +5714,11 @@ async function checkAccountExistsService(params) {
|
|
|
5719
5714
|
if (email) {
|
|
5720
5715
|
identifier = email;
|
|
5721
5716
|
identifierType = "email";
|
|
5722
|
-
user = await
|
|
5717
|
+
user = await findOne2(users, { email });
|
|
5723
5718
|
} else if (phone) {
|
|
5724
5719
|
identifier = phone;
|
|
5725
5720
|
identifierType = "phone";
|
|
5726
|
-
user = await
|
|
5721
|
+
user = await findOne2(users, { phone });
|
|
5727
5722
|
} else {
|
|
5728
5723
|
throw new ValidationError2("Either email or phone must be provided");
|
|
5729
5724
|
}
|
|
@@ -5752,9 +5747,9 @@ async function registerService(params) {
|
|
|
5752
5747
|
}
|
|
5753
5748
|
let existingUser;
|
|
5754
5749
|
if (email) {
|
|
5755
|
-
existingUser = await
|
|
5750
|
+
existingUser = await findOne2(users, { email });
|
|
5756
5751
|
} else if (phone) {
|
|
5757
|
-
existingUser = await
|
|
5752
|
+
existingUser = await findOne2(users, { phone });
|
|
5758
5753
|
} else {
|
|
5759
5754
|
throw new ValidationError2("Either email or phone must be provided");
|
|
5760
5755
|
}
|
|
@@ -5795,15 +5790,17 @@ async function loginService(params) {
|
|
|
5795
5790
|
const { email, phone, password, publicKey, keyId, fingerprint, oldKeyId, algorithm } = params;
|
|
5796
5791
|
let user;
|
|
5797
5792
|
if (email) {
|
|
5798
|
-
user = await
|
|
5793
|
+
user = await findOne2(users, { email });
|
|
5799
5794
|
} else if (phone) {
|
|
5800
|
-
user = await
|
|
5795
|
+
user = await findOne2(users, { phone });
|
|
5801
5796
|
} else {
|
|
5802
5797
|
throw new ValidationError2("Either email or phone must be provided");
|
|
5803
5798
|
}
|
|
5804
5799
|
if (!user || !user.passwordHash) {
|
|
5805
5800
|
throw new InvalidCredentialsError();
|
|
5806
5801
|
}
|
|
5802
|
+
console.log("user", user);
|
|
5803
|
+
console.log("\uD328\uC2A4\uC6CC\uB4DC: ", password);
|
|
5807
5804
|
const isValid = await verifyPassword(password, user.passwordHash);
|
|
5808
5805
|
if (!isValid) {
|
|
5809
5806
|
throw new InvalidCredentialsError();
|
|
@@ -5847,7 +5844,7 @@ async function changePasswordService(params) {
|
|
|
5847
5844
|
if (providedHash) {
|
|
5848
5845
|
passwordHash = providedHash;
|
|
5849
5846
|
} else {
|
|
5850
|
-
const user = await
|
|
5847
|
+
const user = await findOne2(users, { id: userId });
|
|
5851
5848
|
if (!user) {
|
|
5852
5849
|
throw new ValidationError2("User not found");
|
|
5853
5850
|
}
|
|
@@ -5903,10 +5900,132 @@ async function verifyCodeService(params) {
|
|
|
5903
5900
|
};
|
|
5904
5901
|
}
|
|
5905
5902
|
|
|
5903
|
+
// src/server/services/me.service.ts
|
|
5904
|
+
init_entities();
|
|
5905
|
+
import { getDatabase as getDatabase4 } from "@spfn/core/db";
|
|
5906
|
+
import { eq as eq4, and as and4 } from "drizzle-orm";
|
|
5907
|
+
async function getMeService(userId) {
|
|
5908
|
+
const db = getDatabase4();
|
|
5909
|
+
if (!db) {
|
|
5910
|
+
throw new Error("[Auth] Database not initialized");
|
|
5911
|
+
}
|
|
5912
|
+
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
5913
|
+
const [userWithRole] = await db.select({
|
|
5914
|
+
userId: users.id,
|
|
5915
|
+
email: users.email,
|
|
5916
|
+
phone: users.phone,
|
|
5917
|
+
roleId: roles.id,
|
|
5918
|
+
roleName: roles.name,
|
|
5919
|
+
roleDisplayName: roles.displayName,
|
|
5920
|
+
rolePriority: roles.priority
|
|
5921
|
+
}).from(users).innerJoin(roles, eq4(users.roleId, roles.id)).where(eq4(users.id, userIdNum)).limit(1);
|
|
5922
|
+
if (!userWithRole) {
|
|
5923
|
+
throw new Error("[Auth] User not found");
|
|
5924
|
+
}
|
|
5925
|
+
const rolePerms = await db.select({
|
|
5926
|
+
id: permissions.id,
|
|
5927
|
+
name: permissions.name,
|
|
5928
|
+
displayName: permissions.displayName,
|
|
5929
|
+
category: permissions.category
|
|
5930
|
+
}).from(rolePermissions).innerJoin(permissions, eq4(rolePermissions.permissionId, permissions.id)).where(
|
|
5931
|
+
and4(
|
|
5932
|
+
eq4(rolePermissions.roleId, userWithRole.roleId),
|
|
5933
|
+
eq4(permissions.isActive, true)
|
|
5934
|
+
)
|
|
5935
|
+
);
|
|
5936
|
+
return {
|
|
5937
|
+
userId: userWithRole.userId.toString(),
|
|
5938
|
+
email: userWithRole.email ?? void 0,
|
|
5939
|
+
phone: userWithRole.phone ?? void 0,
|
|
5940
|
+
role: {
|
|
5941
|
+
id: userWithRole.roleId,
|
|
5942
|
+
name: userWithRole.roleName,
|
|
5943
|
+
displayName: userWithRole.roleDisplayName,
|
|
5944
|
+
priority: userWithRole.rolePriority
|
|
5945
|
+
},
|
|
5946
|
+
permissions: rolePerms.map((perm) => ({
|
|
5947
|
+
id: perm.id,
|
|
5948
|
+
name: perm.name,
|
|
5949
|
+
displayName: perm.displayName,
|
|
5950
|
+
category: perm.category ?? void 0
|
|
5951
|
+
}))
|
|
5952
|
+
};
|
|
5953
|
+
}
|
|
5954
|
+
|
|
5906
5955
|
// src/server/services/rbac.service.ts
|
|
5907
5956
|
init_entities();
|
|
5957
|
+
import { getDatabase as getDatabase5 } from "@spfn/core/db";
|
|
5958
|
+
import { logger } from "@spfn/core/logger";
|
|
5959
|
+
import { eq as eq5, and as and5, inArray } from "drizzle-orm";
|
|
5960
|
+
var authLogger = logger.child("@spfn/auth");
|
|
5961
|
+
|
|
5962
|
+
// src/server/services/permission.service.ts
|
|
5963
|
+
init_entities();
|
|
5908
5964
|
import { getDatabase as getDatabase6 } from "@spfn/core/db";
|
|
5909
|
-
import { eq as eq6, and as and6
|
|
5965
|
+
import { eq as eq6, and as and6 } from "drizzle-orm";
|
|
5966
|
+
async function getUserPermissions(userId) {
|
|
5967
|
+
const db = getDatabase6();
|
|
5968
|
+
if (!db) {
|
|
5969
|
+
throw new Error("[Auth] Database not initialized");
|
|
5970
|
+
}
|
|
5971
|
+
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
5972
|
+
const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
|
|
5973
|
+
if (!user || !user.roleId) {
|
|
5974
|
+
return [];
|
|
5975
|
+
}
|
|
5976
|
+
const permSet = /* @__PURE__ */ new Set();
|
|
5977
|
+
const rolePerms = await db.select({ name: permissions.name }).from(rolePermissions).innerJoin(permissions, eq6(rolePermissions.permissionId, permissions.id)).where(
|
|
5978
|
+
and6(
|
|
5979
|
+
eq6(rolePermissions.roleId, user.roleId),
|
|
5980
|
+
eq6(permissions.isActive, true)
|
|
5981
|
+
)
|
|
5982
|
+
);
|
|
5983
|
+
for (const perm of rolePerms) {
|
|
5984
|
+
permSet.add(perm.name);
|
|
5985
|
+
}
|
|
5986
|
+
const userPerms = await db.select({
|
|
5987
|
+
name: permissions.name,
|
|
5988
|
+
granted: userPermissions.granted,
|
|
5989
|
+
expiresAt: userPermissions.expiresAt
|
|
5990
|
+
}).from(userPermissions).innerJoin(permissions, eq6(userPermissions.permissionId, permissions.id)).where(eq6(userPermissions.userId, userIdNum));
|
|
5991
|
+
const now = /* @__PURE__ */ new Date();
|
|
5992
|
+
for (const userPerm of userPerms) {
|
|
5993
|
+
if (userPerm.expiresAt && userPerm.expiresAt < now) {
|
|
5994
|
+
continue;
|
|
5995
|
+
}
|
|
5996
|
+
if (userPerm.granted) {
|
|
5997
|
+
permSet.add(userPerm.name);
|
|
5998
|
+
} else {
|
|
5999
|
+
permSet.delete(userPerm.name);
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
return Array.from(permSet);
|
|
6003
|
+
}
|
|
6004
|
+
async function hasAllPermissions(userId, permissionNames) {
|
|
6005
|
+
const perms = await getUserPermissions(userId);
|
|
6006
|
+
return permissionNames.every((p) => perms.includes(p));
|
|
6007
|
+
}
|
|
6008
|
+
async function hasRole(userId, roleName) {
|
|
6009
|
+
const db = getDatabase6();
|
|
6010
|
+
if (!db) {
|
|
6011
|
+
throw new Error("[Auth] Database not initialized");
|
|
6012
|
+
}
|
|
6013
|
+
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
6014
|
+
const [user] = await db.select({ roleId: users.roleId }).from(users).where(eq6(users.id, userIdNum)).limit(1);
|
|
6015
|
+
if (!user || !user.roleId) {
|
|
6016
|
+
return false;
|
|
6017
|
+
}
|
|
6018
|
+
const [role] = await db.select({ name: roles.name }).from(roles).where(eq6(roles.id, user.roleId)).limit(1);
|
|
6019
|
+
return role?.name === roleName;
|
|
6020
|
+
}
|
|
6021
|
+
async function hasAnyRole(userId, roleNames) {
|
|
6022
|
+
for (const roleName of roleNames) {
|
|
6023
|
+
if (await hasRole(userId, roleName)) {
|
|
6024
|
+
return true;
|
|
6025
|
+
}
|
|
6026
|
+
}
|
|
6027
|
+
return false;
|
|
6028
|
+
}
|
|
5910
6029
|
|
|
5911
6030
|
// src/server/services/index.ts
|
|
5912
6031
|
init_role_service();
|
|
@@ -6202,12 +6321,16 @@ app.bind(loginContract, async (c) => {
|
|
|
6202
6321
|
const result = await loginService(body);
|
|
6203
6322
|
return c.success(result);
|
|
6204
6323
|
});
|
|
6205
|
-
app.bind(logoutContract,
|
|
6206
|
-
const
|
|
6324
|
+
app.bind(logoutContract, async (c) => {
|
|
6325
|
+
const auth = getAuth(c);
|
|
6326
|
+
if (!auth) {
|
|
6327
|
+
return c.success({ success: true });
|
|
6328
|
+
}
|
|
6329
|
+
const { keyId, userId } = auth;
|
|
6207
6330
|
await logoutService({ userId: Number(userId), keyId });
|
|
6208
6331
|
return c.success({ success: true });
|
|
6209
6332
|
});
|
|
6210
|
-
app.bind(rotateKeyContract,
|
|
6333
|
+
app.bind(rotateKeyContract, async (c) => {
|
|
6211
6334
|
const body = await c.data();
|
|
6212
6335
|
const { keyId: oldKeyId, userId } = getAuth(c);
|
|
6213
6336
|
const result = await rotateKeyService({
|
|
@@ -6220,7 +6343,7 @@ app.bind(rotateKeyContract, [authenticate], async (c) => {
|
|
|
6220
6343
|
});
|
|
6221
6344
|
return c.success(result);
|
|
6222
6345
|
});
|
|
6223
|
-
app.bind(changePasswordContract,
|
|
6346
|
+
app.bind(changePasswordContract, async (c) => {
|
|
6224
6347
|
const body = await c.data();
|
|
6225
6348
|
const user = getUser(c);
|
|
6226
6349
|
await changePasswordService({
|
|
@@ -6231,10 +6354,119 @@ app.bind(changePasswordContract, [authenticate], async (c) => {
|
|
|
6231
6354
|
});
|
|
6232
6355
|
return c.success({ success: true });
|
|
6233
6356
|
});
|
|
6357
|
+
app.bind(getMeContract, async (c) => {
|
|
6358
|
+
const { userId } = getAuth(c);
|
|
6359
|
+
const result = await getMeService(userId);
|
|
6360
|
+
return c.success(result);
|
|
6361
|
+
});
|
|
6234
6362
|
var auth_default = app;
|
|
6235
6363
|
|
|
6236
6364
|
// src/server/routes/invitations/index.ts
|
|
6237
6365
|
import { createApp as createApp2 } from "@spfn/core/route";
|
|
6366
|
+
|
|
6367
|
+
// src/server/middleware/authenticate.ts
|
|
6368
|
+
init_jwt();
|
|
6369
|
+
init_entities();
|
|
6370
|
+
import { findOne as findOne3, getDatabase as getDatabase8 } from "@spfn/core/db";
|
|
6371
|
+
import { UnauthorizedError as UnauthorizedError2 } from "@spfn/core/errors";
|
|
6372
|
+
import { eq as eq8, and as and8 } from "drizzle-orm";
|
|
6373
|
+
async function authenticate(c, next) {
|
|
6374
|
+
const authHeader = c.req.header("Authorization");
|
|
6375
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
6376
|
+
throw new UnauthorizedError2("Missing or invalid authorization header");
|
|
6377
|
+
}
|
|
6378
|
+
const token = authHeader.substring(7);
|
|
6379
|
+
const { decodeToken: decodeToken2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
|
|
6380
|
+
const decoded = decodeToken2(token);
|
|
6381
|
+
if (!decoded || !decoded.keyId) {
|
|
6382
|
+
throw new UnauthorizedError2("Invalid token: missing keyId");
|
|
6383
|
+
}
|
|
6384
|
+
const keyId = decoded.keyId;
|
|
6385
|
+
const db = getDatabase8();
|
|
6386
|
+
const [keyRecord] = await db.select().from(userPublicKeys).where(
|
|
6387
|
+
and8(
|
|
6388
|
+
eq8(userPublicKeys.keyId, keyId),
|
|
6389
|
+
eq8(userPublicKeys.isActive, true)
|
|
6390
|
+
)
|
|
6391
|
+
);
|
|
6392
|
+
if (!keyRecord) {
|
|
6393
|
+
throw new UnauthorizedError2("Invalid or revoked key");
|
|
6394
|
+
}
|
|
6395
|
+
if (keyRecord.expiresAt && /* @__PURE__ */ new Date() > keyRecord.expiresAt) {
|
|
6396
|
+
throw new KeyExpiredError();
|
|
6397
|
+
}
|
|
6398
|
+
try {
|
|
6399
|
+
verifyClientToken(
|
|
6400
|
+
token,
|
|
6401
|
+
keyRecord.publicKey,
|
|
6402
|
+
keyRecord.algorithm
|
|
6403
|
+
);
|
|
6404
|
+
} catch (err) {
|
|
6405
|
+
if (err instanceof Error) {
|
|
6406
|
+
if (err.name === "TokenExpiredError") {
|
|
6407
|
+
throw new TokenExpiredError();
|
|
6408
|
+
}
|
|
6409
|
+
if (err.name === "JsonWebTokenError") {
|
|
6410
|
+
throw new InvalidTokenError("Invalid token signature");
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
throw new UnauthorizedError2("Authentication failed");
|
|
6414
|
+
}
|
|
6415
|
+
const user = await findOne3(users, { id: keyRecord.userId });
|
|
6416
|
+
if (!user) {
|
|
6417
|
+
throw new UnauthorizedError2("User not found");
|
|
6418
|
+
}
|
|
6419
|
+
if (user.status !== "active") {
|
|
6420
|
+
throw new AccountDisabledError(user.status);
|
|
6421
|
+
}
|
|
6422
|
+
db.update(userPublicKeys).set({ lastUsedAt: /* @__PURE__ */ new Date() }).where(eq8(userPublicKeys.id, keyRecord.id)).execute().catch((err) => console.error("Failed to update lastUsedAt:", err));
|
|
6423
|
+
c.set("auth", {
|
|
6424
|
+
user,
|
|
6425
|
+
userId: String(user.id),
|
|
6426
|
+
keyId
|
|
6427
|
+
});
|
|
6428
|
+
await next();
|
|
6429
|
+
}
|
|
6430
|
+
|
|
6431
|
+
// src/server/middleware/require-permission.ts
|
|
6432
|
+
import { ForbiddenError as ForbiddenError2 } from "@spfn/core/errors";
|
|
6433
|
+
function requirePermissions(...permissionNames) {
|
|
6434
|
+
return async (c, next) => {
|
|
6435
|
+
const auth = getAuth(c);
|
|
6436
|
+
if (!auth) {
|
|
6437
|
+
throw new ForbiddenError2("Authentication required");
|
|
6438
|
+
}
|
|
6439
|
+
const { userId } = auth;
|
|
6440
|
+
const allowed = await hasAllPermissions(userId, permissionNames);
|
|
6441
|
+
if (!allowed) {
|
|
6442
|
+
throw new ForbiddenError2(
|
|
6443
|
+
`Missing required permissions: ${permissionNames.join(", ")}`
|
|
6444
|
+
);
|
|
6445
|
+
}
|
|
6446
|
+
await next();
|
|
6447
|
+
};
|
|
6448
|
+
}
|
|
6449
|
+
|
|
6450
|
+
// src/server/middleware/require-role.ts
|
|
6451
|
+
import { ForbiddenError as ForbiddenError3 } from "@spfn/core/errors";
|
|
6452
|
+
function requireRole(...roleNames) {
|
|
6453
|
+
return async (c, next) => {
|
|
6454
|
+
const auth = getAuth(c);
|
|
6455
|
+
if (!auth) {
|
|
6456
|
+
throw new ForbiddenError3("Authentication required");
|
|
6457
|
+
}
|
|
6458
|
+
const { userId } = auth;
|
|
6459
|
+
const allowed = await hasAnyRole(userId, roleNames);
|
|
6460
|
+
if (!allowed) {
|
|
6461
|
+
throw new ForbiddenError3(
|
|
6462
|
+
`Required roles: ${roleNames.join(", ")}`
|
|
6463
|
+
);
|
|
6464
|
+
}
|
|
6465
|
+
await next();
|
|
6466
|
+
};
|
|
6467
|
+
}
|
|
6468
|
+
|
|
6469
|
+
// src/server/routes/invitations/index.ts
|
|
6238
6470
|
var app2 = createApp2();
|
|
6239
6471
|
app2.bind(getInvitationContract, async (c) => {
|
|
6240
6472
|
const params = await c.data();
|
|
@@ -6268,7 +6500,7 @@ app2.bind(acceptInvitationContract, async (c) => {
|
|
|
6268
6500
|
});
|
|
6269
6501
|
return c.success(result);
|
|
6270
6502
|
});
|
|
6271
|
-
app2.bind(createInvitationContract, [authenticate], async (c) => {
|
|
6503
|
+
app2.bind(createInvitationContract, [authenticate, requirePermissions("user:invite")], async (c) => {
|
|
6272
6504
|
const body = await c.data();
|
|
6273
6505
|
const { userId } = getAuth(c);
|
|
6274
6506
|
const invitation = await createInvitation({
|
|
@@ -6289,7 +6521,7 @@ app2.bind(createInvitationContract, [authenticate], async (c) => {
|
|
|
6289
6521
|
invitationUrl
|
|
6290
6522
|
});
|
|
6291
6523
|
});
|
|
6292
|
-
app2.bind(listInvitationsContract, [authenticate], async (c) => {
|
|
6524
|
+
app2.bind(listInvitationsContract, [authenticate, requirePermissions("user:read")], async (c) => {
|
|
6293
6525
|
const query = await c.data();
|
|
6294
6526
|
const result = await listInvitations({
|
|
6295
6527
|
status: query.status,
|
|
@@ -6308,7 +6540,7 @@ app2.bind(listInvitationsContract, [authenticate], async (c) => {
|
|
|
6308
6540
|
invitations: formattedInvitations
|
|
6309
6541
|
});
|
|
6310
6542
|
});
|
|
6311
|
-
app2.bind(cancelInvitationContract, [authenticate], async (c) => {
|
|
6543
|
+
app2.bind(cancelInvitationContract, [authenticate, requirePermissions("user:invite")], async (c) => {
|
|
6312
6544
|
const data = await c.data();
|
|
6313
6545
|
const { userId } = getAuth(c);
|
|
6314
6546
|
await cancelInvitation(
|
|
@@ -6321,7 +6553,7 @@ app2.bind(cancelInvitationContract, [authenticate], async (c) => {
|
|
|
6321
6553
|
cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6322
6554
|
});
|
|
6323
6555
|
});
|
|
6324
|
-
app2.bind(resendInvitationContract, [authenticate], async (c) => {
|
|
6556
|
+
app2.bind(resendInvitationContract, [authenticate, requirePermissions("user:invite")], async (c) => {
|
|
6325
6557
|
const data = await c.data();
|
|
6326
6558
|
const updated = await resendInvitation(
|
|
6327
6559
|
data.id,
|
|
@@ -6332,7 +6564,7 @@ app2.bind(resendInvitationContract, [authenticate], async (c) => {
|
|
|
6332
6564
|
expiresAt: updated.expiresAt.toISOString()
|
|
6333
6565
|
});
|
|
6334
6566
|
});
|
|
6335
|
-
app2.bind(deleteInvitationContract, [authenticate], async (c) => {
|
|
6567
|
+
app2.bind(deleteInvitationContract, [authenticate, requireRole("superadmin")], async (c) => {
|
|
6336
6568
|
const data = await c.data();
|
|
6337
6569
|
await deleteInvitation(data.id);
|
|
6338
6570
|
return c.success({
|