@dyrected/core 2.5.43 → 2.5.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-config-3XBnu9Xz.d.cts +1379 -0
- package/dist/app-config-3XBnu9Xz.d.ts +1379 -0
- package/dist/chunk-44O2LDPT.js +1513 -0
- package/dist/index.cjs +86 -95
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +16 -10
- package/dist/server.cjs +483 -310
- package/dist/server.d.cts +34 -1
- package/dist/server.d.ts +34 -1
- package/dist/server.js +411 -231
- package/dist/where-sanitizer-7Q4JXMX6.js +57 -0
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -13,14 +13,14 @@ import {
|
|
|
13
13
|
runCollectionHooks,
|
|
14
14
|
saveWorkflowDraft,
|
|
15
15
|
transitionWorkflow
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-44O2LDPT.js";
|
|
17
17
|
|
|
18
|
-
// src/app.
|
|
18
|
+
// src/app.js
|
|
19
19
|
import { Hono } from "hono";
|
|
20
20
|
import { cors } from "hono/cors";
|
|
21
21
|
import { requestId } from "hono/request-id";
|
|
22
22
|
|
|
23
|
-
// src/services/defaults.service.
|
|
23
|
+
// src/services/defaults.service.js
|
|
24
24
|
var DefaultsService = class {
|
|
25
25
|
/**
|
|
26
26
|
* Recursively apply default values to a data object based on field definitions.
|
|
@@ -28,12 +28,14 @@ var DefaultsService = class {
|
|
|
28
28
|
static apply(fields, data = {}) {
|
|
29
29
|
const result = { ...data || {} };
|
|
30
30
|
fields.forEach((field) => {
|
|
31
|
-
if (field.type === "join")
|
|
31
|
+
if (field.type === "join")
|
|
32
|
+
return;
|
|
32
33
|
if (field.type === "row" && field.fields) {
|
|
33
34
|
Object.assign(result, this.apply(field.fields, data));
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
|
-
if (!field.name)
|
|
37
|
+
if (!field.name)
|
|
38
|
+
return;
|
|
37
39
|
let value = result[field.name];
|
|
38
40
|
if ((value === void 0 || value === null) && field.renameTo) {
|
|
39
41
|
const legacyValue = result[field.renameTo];
|
|
@@ -46,9 +48,12 @@ var DefaultsService = class {
|
|
|
46
48
|
if (field.defaultValue !== void 0) {
|
|
47
49
|
result[field.name] = field.defaultValue;
|
|
48
50
|
} else {
|
|
49
|
-
if (field.type === "boolean")
|
|
50
|
-
|
|
51
|
-
else if (field.type === "
|
|
51
|
+
if (field.type === "boolean")
|
|
52
|
+
result[field.name] = false;
|
|
53
|
+
else if (field.type === "array")
|
|
54
|
+
result[field.name] = [];
|
|
55
|
+
else if (field.type === "multiSelect")
|
|
56
|
+
result[field.name] = [];
|
|
52
57
|
else if (field.type === "object") {
|
|
53
58
|
result[field.name] = this.apply(field.fields || [], {});
|
|
54
59
|
}
|
|
@@ -63,7 +68,7 @@ var DefaultsService = class {
|
|
|
63
68
|
}
|
|
64
69
|
};
|
|
65
70
|
|
|
66
|
-
// src/services/population.service.
|
|
71
|
+
// src/services/population.service.js
|
|
67
72
|
var PopulationService = class {
|
|
68
73
|
db;
|
|
69
74
|
collections;
|
|
@@ -116,29 +121,31 @@ var PopulationService = class {
|
|
|
116
121
|
Object.assign(populatedDoc, rowPopulated);
|
|
117
122
|
continue;
|
|
118
123
|
}
|
|
119
|
-
if (!field.name)
|
|
124
|
+
if (!field.name)
|
|
125
|
+
continue;
|
|
120
126
|
const value = populatedDoc[field.name];
|
|
121
127
|
if (field.type === "relationship" && field.relationTo && value) {
|
|
122
128
|
const relatedCollection = this.collections.find((c) => c.slug === field.relationTo);
|
|
123
|
-
if (!relatedCollection)
|
|
129
|
+
if (!relatedCollection)
|
|
130
|
+
continue;
|
|
124
131
|
if (Array.isArray(value)) {
|
|
125
|
-
populatedDoc[field.name] = await Promise.all(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
})
|
|
141
|
-
);
|
|
132
|
+
populatedDoc[field.name] = await Promise.all(value.map(async (id) => {
|
|
133
|
+
if (!id)
|
|
134
|
+
return id;
|
|
135
|
+
let doc = id;
|
|
136
|
+
if (typeof id === "string") {
|
|
137
|
+
doc = await this.db.findOne({ collection: field.relationTo, id });
|
|
138
|
+
}
|
|
139
|
+
if (!doc || typeof doc !== "object")
|
|
140
|
+
return id;
|
|
141
|
+
const docWithDefaults = DefaultsService.apply(relatedCollection.fields, doc);
|
|
142
|
+
return this.populate({
|
|
143
|
+
data: docWithDefaults,
|
|
144
|
+
fields: relatedCollection.fields,
|
|
145
|
+
currentDepth: currentDepth + 1,
|
|
146
|
+
maxDepth
|
|
147
|
+
});
|
|
148
|
+
}));
|
|
142
149
|
} else if (value) {
|
|
143
150
|
let doc = value;
|
|
144
151
|
if (typeof value === "string") {
|
|
@@ -187,18 +194,17 @@ var PopulationService = class {
|
|
|
187
194
|
});
|
|
188
195
|
}
|
|
189
196
|
if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
|
|
190
|
-
populatedDoc[field.name] = await Promise.all(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
);
|
|
197
|
+
populatedDoc[field.name] = await Promise.all(value.map(async (blockData) => {
|
|
198
|
+
const blockConfig = field.blocks.find((b) => b.slug === blockData.blockType);
|
|
199
|
+
if (!blockConfig)
|
|
200
|
+
return blockData;
|
|
201
|
+
return this.populate({
|
|
202
|
+
data: blockData,
|
|
203
|
+
fields: blockConfig.fields,
|
|
204
|
+
currentDepth,
|
|
205
|
+
maxDepth
|
|
206
|
+
});
|
|
207
|
+
}));
|
|
202
208
|
}
|
|
203
209
|
}
|
|
204
210
|
return populatedDoc;
|
|
@@ -207,7 +213,8 @@ var PopulationService = class {
|
|
|
207
213
|
* Helper to populate a PaginatedResult
|
|
208
214
|
*/
|
|
209
215
|
async populateResult(result, fields, maxDepth) {
|
|
210
|
-
if (maxDepth <= 0)
|
|
216
|
+
if (maxDepth <= 0)
|
|
217
|
+
return result;
|
|
211
218
|
const populatedDocs = await this.populate({
|
|
212
219
|
data: result.docs,
|
|
213
220
|
fields,
|
|
@@ -221,7 +228,7 @@ var PopulationService = class {
|
|
|
221
228
|
}
|
|
222
229
|
};
|
|
223
230
|
|
|
224
|
-
// src/services/audit.service.
|
|
231
|
+
// src/services/audit.service.js
|
|
225
232
|
var AuditService = class {
|
|
226
233
|
/**
|
|
227
234
|
* Writes a single entry to the __audit collection.
|
|
@@ -249,7 +256,7 @@ var AuditService = class {
|
|
|
249
256
|
}
|
|
250
257
|
};
|
|
251
258
|
|
|
252
|
-
// src/auth/password.
|
|
259
|
+
// src/auth/password.js
|
|
253
260
|
import { promisify } from "util";
|
|
254
261
|
import { scrypt, randomBytes, timingSafeEqual } from "crypto";
|
|
255
262
|
var scryptAsync = promisify(scrypt);
|
|
@@ -262,23 +269,23 @@ async function hashPassword(plain) {
|
|
|
262
269
|
}
|
|
263
270
|
async function verifyPassword(plain, stored) {
|
|
264
271
|
const [salt, storedHash] = stored.split(":");
|
|
265
|
-
if (!salt || !storedHash)
|
|
272
|
+
if (!salt || !storedHash)
|
|
273
|
+
return false;
|
|
266
274
|
const derivedKey = await scryptAsync(plain, salt, KEY_LEN);
|
|
267
275
|
const storedBuffer = Buffer.from(storedHash, "hex");
|
|
268
|
-
if (derivedKey.length !== storedBuffer.length)
|
|
276
|
+
if (derivedKey.length !== storedBuffer.length)
|
|
277
|
+
return false;
|
|
269
278
|
return timingSafeEqual(derivedKey, storedBuffer);
|
|
270
279
|
}
|
|
271
280
|
|
|
272
|
-
// src/utils/readonly-db.
|
|
281
|
+
// src/utils/readonly-db.js
|
|
273
282
|
var WRITE_METHODS = ["create", "update", "delete", "updateGlobal", "execute"];
|
|
274
283
|
function createReadonlyDb(db) {
|
|
275
284
|
return new Proxy(db, {
|
|
276
285
|
get(target, prop) {
|
|
277
286
|
if (WRITE_METHODS.includes(prop)) {
|
|
278
287
|
return () => {
|
|
279
|
-
throw new Error(
|
|
280
|
-
`[dyrected] Write operation "${String(prop)}" is not allowed in this hook phase. Use afterChange/afterDelete hooks for write operations.`
|
|
281
|
-
);
|
|
288
|
+
throw new Error(`[dyrected] Write operation "${String(prop)}" is not allowed in this hook phase. Use afterChange/afterDelete hooks for write operations.`);
|
|
282
289
|
};
|
|
283
290
|
}
|
|
284
291
|
return Reflect.get(target, prop);
|
|
@@ -286,11 +293,13 @@ function createReadonlyDb(db) {
|
|
|
286
293
|
});
|
|
287
294
|
}
|
|
288
295
|
|
|
289
|
-
// src/auth/jexl.
|
|
296
|
+
// src/auth/jexl.js
|
|
290
297
|
import jexl from "jexl";
|
|
291
298
|
async function evaluateAccess(expression, context) {
|
|
292
|
-
if (expression === void 0 || expression === null)
|
|
293
|
-
|
|
299
|
+
if (expression === void 0 || expression === null)
|
|
300
|
+
return false;
|
|
301
|
+
if (typeof expression === "boolean")
|
|
302
|
+
return expression;
|
|
294
303
|
try {
|
|
295
304
|
const result = await jexl.eval(expression, context);
|
|
296
305
|
return !!result;
|
|
@@ -300,16 +309,61 @@ async function evaluateAccess(expression, context) {
|
|
|
300
309
|
}
|
|
301
310
|
}
|
|
302
311
|
|
|
303
|
-
// src/controllers/collection.controller.
|
|
312
|
+
// src/controllers/collection.controller.js
|
|
304
313
|
var CollectionController = class {
|
|
305
314
|
collection;
|
|
306
315
|
constructor(collection) {
|
|
307
316
|
this.collection = collection;
|
|
308
317
|
}
|
|
318
|
+
getDelegatedProvider(c) {
|
|
319
|
+
const config = c.get("config");
|
|
320
|
+
const authCollectionSlug = config.adminAuth?.collectionSlug || "__admins";
|
|
321
|
+
if (this.collection.slug !== authCollectionSlug) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
return config.adminAuth?.providers?.find((p) => p.members) || null;
|
|
325
|
+
}
|
|
309
326
|
async find(c) {
|
|
310
327
|
const config = c.get("config");
|
|
311
328
|
const db = config.db;
|
|
312
|
-
if (!db)
|
|
329
|
+
if (!db)
|
|
330
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
331
|
+
const provider = this.getDelegatedProvider(c);
|
|
332
|
+
if (provider && provider.members?.list) {
|
|
333
|
+
const limit2 = Number(c.req.query("limit")) || 10;
|
|
334
|
+
const page2 = Number(c.req.query("page")) || 1;
|
|
335
|
+
const sort2 = c.req.query("sort") || void 0;
|
|
336
|
+
let where2 = void 0;
|
|
337
|
+
const whereRaw2 = c.req.query("where");
|
|
338
|
+
if (whereRaw2) {
|
|
339
|
+
try {
|
|
340
|
+
where2 = JSON.parse(decodeURIComponent(whereRaw2));
|
|
341
|
+
} catch {
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const paginatedResult = await provider.members.list({ limit: limit2, page: page2, sort: sort2, where: where2, req: c.req });
|
|
345
|
+
const mappedDocs = [];
|
|
346
|
+
for (const m of paginatedResult.docs) {
|
|
347
|
+
let localId = m.id;
|
|
348
|
+
const localDoc = await db.find({
|
|
349
|
+
collection: this.collection.slug,
|
|
350
|
+
where: m.id ? { externalSubject: { equals: m.id } } : { email: { equals: m.email } },
|
|
351
|
+
limit: 1
|
|
352
|
+
});
|
|
353
|
+
if (localDoc.docs[0]) {
|
|
354
|
+
localId = localDoc.docs[0].id;
|
|
355
|
+
}
|
|
356
|
+
mappedDocs.push({
|
|
357
|
+
...m,
|
|
358
|
+
id: localId,
|
|
359
|
+
externalSubject: m.id
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return c.json({
|
|
363
|
+
...paginatedResult,
|
|
364
|
+
docs: mappedDocs
|
|
365
|
+
});
|
|
366
|
+
}
|
|
313
367
|
const readonlyDb = createReadonlyDb(db);
|
|
314
368
|
const limit = Number(c.req.query("limit")) || 10;
|
|
315
369
|
const page = Number(c.req.query("page")) || 1;
|
|
@@ -328,7 +382,7 @@ var CollectionController = class {
|
|
|
328
382
|
if (this.collection.admin?.filterable === false) {
|
|
329
383
|
where = void 0;
|
|
330
384
|
} else {
|
|
331
|
-
const { sanitizeWhereClause } = await import("./where-sanitizer-
|
|
385
|
+
const { sanitizeWhereClause } = await import("./where-sanitizer-7Q4JXMX6.js");
|
|
332
386
|
where = sanitizeWhereClause(where, this.collection.fields);
|
|
333
387
|
if (Object.keys(where).length === 0) {
|
|
334
388
|
where = void 0;
|
|
@@ -367,11 +421,13 @@ var CollectionController = class {
|
|
|
367
421
|
where
|
|
368
422
|
});
|
|
369
423
|
}
|
|
370
|
-
result.docs = result.docs.map((doc) => this.collection.workflow ? materializeWorkflowDocument(doc, this.collection.workflow, user) : doc).filter((doc) => doc !== null)
|
|
424
|
+
result.docs = result.docs.map((doc) => this.collection.workflow ? materializeWorkflowDocument(doc, this.collection.workflow, user) : doc).filter((doc) => doc !== null);
|
|
371
425
|
const processedDocs = [];
|
|
426
|
+
const readonlyDbForHooks = createReadonlyDb(db);
|
|
372
427
|
for (const doc of result.docs) {
|
|
428
|
+
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
373
429
|
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
374
|
-
doc,
|
|
430
|
+
doc: docWithDefaults,
|
|
375
431
|
req: c.req,
|
|
376
432
|
user,
|
|
377
433
|
db: readonlyDb
|
|
@@ -389,17 +445,43 @@ var CollectionController = class {
|
|
|
389
445
|
async findOne(c) {
|
|
390
446
|
const config = c.get("config");
|
|
391
447
|
const db = config.db;
|
|
392
|
-
if (!db)
|
|
448
|
+
if (!db)
|
|
449
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
450
|
+
const provider = this.getDelegatedProvider(c);
|
|
451
|
+
if (provider && (provider.members?.get || provider.members?.list)) {
|
|
452
|
+
const id2 = c.req.param("id");
|
|
453
|
+
if (!id2)
|
|
454
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
455
|
+
const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
|
|
456
|
+
const externalSubject = localDoc?.externalSubject || id2;
|
|
457
|
+
let member = null;
|
|
458
|
+
if (provider.members.get) {
|
|
459
|
+
member = await provider.members.get({ externalSubject, req: c.req });
|
|
460
|
+
} else if (provider.members.list) {
|
|
461
|
+
const listResult = await provider.members.list({ req: c.req });
|
|
462
|
+
member = listResult.docs.find((m) => m.id === externalSubject) || null;
|
|
463
|
+
}
|
|
464
|
+
if (!member)
|
|
465
|
+
return c.json({ message: "Not Found" }, 404);
|
|
466
|
+
return c.json({
|
|
467
|
+
...member,
|
|
468
|
+
id: localDoc ? localDoc.id : member.id,
|
|
469
|
+
externalSubject: member.id
|
|
470
|
+
});
|
|
471
|
+
}
|
|
393
472
|
const readonlyDb = createReadonlyDb(db);
|
|
394
473
|
const id = c.req.param("id");
|
|
395
474
|
const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
|
|
396
475
|
const user = c.get("user");
|
|
397
|
-
if (!id)
|
|
476
|
+
if (!id)
|
|
477
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
398
478
|
let doc = await db.findOne({ collection: this.collection.slug, id });
|
|
399
|
-
if (!doc)
|
|
479
|
+
if (!doc)
|
|
480
|
+
return c.json({ message: "Not Found" }, 404);
|
|
400
481
|
if (this.collection.workflow) {
|
|
401
482
|
doc = materializeWorkflowDocument(doc, this.collection.workflow, user);
|
|
402
|
-
if (!doc)
|
|
483
|
+
if (!doc)
|
|
484
|
+
return c.json({ message: "Not Found" }, 404);
|
|
403
485
|
}
|
|
404
486
|
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
405
487
|
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
@@ -424,7 +506,22 @@ var CollectionController = class {
|
|
|
424
506
|
async create(c) {
|
|
425
507
|
const config = c.get("config");
|
|
426
508
|
const db = config.db;
|
|
427
|
-
if (!db)
|
|
509
|
+
if (!db)
|
|
510
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
511
|
+
const provider = this.getDelegatedProvider(c);
|
|
512
|
+
if (provider && provider.members?.create) {
|
|
513
|
+
const contentType2 = c.req.header("Content-Type") || "";
|
|
514
|
+
if (contentType2.toLowerCase().includes("multipart/form-data")) {
|
|
515
|
+
return this.upload(c);
|
|
516
|
+
}
|
|
517
|
+
const body2 = await c.req.json();
|
|
518
|
+
const member = await provider.members.create({ data: body2, req: c.req });
|
|
519
|
+
return c.json({
|
|
520
|
+
...member,
|
|
521
|
+
id: member.id,
|
|
522
|
+
externalSubject: member.id
|
|
523
|
+
}, 201);
|
|
524
|
+
}
|
|
428
525
|
const readonlyDb = createReadonlyDb(db);
|
|
429
526
|
const contentType = c.req.header("Content-Type") || "";
|
|
430
527
|
if (contentType.toLowerCase().includes("multipart/form-data")) {
|
|
@@ -485,13 +582,16 @@ var CollectionController = class {
|
|
|
485
582
|
async upload(c) {
|
|
486
583
|
const config = c.get("config");
|
|
487
584
|
const storage = config.storage;
|
|
488
|
-
if (!storage)
|
|
585
|
+
if (!storage)
|
|
586
|
+
return c.json({ message: "Storage not configured" }, 500);
|
|
489
587
|
const db = config.db;
|
|
490
|
-
if (!db)
|
|
588
|
+
if (!db)
|
|
589
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
491
590
|
const readonlyDb = createReadonlyDb(db);
|
|
492
591
|
const formData = await c.req.formData();
|
|
493
592
|
const file = formData.get("file");
|
|
494
|
-
if (!file)
|
|
593
|
+
if (!file)
|
|
594
|
+
return c.json({ message: "No file uploaded" }, 400);
|
|
495
595
|
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
496
596
|
const siteId = c.get("siteId");
|
|
497
597
|
const workspaceId = c.get("workspaceId");
|
|
@@ -550,10 +650,27 @@ var CollectionController = class {
|
|
|
550
650
|
async update(c) {
|
|
551
651
|
const config = c.get("config");
|
|
552
652
|
const db = config.db;
|
|
553
|
-
if (!db)
|
|
653
|
+
if (!db)
|
|
654
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
655
|
+
const provider = this.getDelegatedProvider(c);
|
|
656
|
+
if (provider && provider.members?.update) {
|
|
657
|
+
const id2 = c.req.param("id");
|
|
658
|
+
if (!id2)
|
|
659
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
660
|
+
const body2 = await c.req.json();
|
|
661
|
+
const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
|
|
662
|
+
const externalSubject = localDoc?.externalSubject || id2;
|
|
663
|
+
const member = await provider.members.update({ externalSubject, data: body2, req: c.req });
|
|
664
|
+
return c.json({
|
|
665
|
+
...member,
|
|
666
|
+
id: localDoc ? localDoc.id : member.id,
|
|
667
|
+
externalSubject: member.id
|
|
668
|
+
});
|
|
669
|
+
}
|
|
554
670
|
const readonlyDb = createReadonlyDb(db);
|
|
555
671
|
const id = c.req.param("id");
|
|
556
|
-
if (!id)
|
|
672
|
+
if (!id)
|
|
673
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
557
674
|
const body = await c.req.json();
|
|
558
675
|
const user = c.get("user");
|
|
559
676
|
let data = { ...body };
|
|
@@ -567,7 +684,8 @@ var CollectionController = class {
|
|
|
567
684
|
updatedBy: user?.sub ?? null
|
|
568
685
|
});
|
|
569
686
|
const originalDoc = await db.findOne({ collection: this.collection.slug, id });
|
|
570
|
-
if (!originalDoc)
|
|
687
|
+
if (!originalDoc)
|
|
688
|
+
return c.json({ message: "Not Found" }, 404);
|
|
571
689
|
let before = null;
|
|
572
690
|
if (this.collection.audit) {
|
|
573
691
|
before = originalDoc;
|
|
@@ -611,8 +729,10 @@ var CollectionController = class {
|
|
|
611
729
|
}
|
|
612
730
|
async transition(c) {
|
|
613
731
|
const config = c.get("config");
|
|
614
|
-
if (!config.db)
|
|
615
|
-
|
|
732
|
+
if (!config.db)
|
|
733
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
734
|
+
if (!this.collection.workflow)
|
|
735
|
+
return c.json({ message: "Workflows are not enabled for this collection" }, 404);
|
|
616
736
|
const id = c.req.param("id");
|
|
617
737
|
const transitionName = c.req.param("transition");
|
|
618
738
|
const body = await c.req.json().catch(() => ({}));
|
|
@@ -635,12 +755,16 @@ var CollectionController = class {
|
|
|
635
755
|
}
|
|
636
756
|
async workflowHistory(c) {
|
|
637
757
|
const config = c.get("config");
|
|
638
|
-
if (!config.db)
|
|
639
|
-
|
|
758
|
+
if (!config.db)
|
|
759
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
760
|
+
if (!this.collection.workflow)
|
|
761
|
+
return c.json({ message: "Workflows are not enabled for this collection" }, 404);
|
|
640
762
|
const documentId = c.req.param("id");
|
|
641
|
-
if (!documentId)
|
|
763
|
+
if (!documentId)
|
|
764
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
642
765
|
const document = await config.db.findOne({ collection: this.collection.slug, id: documentId });
|
|
643
|
-
if (!document)
|
|
766
|
+
if (!document)
|
|
767
|
+
return c.json({ message: "Not Found" }, 404);
|
|
644
768
|
const readAccess = this.collection.access?.read;
|
|
645
769
|
if (readAccess !== void 0 && readAccess !== null) {
|
|
646
770
|
const args = { user: c.get("user"), req: c.req, doc: document };
|
|
@@ -654,7 +778,8 @@ var CollectionController = class {
|
|
|
654
778
|
});
|
|
655
779
|
allowed = match.total > 0;
|
|
656
780
|
}
|
|
657
|
-
if (!allowed)
|
|
781
|
+
if (!allowed)
|
|
782
|
+
return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
|
|
658
783
|
}
|
|
659
784
|
const result = await config.db.find({
|
|
660
785
|
collection: WORKFLOW_HISTORY_COLLECTION,
|
|
@@ -678,14 +803,17 @@ var CollectionController = class {
|
|
|
678
803
|
async changePassword(c) {
|
|
679
804
|
const config = c.get("config");
|
|
680
805
|
const db = config.db;
|
|
681
|
-
if (!db)
|
|
806
|
+
if (!db)
|
|
807
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
682
808
|
if (!this.collection.auth) {
|
|
683
809
|
return c.json({ message: "This collection does not support authentication" }, 400);
|
|
684
810
|
}
|
|
685
811
|
const id = c.req.param("id");
|
|
686
|
-
if (!id)
|
|
812
|
+
if (!id)
|
|
813
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
687
814
|
const user = c.get("user");
|
|
688
|
-
if (!user)
|
|
815
|
+
if (!user)
|
|
816
|
+
return c.json({ message: "Authentication required" }, 401);
|
|
689
817
|
const body = await c.req.json().catch(() => null);
|
|
690
818
|
const { oldPassword, newPassword, confirmPassword } = body ?? {};
|
|
691
819
|
if (!newPassword) {
|
|
@@ -707,7 +835,8 @@ var CollectionController = class {
|
|
|
707
835
|
return c.json({ message: "Current password is required" }, 400);
|
|
708
836
|
}
|
|
709
837
|
const existing = await db.findOne({ collection: this.collection.slug, id });
|
|
710
|
-
if (!existing)
|
|
838
|
+
if (!existing)
|
|
839
|
+
return c.json({ message: "User not found" }, 404);
|
|
711
840
|
const valid = await verifyPassword(oldPassword, existing.password);
|
|
712
841
|
if (!valid) {
|
|
713
842
|
return c.json({ message: "Invalid current password" }, 400);
|
|
@@ -738,13 +867,26 @@ var CollectionController = class {
|
|
|
738
867
|
async delete(c) {
|
|
739
868
|
const config = c.get("config");
|
|
740
869
|
const db = config.db;
|
|
741
|
-
if (!db)
|
|
870
|
+
if (!db)
|
|
871
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
872
|
+
const provider = this.getDelegatedProvider(c);
|
|
873
|
+
if (provider && provider.members?.delete) {
|
|
874
|
+
const id2 = c.req.param("id");
|
|
875
|
+
if (!id2)
|
|
876
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
877
|
+
const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
|
|
878
|
+
const externalSubject = localDoc?.externalSubject || id2;
|
|
879
|
+
await provider.members.delete({ externalSubject, req: c.req });
|
|
880
|
+
return c.json({ message: "Deleted" });
|
|
881
|
+
}
|
|
742
882
|
const readonlyDb = createReadonlyDb(db);
|
|
743
883
|
const id = c.req.param("id");
|
|
744
|
-
if (!id)
|
|
884
|
+
if (!id)
|
|
885
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
745
886
|
const user = c.get("user");
|
|
746
887
|
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
747
|
-
if (!doc)
|
|
888
|
+
if (!doc)
|
|
889
|
+
return c.json({ message: "Not Found" }, 404);
|
|
748
890
|
let before = null;
|
|
749
891
|
if (this.collection.audit) {
|
|
750
892
|
before = doc;
|
|
@@ -779,7 +921,8 @@ var CollectionController = class {
|
|
|
779
921
|
async deleteMany(c) {
|
|
780
922
|
const config = c.get("config");
|
|
781
923
|
const db = config.db;
|
|
782
|
-
if (!db)
|
|
924
|
+
if (!db)
|
|
925
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
783
926
|
const readonlyDb = createReadonlyDb(db);
|
|
784
927
|
const user = c.get("user");
|
|
785
928
|
let ids = [];
|
|
@@ -794,7 +937,8 @@ var CollectionController = class {
|
|
|
794
937
|
const raw = c.req.queries("ids") ?? c.req.queries("ids[]") ?? [];
|
|
795
938
|
ids = raw.filter(Boolean);
|
|
796
939
|
}
|
|
797
|
-
if (!ids.length)
|
|
940
|
+
if (!ids.length)
|
|
941
|
+
return c.json({ message: "No IDs provided" }, 400);
|
|
798
942
|
const deleted = [];
|
|
799
943
|
const failed = [];
|
|
800
944
|
for (const id of ids) {
|
|
@@ -847,7 +991,8 @@ var CollectionController = class {
|
|
|
847
991
|
async seed(c) {
|
|
848
992
|
const config = c.get("config");
|
|
849
993
|
const db = config.db;
|
|
850
|
-
if (!db)
|
|
994
|
+
if (!db)
|
|
995
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
851
996
|
const body = await c.req.json();
|
|
852
997
|
const initialData = body.data;
|
|
853
998
|
if (!initialData || !Array.isArray(initialData)) {
|
|
@@ -867,7 +1012,7 @@ var CollectionController = class {
|
|
|
867
1012
|
}
|
|
868
1013
|
};
|
|
869
1014
|
|
|
870
|
-
// src/controllers/global.controller.
|
|
1015
|
+
// src/controllers/global.controller.js
|
|
871
1016
|
var GlobalController = class {
|
|
872
1017
|
global;
|
|
873
1018
|
constructor(global) {
|
|
@@ -876,7 +1021,8 @@ var GlobalController = class {
|
|
|
876
1021
|
async get(c) {
|
|
877
1022
|
const config = c.get("config");
|
|
878
1023
|
const db = config.db;
|
|
879
|
-
if (!db)
|
|
1024
|
+
if (!db)
|
|
1025
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
880
1026
|
const readonlyDb = createReadonlyDb(db);
|
|
881
1027
|
const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
|
|
882
1028
|
const user = c.get("user");
|
|
@@ -920,7 +1066,8 @@ var GlobalController = class {
|
|
|
920
1066
|
async update(c) {
|
|
921
1067
|
const config = c.get("config");
|
|
922
1068
|
const db = config.db;
|
|
923
|
-
if (!db)
|
|
1069
|
+
if (!db)
|
|
1070
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
924
1071
|
const readonlyDb = createReadonlyDb(db);
|
|
925
1072
|
const body = await c.req.json();
|
|
926
1073
|
const user = c.get("user");
|
|
@@ -955,7 +1102,8 @@ var GlobalController = class {
|
|
|
955
1102
|
async seed(c) {
|
|
956
1103
|
const config = c.get("config");
|
|
957
1104
|
const db = config.db;
|
|
958
|
-
if (!db)
|
|
1105
|
+
if (!db)
|
|
1106
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
959
1107
|
const body = await c.req.json();
|
|
960
1108
|
const initialData = body.data;
|
|
961
1109
|
if (!initialData) {
|
|
@@ -971,20 +1119,23 @@ var GlobalController = class {
|
|
|
971
1119
|
}
|
|
972
1120
|
};
|
|
973
1121
|
function isFunctionallyEmpty(obj) {
|
|
974
|
-
if (obj === null || obj === void 0 || obj === "")
|
|
1122
|
+
if (obj === null || obj === void 0 || obj === "")
|
|
1123
|
+
return true;
|
|
975
1124
|
if (Array.isArray(obj)) {
|
|
976
|
-
if (obj.length === 0)
|
|
1125
|
+
if (obj.length === 0)
|
|
1126
|
+
return true;
|
|
977
1127
|
return obj.every(isFunctionallyEmpty);
|
|
978
1128
|
}
|
|
979
1129
|
if (typeof obj === "object") {
|
|
980
1130
|
const keys = Object.keys(obj);
|
|
981
|
-
if (keys.length === 0)
|
|
1131
|
+
if (keys.length === 0)
|
|
1132
|
+
return true;
|
|
982
1133
|
return keys.every((key) => isFunctionallyEmpty(obj[key]));
|
|
983
1134
|
}
|
|
984
1135
|
return false;
|
|
985
1136
|
}
|
|
986
1137
|
|
|
987
|
-
// src/controllers/media.controller.
|
|
1138
|
+
// src/controllers/media.controller.js
|
|
988
1139
|
var MediaController = class {
|
|
989
1140
|
collection;
|
|
990
1141
|
constructor(collection = "media") {
|
|
@@ -1064,7 +1215,8 @@ var MediaController = class {
|
|
|
1064
1215
|
}
|
|
1065
1216
|
}
|
|
1066
1217
|
const db = config.db;
|
|
1067
|
-
if (!db)
|
|
1218
|
+
if (!db)
|
|
1219
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1068
1220
|
const doc = await db.create({
|
|
1069
1221
|
collection: this.collection,
|
|
1070
1222
|
data: finalFileData
|
|
@@ -1073,7 +1225,8 @@ var MediaController = class {
|
|
|
1073
1225
|
}
|
|
1074
1226
|
async find(c) {
|
|
1075
1227
|
const db = c.get("config").db;
|
|
1076
|
-
if (!db)
|
|
1228
|
+
if (!db)
|
|
1229
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1077
1230
|
const limit = Number(c.req.query("limit")) || 10;
|
|
1078
1231
|
const page = Number(c.req.query("page")) || 1;
|
|
1079
1232
|
const result = await db.find({
|
|
@@ -1087,11 +1240,14 @@ var MediaController = class {
|
|
|
1087
1240
|
const config = c.get("config");
|
|
1088
1241
|
const storage = config.storage;
|
|
1089
1242
|
const db = config.db;
|
|
1090
|
-
if (!db)
|
|
1243
|
+
if (!db)
|
|
1244
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1091
1245
|
const id = c.req.param("id");
|
|
1092
|
-
if (!id)
|
|
1246
|
+
if (!id)
|
|
1247
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
1093
1248
|
const doc = await db.findOne({ collection: this.collection, id });
|
|
1094
|
-
if (!doc)
|
|
1249
|
+
if (!doc)
|
|
1250
|
+
return c.json({ message: "Not Found" }, 404);
|
|
1095
1251
|
if (storage) {
|
|
1096
1252
|
await storage.delete({ filename: doc.filename });
|
|
1097
1253
|
if (doc.sizes) {
|
|
@@ -1112,26 +1268,26 @@ var MediaController = class {
|
|
|
1112
1268
|
return c.json({ message: "Storage not configured for serving" }, 404);
|
|
1113
1269
|
}
|
|
1114
1270
|
const filename = c.req.param("filename");
|
|
1115
|
-
if (!filename)
|
|
1271
|
+
if (!filename)
|
|
1272
|
+
return c.json({ message: "Missing filename" }, 400);
|
|
1116
1273
|
let res = await storage.resolve({ filename });
|
|
1117
1274
|
if (!res && !filename.includes("/")) {
|
|
1118
1275
|
res = await storage.resolve({ filename: `default/${filename}` });
|
|
1119
1276
|
}
|
|
1120
|
-
if (!res)
|
|
1277
|
+
if (!res)
|
|
1278
|
+
return c.json({ message: "Not Found" }, 404);
|
|
1121
1279
|
c.header("Content-Type", res.mimeType);
|
|
1122
1280
|
return c.body(res.buffer);
|
|
1123
1281
|
}
|
|
1124
1282
|
};
|
|
1125
1283
|
|
|
1126
|
-
// src/auth/token.
|
|
1284
|
+
// src/auth/token.js
|
|
1127
1285
|
import { SignJWT, jwtVerify, decodeJwt } from "jose";
|
|
1128
1286
|
import { TextEncoder } from "util";
|
|
1129
1287
|
function getSecret() {
|
|
1130
1288
|
const secret = process.env.DYRECTED_JWT_SECRET || process.env.JWT_SECRET;
|
|
1131
1289
|
if (!secret) {
|
|
1132
|
-
throw new Error(
|
|
1133
|
-
"[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections."
|
|
1134
|
-
);
|
|
1290
|
+
throw new Error("[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections.");
|
|
1135
1291
|
}
|
|
1136
1292
|
return new TextEncoder().encode(secret);
|
|
1137
1293
|
}
|
|
@@ -1144,7 +1300,7 @@ async function verifyCollectionToken(token) {
|
|
|
1144
1300
|
return payload;
|
|
1145
1301
|
}
|
|
1146
1302
|
|
|
1147
|
-
// src/services/email-template.
|
|
1303
|
+
// src/services/email-template.js
|
|
1148
1304
|
var emailTokens = {
|
|
1149
1305
|
colors: {
|
|
1150
1306
|
canvas: "#f6f7f2",
|
|
@@ -1208,11 +1364,9 @@ function detailBox(content, monospace = false) {
|
|
|
1208
1364
|
}
|
|
1209
1365
|
function ctaButton(label, href) {
|
|
1210
1366
|
const safeHref = safeHttpUrl(href);
|
|
1211
|
-
if (!safeHref)
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
"padding:8px 0 24px"
|
|
1215
|
-
), "width:auto");
|
|
1367
|
+
if (!safeHref)
|
|
1368
|
+
return "";
|
|
1369
|
+
return table(row(`<a href="${safeHref}" style="display:inline-block;padding:13px 22px;border-radius:${emailTokens.radius.control};background:${emailTokens.colors.accent};font-family:${emailTokens.font};font-size:14px;line-height:1.2;font-weight:700;color:${emailTokens.colors.text};text-decoration:none">${escapeHtml(label)}</a>`, "padding:8px 0 24px"), "width:auto");
|
|
1216
1370
|
}
|
|
1217
1371
|
function alertBox(content) {
|
|
1218
1372
|
return table(row(escapeHtml(content), `padding:13px 16px;font-family:${emailTokens.font};font-size:13px;line-height:1.5;color:${emailTokens.colors.dangerText}`), `width:100%;background:${emailTokens.colors.dangerSurface};border:1px solid ${emailTokens.colors.dangerBorder};border-radius:${emailTokens.radius.control}`);
|
|
@@ -1223,23 +1377,19 @@ function layout({ preheader, title, content, footer }) {
|
|
|
1223
1377
|
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head>
|
|
1224
1378
|
<body style="margin:0;padding:0;background:${emailTokens.colors.canvas}">
|
|
1225
1379
|
<div style="display:none;max-height:0;overflow:hidden;opacity:0;color:transparent">${escapeHtml(preheader)}</div>
|
|
1226
|
-
${table(row(
|
|
1227
|
-
table(
|
|
1228
|
-
row(" ", `height:5px;background:${emailTokens.colors.accent};font-size:0;line-height:0`) + row(`${sectionLabel("Dyrected")}${heading(title)}`, "padding:30px 32px 24px") + row(content, "padding:0 32px 32px") + row(`${divider()}${paragraph(footer, "20px 0 6px")}${paragraph("Privacy: this message contains account-related information; please avoid forwarding it.", "0")}`, "padding:0 32px 28px"),
|
|
1229
|
-
`width:100%;max-width:${emailTokens.width};background:${emailTokens.colors.surface};border:1px solid ${emailTokens.colors.border};border-radius:${emailTokens.radius.card};overflow:hidden`
|
|
1230
|
-
),
|
|
1231
|
-
"padding:32px 12px"
|
|
1232
|
-
), `width:100%;background:${emailTokens.colors.canvas}`)}
|
|
1380
|
+
${table(row(table(row(" ", `height:5px;background:${emailTokens.colors.accent};font-size:0;line-height:0`) + row(`${sectionLabel("Dyrected")}${heading(title)}`, "padding:30px 32px 24px") + row(content, "padding:0 32px 32px") + row(`${divider()}${paragraph(footer, "20px 0 6px")}${paragraph("Privacy: this message contains account-related information; please avoid forwarding it.", "0")}`, "padding:0 32px 28px"), `width:100%;max-width:${emailTokens.width};background:${emailTokens.colors.surface};border:1px solid ${emailTokens.colors.border};border-radius:${emailTokens.radius.card};overflow:hidden`), "padding:32px 12px"), `width:100%;background:${emailTokens.colors.canvas}`)}
|
|
1233
1381
|
</body>
|
|
1234
1382
|
</html>`;
|
|
1235
1383
|
}
|
|
1236
1384
|
|
|
1237
|
-
// src/services/email.service.
|
|
1385
|
+
// src/services/email.service.js
|
|
1238
1386
|
var _devSend = null;
|
|
1239
1387
|
var _devSendPromise = null;
|
|
1240
1388
|
async function getDevSend() {
|
|
1241
|
-
if (_devSend)
|
|
1242
|
-
|
|
1389
|
+
if (_devSend)
|
|
1390
|
+
return _devSend;
|
|
1391
|
+
if (_devSendPromise)
|
|
1392
|
+
return _devSendPromise;
|
|
1243
1393
|
_devSendPromise = (async () => {
|
|
1244
1394
|
try {
|
|
1245
1395
|
const nodemailer = await import("nodemailer");
|
|
@@ -1323,7 +1473,7 @@ function buildPasswordChangedEmail(config, args) {
|
|
|
1323
1473
|
};
|
|
1324
1474
|
}
|
|
1325
1475
|
|
|
1326
|
-
// src/controllers/auth.controller.
|
|
1476
|
+
// src/controllers/auth.controller.js
|
|
1327
1477
|
var AuthController = class {
|
|
1328
1478
|
collection;
|
|
1329
1479
|
constructor(collection) {
|
|
@@ -1335,7 +1485,8 @@ var AuthController = class {
|
|
|
1335
1485
|
// ---------------------------------------------------------------------------
|
|
1336
1486
|
async init(c) {
|
|
1337
1487
|
const db = c.get("config").db;
|
|
1338
|
-
if (!db)
|
|
1488
|
+
if (!db)
|
|
1489
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1339
1490
|
const result = await db.find({
|
|
1340
1491
|
collection: this.collection.slug,
|
|
1341
1492
|
limit: 1
|
|
@@ -1351,7 +1502,8 @@ var AuthController = class {
|
|
|
1351
1502
|
async registerFirstUser(c) {
|
|
1352
1503
|
const config = c.get("config");
|
|
1353
1504
|
const db = config.db;
|
|
1354
|
-
if (!db)
|
|
1505
|
+
if (!db)
|
|
1506
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1355
1507
|
const check = await db.find({
|
|
1356
1508
|
collection: this.collection.slug,
|
|
1357
1509
|
limit: 1
|
|
@@ -1379,9 +1531,7 @@ var AuthController = class {
|
|
|
1379
1531
|
collection: this.collection.slug
|
|
1380
1532
|
});
|
|
1381
1533
|
const { subject, html } = buildWelcomeEmail(config, { email: body.email });
|
|
1382
|
-
sendEmail(config, { to: body.email, subject, html }).catch(
|
|
1383
|
-
(err) => console.error("[dyrected/core] Failed to send welcome email:", err)
|
|
1384
|
-
);
|
|
1534
|
+
sendEmail(config, { to: body.email, subject, html }).catch((err) => console.error("[dyrected/core] Failed to send welcome email:", err));
|
|
1385
1535
|
const { password: _, ...safeUser } = user;
|
|
1386
1536
|
return c.json({ token, user: safeUser });
|
|
1387
1537
|
}
|
|
@@ -1390,7 +1540,8 @@ var AuthController = class {
|
|
|
1390
1540
|
// ---------------------------------------------------------------------------
|
|
1391
1541
|
async login(c) {
|
|
1392
1542
|
const db = c.get("config").db;
|
|
1393
|
-
if (!db)
|
|
1543
|
+
if (!db)
|
|
1544
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1394
1545
|
const body = await c.req.json().catch(() => null);
|
|
1395
1546
|
if (!body?.email || !body?.password) {
|
|
1396
1547
|
return c.json({ error: true, message: "email and password are required." }, 400);
|
|
@@ -1429,7 +1580,8 @@ var AuthController = class {
|
|
|
1429
1580
|
// ---------------------------------------------------------------------------
|
|
1430
1581
|
async me(c) {
|
|
1431
1582
|
const db = c.get("config").db;
|
|
1432
|
-
if (!db)
|
|
1583
|
+
if (!db)
|
|
1584
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1433
1585
|
const requestUser = c.get("user");
|
|
1434
1586
|
if (!requestUser) {
|
|
1435
1587
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
@@ -1464,7 +1616,8 @@ var AuthController = class {
|
|
|
1464
1616
|
async forgotPassword(c) {
|
|
1465
1617
|
const config = c.get("config");
|
|
1466
1618
|
const db = config.db;
|
|
1467
|
-
if (!db)
|
|
1619
|
+
if (!db)
|
|
1620
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1468
1621
|
const body = await c.req.json().catch(() => null);
|
|
1469
1622
|
if (!body?.email) {
|
|
1470
1623
|
return c.json({ error: true, message: "email is required." }, 400);
|
|
@@ -1476,10 +1629,7 @@ var AuthController = class {
|
|
|
1476
1629
|
});
|
|
1477
1630
|
const user = result.docs[0];
|
|
1478
1631
|
if (user) {
|
|
1479
|
-
const resetToken = await signCollectionToken(
|
|
1480
|
-
{ sub: user.id, email: user.email, collection: this.collection.slug, purpose: "reset" },
|
|
1481
|
-
"1h"
|
|
1482
|
-
);
|
|
1632
|
+
const resetToken = await signCollectionToken({ sub: user.id, email: user.email, collection: this.collection.slug, purpose: "reset" }, "1h");
|
|
1483
1633
|
const resetUrl = body?.resetUrl;
|
|
1484
1634
|
const url = resetUrl ? `${resetUrl}${resetUrl.includes("?") ? "&" : "?"}token=${encodeURIComponent(resetToken)}` : void 0;
|
|
1485
1635
|
try {
|
|
@@ -1502,7 +1652,8 @@ var AuthController = class {
|
|
|
1502
1652
|
async resetPassword(c) {
|
|
1503
1653
|
const config = c.get("config");
|
|
1504
1654
|
const db = config.db;
|
|
1505
|
-
if (!db)
|
|
1655
|
+
if (!db)
|
|
1656
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1506
1657
|
const body = await c.req.json().catch(() => null);
|
|
1507
1658
|
if (!body?.token || !body?.password) {
|
|
1508
1659
|
return c.json({ error: true, message: "token and password are required." }, 400);
|
|
@@ -1523,9 +1674,7 @@ var AuthController = class {
|
|
|
1523
1674
|
data: { password: hashedPassword }
|
|
1524
1675
|
});
|
|
1525
1676
|
const { subject, html } = buildPasswordChangedEmail(config, { email: payload.email });
|
|
1526
|
-
sendEmail(config, { to: payload.email, subject, html }).catch(
|
|
1527
|
-
(err) => console.error("[dyrected/core] Failed to send password-changed email:", err)
|
|
1528
|
-
);
|
|
1677
|
+
sendEmail(config, { to: payload.email, subject, html }).catch((err) => console.error("[dyrected/core] Failed to send password-changed email:", err));
|
|
1529
1678
|
return c.json({ success: true, message: "Password has been reset. You can now log in." });
|
|
1530
1679
|
}
|
|
1531
1680
|
// ---------------------------------------------------------------------------
|
|
@@ -1535,7 +1684,8 @@ var AuthController = class {
|
|
|
1535
1684
|
async invite(c) {
|
|
1536
1685
|
const config = c.get("config");
|
|
1537
1686
|
const db = config.db;
|
|
1538
|
-
if (!db)
|
|
1687
|
+
if (!db)
|
|
1688
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1539
1689
|
const requestUser = c.get("user");
|
|
1540
1690
|
if (!requestUser) {
|
|
1541
1691
|
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
@@ -1552,10 +1702,7 @@ var AuthController = class {
|
|
|
1552
1702
|
if (existing.total > 0) {
|
|
1553
1703
|
return c.json({ error: true, message: "An account with that email already exists." }, 409);
|
|
1554
1704
|
}
|
|
1555
|
-
const inviteToken = await signCollectionToken(
|
|
1556
|
-
{ sub: body.email, email: body.email, collection: this.collection.slug, purpose: "invite" },
|
|
1557
|
-
"7d"
|
|
1558
|
-
);
|
|
1705
|
+
const inviteToken = await signCollectionToken({ sub: body.email, email: body.email, collection: this.collection.slug, purpose: "invite" }, "7d");
|
|
1559
1706
|
try {
|
|
1560
1707
|
const { subject, html } = buildInviteEmail(config, {
|
|
1561
1708
|
token: inviteToken,
|
|
@@ -1575,7 +1722,8 @@ var AuthController = class {
|
|
|
1575
1722
|
async acceptInvite(c) {
|
|
1576
1723
|
const config = c.get("config");
|
|
1577
1724
|
const db = config.db;
|
|
1578
|
-
if (!db)
|
|
1725
|
+
if (!db)
|
|
1726
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
1579
1727
|
const body = await c.req.json().catch(() => null);
|
|
1580
1728
|
if (!body?.token || !body?.password) {
|
|
1581
1729
|
return c.json({ error: true, message: "token and password are required." }, 400);
|
|
@@ -1610,28 +1758,21 @@ var AuthController = class {
|
|
|
1610
1758
|
collection: this.collection.slug
|
|
1611
1759
|
});
|
|
1612
1760
|
const { subject, html } = buildWelcomeEmail(config, { email: inviteeEmail });
|
|
1613
|
-
sendEmail(config, { to: inviteeEmail, subject, html }).catch(
|
|
1614
|
-
(err) => console.error("[dyrected/core] Failed to send welcome email:", err)
|
|
1615
|
-
);
|
|
1761
|
+
sendEmail(config, { to: inviteeEmail, subject, html }).catch((err) => console.error("[dyrected/core] Failed to send welcome email:", err));
|
|
1616
1762
|
const { password: _, ...safeUser } = user;
|
|
1617
1763
|
return c.json({ token: sessionToken, user: safeUser }, 201);
|
|
1618
1764
|
}
|
|
1619
1765
|
};
|
|
1620
1766
|
|
|
1621
|
-
// src/controllers/admin-auth.controller.
|
|
1767
|
+
// src/controllers/admin-auth.controller.js
|
|
1622
1768
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
1623
1769
|
import { TextEncoder as TextEncoder2 } from "util";
|
|
1624
|
-
import {
|
|
1625
|
-
SignJWT as SignJWT2,
|
|
1626
|
-
createRemoteJWKSet,
|
|
1627
|
-
jwtVerify as jwtVerify2,
|
|
1628
|
-
decodeJwt as decodeJwt2
|
|
1629
|
-
} from "jose";
|
|
1770
|
+
import { SignJWT as SignJWT2, createRemoteJWKSet, jwtVerify as jwtVerify2, decodeJwt as decodeJwt2 } from "jose";
|
|
1630
1771
|
var AdminAuthController = class {
|
|
1772
|
+
config;
|
|
1631
1773
|
constructor(config) {
|
|
1632
1774
|
this.config = config;
|
|
1633
1775
|
}
|
|
1634
|
-
config;
|
|
1635
1776
|
providers(c) {
|
|
1636
1777
|
return c.json(getPublicAdminAuthConfig(this.config.adminAuth));
|
|
1637
1778
|
}
|
|
@@ -1644,12 +1785,7 @@ var AdminAuthController = class {
|
|
|
1644
1785
|
const siteId = this.getSiteId(c);
|
|
1645
1786
|
const returnTo = this.normalizeReturnTo(c.req.query("returnTo"), c);
|
|
1646
1787
|
if (provider.type === "oidc") {
|
|
1647
|
-
const redirectUrl = await this.buildOidcStartUrl(
|
|
1648
|
-
provider,
|
|
1649
|
-
returnTo,
|
|
1650
|
-
siteId,
|
|
1651
|
-
new URL(c.req.url).origin
|
|
1652
|
-
);
|
|
1788
|
+
const redirectUrl = await this.buildOidcStartUrl(provider, returnTo, siteId, new URL(c.req.url).origin);
|
|
1653
1789
|
return c.redirect(redirectUrl, 302);
|
|
1654
1790
|
}
|
|
1655
1791
|
if (!provider.startUrl) {
|
|
@@ -1657,16 +1793,14 @@ var AdminAuthController = class {
|
|
|
1657
1793
|
}
|
|
1658
1794
|
const url = this.buildCustomProviderStartUrl(provider.startUrl, new URL(c.req.url).origin);
|
|
1659
1795
|
if (!url) {
|
|
1660
|
-
return c.json(
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
},
|
|
1665
|
-
500
|
|
1666
|
-
);
|
|
1796
|
+
return c.json({
|
|
1797
|
+
error: true,
|
|
1798
|
+
message: `Admin auth provider "${provider.id}" has an invalid start URL.`
|
|
1799
|
+
}, 500);
|
|
1667
1800
|
}
|
|
1668
1801
|
url.searchParams.set("returnTo", returnTo);
|
|
1669
|
-
if (siteId)
|
|
1802
|
+
if (siteId)
|
|
1803
|
+
url.searchParams.set("siteId", siteId);
|
|
1670
1804
|
url.searchParams.set("provider", provider.id);
|
|
1671
1805
|
return c.redirect(url.toString(), 302);
|
|
1672
1806
|
}
|
|
@@ -1713,7 +1847,8 @@ var AdminAuthController = class {
|
|
|
1713
1847
|
return c.json({ success: true, message: "Logged out. Discard your token." });
|
|
1714
1848
|
}
|
|
1715
1849
|
getProvider(config, id) {
|
|
1716
|
-
if (config.adminAuth?.mode !== "external")
|
|
1850
|
+
if (config.adminAuth?.mode !== "external")
|
|
1851
|
+
return void 0;
|
|
1717
1852
|
return config.adminAuth.providers.find((provider) => provider.id === id);
|
|
1718
1853
|
}
|
|
1719
1854
|
async completeProviderAuth(requestConfig, provider, c) {
|
|
@@ -1725,7 +1860,8 @@ var AdminAuthController = class {
|
|
|
1725
1860
|
throw new Error("Admin auth collection is not configured.");
|
|
1726
1861
|
}
|
|
1727
1862
|
const db = c.get("config").db;
|
|
1728
|
-
if (!db)
|
|
1863
|
+
if (!db)
|
|
1864
|
+
throw new Error("Database not configured.");
|
|
1729
1865
|
let user = await this.findUserByExternalIdentity(db, adminCollection.slug, provider.id, identity.sub) ?? (identity.email ? (await db.find({
|
|
1730
1866
|
collection: adminCollection.slug,
|
|
1731
1867
|
where: { email: identity.email },
|
|
@@ -1785,7 +1921,8 @@ var AdminAuthController = class {
|
|
|
1785
1921
|
},
|
|
1786
1922
|
user
|
|
1787
1923
|
});
|
|
1788
|
-
if (resolved)
|
|
1924
|
+
if (resolved)
|
|
1925
|
+
return resolved;
|
|
1789
1926
|
return { allowed: true, roles: identity.roles, data: {} };
|
|
1790
1927
|
}
|
|
1791
1928
|
async buildUserData(identity, providerId, roles, extraData) {
|
|
@@ -1816,7 +1953,8 @@ var AdminAuthController = class {
|
|
|
1816
1953
|
}
|
|
1817
1954
|
async getRequestConfig(c) {
|
|
1818
1955
|
const siteId = this.getSiteId(c);
|
|
1819
|
-
if (!siteId || !this.config.onSchemaFetch)
|
|
1956
|
+
if (!siteId || !this.config.onSchemaFetch)
|
|
1957
|
+
return this.config;
|
|
1820
1958
|
const dynamic = await this.config.onSchemaFetch(siteId);
|
|
1821
1959
|
return {
|
|
1822
1960
|
...this.config,
|
|
@@ -1847,7 +1985,8 @@ var AdminAuthController = class {
|
|
|
1847
1985
|
async resolveOidcIdentity(provider, c) {
|
|
1848
1986
|
const code = c.req.query("code");
|
|
1849
1987
|
const state = c.req.query("state");
|
|
1850
|
-
if (!code || !state)
|
|
1988
|
+
if (!code || !state)
|
|
1989
|
+
throw new Error("Missing OIDC callback parameters.");
|
|
1851
1990
|
const parsedState = await this.verifyState(state, provider.id);
|
|
1852
1991
|
const discovery = await this.getOidcDiscovery(provider);
|
|
1853
1992
|
const redirectUri = provider.redirectUri || this.defaultCallbackPath(provider.id, new URL(c.req.url).origin);
|
|
@@ -1882,7 +2021,8 @@ var AdminAuthController = class {
|
|
|
1882
2021
|
const userInfo = await fetch(provider.userInfoEndpoint || discovery.userinfo_endpoint, {
|
|
1883
2022
|
headers: { Authorization: `Bearer ${tokenBody.access_token}` }
|
|
1884
2023
|
});
|
|
1885
|
-
if (!userInfo.ok)
|
|
2024
|
+
if (!userInfo.ok)
|
|
2025
|
+
throw new Error("OIDC userinfo request failed.");
|
|
1886
2026
|
claims = await userInfo.json();
|
|
1887
2027
|
} else {
|
|
1888
2028
|
throw new Error("OIDC provider did not return usable identity claims.");
|
|
@@ -1922,15 +2062,13 @@ var AdminAuthController = class {
|
|
|
1922
2062
|
if (provider.audience) {
|
|
1923
2063
|
const aud = claims.aud;
|
|
1924
2064
|
const matches = Array.isArray(aud) ? aud.includes(provider.audience) : aud === provider.audience;
|
|
1925
|
-
if (!matches)
|
|
2065
|
+
if (!matches)
|
|
2066
|
+
throw new Error("External auth audience mismatch.");
|
|
1926
2067
|
}
|
|
1927
2068
|
}
|
|
1928
2069
|
return {
|
|
1929
2070
|
identity: this.mapClaims(claims, provider.claimMapping),
|
|
1930
|
-
returnTo: this.normalizeReturnTo(
|
|
1931
|
-
body?.returnTo || c.req.query("returnTo"),
|
|
1932
|
-
c
|
|
1933
|
-
)
|
|
2071
|
+
returnTo: this.normalizeReturnTo(body?.returnTo || c.req.query("returnTo"), c)
|
|
1934
2072
|
};
|
|
1935
2073
|
}
|
|
1936
2074
|
mapClaims(claims, mapping) {
|
|
@@ -1960,19 +2098,20 @@ var AdminAuthController = class {
|
|
|
1960
2098
|
return typeof value === "string" ? value : void 0;
|
|
1961
2099
|
}
|
|
1962
2100
|
readStringArrayClaim(value) {
|
|
1963
|
-
if (!value)
|
|
2101
|
+
if (!value)
|
|
2102
|
+
return void 0;
|
|
1964
2103
|
if (Array.isArray(value)) {
|
|
1965
2104
|
return value.filter((entry) => typeof entry === "string");
|
|
1966
2105
|
}
|
|
1967
|
-
if (typeof value === "string")
|
|
2106
|
+
if (typeof value === "string")
|
|
2107
|
+
return [value];
|
|
1968
2108
|
return void 0;
|
|
1969
2109
|
}
|
|
1970
2110
|
async getOidcDiscovery(provider) {
|
|
1971
|
-
const discoveryUrl = new URL(
|
|
1972
|
-
provider.issuer.replace(/\/$/, "") + "/.well-known/openid-configuration"
|
|
1973
|
-
);
|
|
2111
|
+
const discoveryUrl = new URL(provider.issuer.replace(/\/$/, "") + "/.well-known/openid-configuration");
|
|
1974
2112
|
const response = await fetch(discoveryUrl.toString());
|
|
1975
|
-
if (!response.ok)
|
|
2113
|
+
if (!response.ok)
|
|
2114
|
+
throw new Error("Failed to load OIDC discovery document.");
|
|
1976
2115
|
return response.json();
|
|
1977
2116
|
}
|
|
1978
2117
|
defaultCallbackPath(providerId, origin = "") {
|
|
@@ -1980,10 +2119,12 @@ var AdminAuthController = class {
|
|
|
1980
2119
|
}
|
|
1981
2120
|
buildCustomProviderStartUrl(startUrl, origin) {
|
|
1982
2121
|
const value = startUrl.trim();
|
|
1983
|
-
if (!value || /^(undefined|null)(\/|$)/i.test(value))
|
|
2122
|
+
if (!value || /^(undefined|null)(\/|$)/i.test(value))
|
|
2123
|
+
return null;
|
|
1984
2124
|
try {
|
|
1985
2125
|
const url = new URL(value, origin);
|
|
1986
|
-
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
2126
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
2127
|
+
return null;
|
|
1987
2128
|
return url;
|
|
1988
2129
|
} catch {
|
|
1989
2130
|
return null;
|
|
@@ -2008,13 +2149,14 @@ var AdminAuthController = class {
|
|
|
2008
2149
|
return new TextEncoder2().encode(secret);
|
|
2009
2150
|
}
|
|
2010
2151
|
normalizeReturnTo(returnTo, c) {
|
|
2011
|
-
if (returnTo)
|
|
2152
|
+
if (returnTo)
|
|
2153
|
+
return returnTo;
|
|
2012
2154
|
const url = new URL(c.req.url);
|
|
2013
2155
|
return `${url.origin}/admin`;
|
|
2014
2156
|
}
|
|
2015
2157
|
};
|
|
2016
2158
|
|
|
2017
|
-
// src/controllers/preview.controller.
|
|
2159
|
+
// src/controllers/preview.controller.js
|
|
2018
2160
|
import { SignJWT as SignJWT3, jwtVerify as jwtVerify3 } from "jose";
|
|
2019
2161
|
import { TextEncoder as TextEncoder3 } from "util";
|
|
2020
2162
|
var PreviewController = class {
|
|
@@ -2057,7 +2199,7 @@ var PreviewController = class {
|
|
|
2057
2199
|
}
|
|
2058
2200
|
};
|
|
2059
2201
|
|
|
2060
|
-
// src/middleware/auth.
|
|
2202
|
+
// src/middleware/auth.js
|
|
2061
2203
|
function requireAuth() {
|
|
2062
2204
|
return async (c, next) => {
|
|
2063
2205
|
const authHeader = c.req.header("Authorization");
|
|
@@ -2089,7 +2231,7 @@ function optionalAuth() {
|
|
|
2089
2231
|
};
|
|
2090
2232
|
}
|
|
2091
2233
|
|
|
2092
|
-
// src/utils/swagger.
|
|
2234
|
+
// src/utils/swagger.js
|
|
2093
2235
|
function getSwaggerHtml(specUrl = "/api/openapi.json") {
|
|
2094
2236
|
return `
|
|
2095
2237
|
<!DOCTYPE html>
|
|
@@ -2138,7 +2280,7 @@ function getSwaggerHtml(specUrl = "/api/openapi.json") {
|
|
|
2138
2280
|
`;
|
|
2139
2281
|
}
|
|
2140
2282
|
|
|
2141
|
-
// src/router.
|
|
2283
|
+
// src/router.js
|
|
2142
2284
|
function accessGate(target, action) {
|
|
2143
2285
|
return async (c, next) => {
|
|
2144
2286
|
const user = c.get("user");
|
|
@@ -2155,7 +2297,8 @@ function accessGate(target, action) {
|
|
|
2155
2297
|
};
|
|
2156
2298
|
}
|
|
2157
2299
|
async function checkAccess(access, accessArgs) {
|
|
2158
|
-
if (access === void 0 || access === null)
|
|
2300
|
+
if (access === void 0 || access === null)
|
|
2301
|
+
return true;
|
|
2159
2302
|
if (typeof access === "function") {
|
|
2160
2303
|
try {
|
|
2161
2304
|
const result = await access(accessArgs);
|
|
@@ -2171,7 +2314,8 @@ async function checkAccess(access, accessArgs) {
|
|
|
2171
2314
|
return true;
|
|
2172
2315
|
}
|
|
2173
2316
|
function serializeFieldForApi(f) {
|
|
2174
|
-
if (!f)
|
|
2317
|
+
if (!f)
|
|
2318
|
+
return f;
|
|
2175
2319
|
const serialized = { ...f };
|
|
2176
2320
|
if (serialized.admin?.hooks) {
|
|
2177
2321
|
const hooks = { ...serialized.admin.hooks };
|
|
@@ -2204,8 +2348,10 @@ function registerRoutes(app, config) {
|
|
|
2204
2348
|
let globals = [...config.globals];
|
|
2205
2349
|
if (siteId && config.onSchemaFetch) {
|
|
2206
2350
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
2207
|
-
if (dynamic.collections)
|
|
2208
|
-
|
|
2351
|
+
if (dynamic.collections)
|
|
2352
|
+
collections = [...collections, ...dynamic.collections];
|
|
2353
|
+
if (dynamic.globals)
|
|
2354
|
+
globals = [...globals, ...dynamic.globals];
|
|
2209
2355
|
if (dynamic.admin) {
|
|
2210
2356
|
config.admin = { ...config.admin, ...dynamic.admin };
|
|
2211
2357
|
}
|
|
@@ -2216,8 +2362,10 @@ function registerRoutes(app, config) {
|
|
|
2216
2362
|
const user = c.get("user");
|
|
2217
2363
|
const accessArgs = { user, req: c.req, doc: null };
|
|
2218
2364
|
const serializeAccess = async (access) => {
|
|
2219
|
-
if (typeof access === "string")
|
|
2220
|
-
|
|
2365
|
+
if (typeof access === "string")
|
|
2366
|
+
return access;
|
|
2367
|
+
if (typeof access === "boolean")
|
|
2368
|
+
return access;
|
|
2221
2369
|
return checkAccess(access, accessArgs);
|
|
2222
2370
|
};
|
|
2223
2371
|
const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
|
|
@@ -2310,7 +2458,8 @@ function registerRoutes(app, config) {
|
|
|
2310
2458
|
let collections = [...config.collections];
|
|
2311
2459
|
if (siteId && config.onSchemaFetch) {
|
|
2312
2460
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
2313
|
-
if (dynamic.collections)
|
|
2461
|
+
if (dynamic.collections)
|
|
2462
|
+
collections = [...collections, ...dynamic.collections];
|
|
2314
2463
|
}
|
|
2315
2464
|
const user = c.get("user");
|
|
2316
2465
|
let collection = collections.find((col) => col.slug === colSlug);
|
|
@@ -2329,7 +2478,8 @@ function registerRoutes(app, config) {
|
|
|
2329
2478
|
let globals = [...config.globals];
|
|
2330
2479
|
if (siteId && config.onSchemaFetch) {
|
|
2331
2480
|
const dynamic = await config.onSchemaFetch(siteId);
|
|
2332
|
-
if (dynamic.globals)
|
|
2481
|
+
if (dynamic.globals)
|
|
2482
|
+
globals = [...globals, ...dynamic.globals];
|
|
2333
2483
|
}
|
|
2334
2484
|
const glb = globals.find((g) => g.slug === colSlug);
|
|
2335
2485
|
if (!glb) {
|
|
@@ -2387,9 +2537,12 @@ function registerRoutes(app, config) {
|
|
|
2387
2537
|
const user = c.get("user");
|
|
2388
2538
|
const key = c.req.param("key");
|
|
2389
2539
|
const scope = c.req.query("scope");
|
|
2390
|
-
if (!db)
|
|
2391
|
-
|
|
2392
|
-
if (!
|
|
2540
|
+
if (!db)
|
|
2541
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2542
|
+
if (!user?.collection || !user.sub)
|
|
2543
|
+
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
2544
|
+
if (!key)
|
|
2545
|
+
return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
2393
2546
|
const getGlobalPreference = async () => {
|
|
2394
2547
|
const globalDoc = await db.findOne({ collection: "__global_preferences", id: key });
|
|
2395
2548
|
return globalDoc ? globalDoc.value : null;
|
|
@@ -2399,7 +2552,8 @@ function registerRoutes(app, config) {
|
|
|
2399
2552
|
return c.json({ key, value: globalValue2 });
|
|
2400
2553
|
}
|
|
2401
2554
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
2402
|
-
if (!doc)
|
|
2555
|
+
if (!doc)
|
|
2556
|
+
return c.json({ error: true, message: "User not found." }, 404);
|
|
2403
2557
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
|
|
2404
2558
|
if (key in preferences) {
|
|
2405
2559
|
return c.json({ key, value: preferences[key] ?? null });
|
|
@@ -2412,9 +2566,12 @@ function registerRoutes(app, config) {
|
|
|
2412
2566
|
const user = c.get("user");
|
|
2413
2567
|
const key = c.req.param("key");
|
|
2414
2568
|
const scope = c.req.query("scope");
|
|
2415
|
-
if (!db)
|
|
2416
|
-
|
|
2417
|
-
if (!
|
|
2569
|
+
if (!db)
|
|
2570
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2571
|
+
if (!user?.collection || !user.sub)
|
|
2572
|
+
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
2573
|
+
if (!key)
|
|
2574
|
+
return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
2418
2575
|
const body = await c.req.json().catch(() => ({}));
|
|
2419
2576
|
if (scope === "global") {
|
|
2420
2577
|
const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
|
|
@@ -2437,7 +2594,8 @@ function registerRoutes(app, config) {
|
|
|
2437
2594
|
return c.json({ key, value: body.value });
|
|
2438
2595
|
}
|
|
2439
2596
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
2440
|
-
if (!doc)
|
|
2597
|
+
if (!doc)
|
|
2598
|
+
return c.json({ error: true, message: "User not found." }, 404);
|
|
2441
2599
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
|
|
2442
2600
|
const nextPreferences = { ...preferences, [key]: body.value };
|
|
2443
2601
|
await db.update({
|
|
@@ -2452,9 +2610,12 @@ function registerRoutes(app, config) {
|
|
|
2452
2610
|
const user = c.get("user");
|
|
2453
2611
|
const key = c.req.param("key");
|
|
2454
2612
|
const scope = c.req.query("scope");
|
|
2455
|
-
if (!db)
|
|
2456
|
-
|
|
2457
|
-
if (!
|
|
2613
|
+
if (!db)
|
|
2614
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2615
|
+
if (!user?.collection || !user.sub)
|
|
2616
|
+
return c.json({ error: true, message: "Authentication required." }, 401);
|
|
2617
|
+
if (!key)
|
|
2618
|
+
return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
2458
2619
|
if (scope === "global") {
|
|
2459
2620
|
const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
|
|
2460
2621
|
if (!isAdminUser) {
|
|
@@ -2464,7 +2625,8 @@ function registerRoutes(app, config) {
|
|
|
2464
2625
|
return c.json({ success: true });
|
|
2465
2626
|
}
|
|
2466
2627
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
2467
|
-
if (!doc)
|
|
2628
|
+
if (!doc)
|
|
2629
|
+
return c.json({ error: true, message: "User not found." }, 404);
|
|
2468
2630
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? { ...doc.__preferences } : {};
|
|
2469
2631
|
delete preferences[key];
|
|
2470
2632
|
await db.update({
|
|
@@ -2500,7 +2662,8 @@ function registerRoutes(app, config) {
|
|
|
2500
2662
|
app.post("/api/admin/auth/:provider/exchange", (c) => adminAuthController.exchange(c));
|
|
2501
2663
|
app.post("/api/admin/logout", (c) => adminAuthController.logout(c));
|
|
2502
2664
|
for (const collection of config.collections) {
|
|
2503
|
-
if (!collection.auth)
|
|
2665
|
+
if (!collection.auth)
|
|
2666
|
+
continue;
|
|
2504
2667
|
const path = `/api/collections/${collection.slug}`;
|
|
2505
2668
|
const authController = new AuthController(collection);
|
|
2506
2669
|
app.post(`${path}/login`, (c) => authController.login(c));
|
|
@@ -2602,25 +2765,39 @@ function registerRoutes(app, config) {
|
|
|
2602
2765
|
if (collection.auth && id) {
|
|
2603
2766
|
const authController = new AuthController(collection);
|
|
2604
2767
|
const method2 = c.req.method;
|
|
2605
|
-
if (method2 === "POST" && id === "login")
|
|
2606
|
-
|
|
2607
|
-
if (method2 === "
|
|
2608
|
-
|
|
2609
|
-
if (method2 === "
|
|
2610
|
-
|
|
2768
|
+
if (method2 === "POST" && id === "login")
|
|
2769
|
+
return authController.login(c);
|
|
2770
|
+
if (method2 === "POST" && id === "logout")
|
|
2771
|
+
return authController.logout(c);
|
|
2772
|
+
if (method2 === "GET" && id === "me")
|
|
2773
|
+
return authController.me(c);
|
|
2774
|
+
if (method2 === "POST" && id === "refresh-token")
|
|
2775
|
+
return authController.refreshToken(c);
|
|
2776
|
+
if (method2 === "POST" && id === "forgot-password")
|
|
2777
|
+
return authController.forgotPassword(c);
|
|
2778
|
+
if (method2 === "POST" && id === "reset-password")
|
|
2779
|
+
return authController.resetPassword(c);
|
|
2611
2780
|
}
|
|
2612
2781
|
const controller = new CollectionController(collection);
|
|
2613
2782
|
const method = c.req.method;
|
|
2614
2783
|
if (id) {
|
|
2615
|
-
if (method === "GET")
|
|
2616
|
-
|
|
2617
|
-
if (method === "
|
|
2618
|
-
|
|
2619
|
-
if (method === "
|
|
2620
|
-
|
|
2784
|
+
if (method === "GET")
|
|
2785
|
+
return controller.findOne(c);
|
|
2786
|
+
if (method === "PATCH")
|
|
2787
|
+
return controller.update(c);
|
|
2788
|
+
if (method === "DELETE" && id === "delete-many")
|
|
2789
|
+
return controller.deleteMany(c);
|
|
2790
|
+
if (method === "DELETE")
|
|
2791
|
+
return controller.delete(c);
|
|
2792
|
+
if (method === "POST" && id === "media")
|
|
2793
|
+
return controller.create(c);
|
|
2794
|
+
if (method === "POST" && id === "seed")
|
|
2795
|
+
return controller.seed(c);
|
|
2621
2796
|
} else {
|
|
2622
|
-
if (method === "GET")
|
|
2623
|
-
|
|
2797
|
+
if (method === "GET")
|
|
2798
|
+
return controller.find(c);
|
|
2799
|
+
if (method === "POST")
|
|
2800
|
+
return controller.create(c);
|
|
2624
2801
|
}
|
|
2625
2802
|
}
|
|
2626
2803
|
}
|
|
@@ -2641,10 +2818,13 @@ function registerRoutes(app, config) {
|
|
|
2641
2818
|
const controller = new GlobalController(global);
|
|
2642
2819
|
const method = c.req.method;
|
|
2643
2820
|
if (id) {
|
|
2644
|
-
if (method === "POST" && id === "seed")
|
|
2821
|
+
if (method === "POST" && id === "seed")
|
|
2822
|
+
return controller.seed(c);
|
|
2645
2823
|
} else {
|
|
2646
|
-
if (method === "GET")
|
|
2647
|
-
|
|
2824
|
+
if (method === "GET")
|
|
2825
|
+
return controller.get(c);
|
|
2826
|
+
if (method === "PATCH")
|
|
2827
|
+
return controller.update(c);
|
|
2648
2828
|
}
|
|
2649
2829
|
}
|
|
2650
2830
|
}
|
|
@@ -2652,7 +2832,7 @@ function registerRoutes(app, config) {
|
|
|
2652
2832
|
});
|
|
2653
2833
|
}
|
|
2654
2834
|
|
|
2655
|
-
// src/app.
|
|
2835
|
+
// src/app.js
|
|
2656
2836
|
async function createDyrectedApp(rawConfig) {
|
|
2657
2837
|
const config = normalizeConfig(rawConfig);
|
|
2658
2838
|
const app = new Hono();
|