@bobbykim/manguito-cms-api 0.8.0 → 0.8.2
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/index.js +230 -184
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +230 -184
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1255,7 +1255,7 @@ function createVersionedPaths(prefix, version) {
|
|
|
1255
1255
|
|
|
1256
1256
|
// src/app.ts
|
|
1257
1257
|
var import_hono = require("hono");
|
|
1258
|
-
var
|
|
1258
|
+
var import_drizzle_orm12 = require("drizzle-orm");
|
|
1259
1259
|
|
|
1260
1260
|
// src/middleware/cors.ts
|
|
1261
1261
|
function createCorsMiddleware(corsConfig) {
|
|
@@ -2270,7 +2270,7 @@ function registerPublicMediaRoutes(app, mediaRepo, paths, listRateLimit) {
|
|
|
2270
2270
|
}
|
|
2271
2271
|
|
|
2272
2272
|
// src/routes/admin/content.ts
|
|
2273
|
-
var
|
|
2273
|
+
var import_drizzle_orm5 = require("drizzle-orm");
|
|
2274
2274
|
|
|
2275
2275
|
// src/references-in-use.ts
|
|
2276
2276
|
var import_drizzle_orm2 = require("drizzle-orm");
|
|
@@ -2358,6 +2358,7 @@ async function applyMediaReferenceDelta(delta, mediaRepo) {
|
|
|
2358
2358
|
init_relations();
|
|
2359
2359
|
|
|
2360
2360
|
// src/relation-input.ts
|
|
2361
|
+
var import_drizzle_orm4 = require("drizzle-orm");
|
|
2361
2362
|
var import_manguito_cms_core3 = require("@bobbykim/manguito-cms-core");
|
|
2362
2363
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2363
2364
|
function isUuid(v) {
|
|
@@ -2402,16 +2403,92 @@ function checkRelationInput(fields, body, registry, path = "") {
|
|
|
2402
2403
|
}
|
|
2403
2404
|
return errors;
|
|
2404
2405
|
}
|
|
2406
|
+
function paragraphItemsIn(fields, body, registry, path) {
|
|
2407
|
+
const out = [];
|
|
2408
|
+
for (const field of fields) {
|
|
2409
|
+
if (field.field_type !== "paragraph" || !(field.name in body)) continue;
|
|
2410
|
+
const comp = field.ui_component;
|
|
2411
|
+
const pType = comp.component === "paragraph-embed" ? registry.paragraph_types[comp.ref] : void 0;
|
|
2412
|
+
if (!pType) continue;
|
|
2413
|
+
const value = body[field.name];
|
|
2414
|
+
const name = `${path}${field.name}`;
|
|
2415
|
+
if ((0, import_manguito_cms_core3.relationCardinality)(field) === "one") {
|
|
2416
|
+
if (isPlainObject(value)) out.push({ item: value, fields: pType.fields, prefix: `${name}.` });
|
|
2417
|
+
} else if (Array.isArray(value)) {
|
|
2418
|
+
value.forEach((item, i) => {
|
|
2419
|
+
if (isPlainObject(item)) out.push({ item, fields: pType.fields, prefix: `${name}[${i}].` });
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
return out;
|
|
2424
|
+
}
|
|
2425
|
+
function isMissing(value) {
|
|
2426
|
+
if (value === null || value === void 0) return true;
|
|
2427
|
+
if (typeof value === "string" && value.trim() === "") return true;
|
|
2428
|
+
if (Array.isArray(value) && value.length === 0) return true;
|
|
2429
|
+
return false;
|
|
2430
|
+
}
|
|
2431
|
+
function checkRequiredInParagraphItems(fields, body, registry, path = "") {
|
|
2432
|
+
const errors = [];
|
|
2433
|
+
for (const { item, fields: itemFields, prefix } of paragraphItemsIn(fields, body, registry, path)) {
|
|
2434
|
+
for (const f of itemFields) {
|
|
2435
|
+
if (!f.required || f.removed === true || !f.db_column || f.db_column.junction) continue;
|
|
2436
|
+
if (isMissing(item[f.name])) {
|
|
2437
|
+
errors.push({ field: `${prefix}${f.name}`, message: `${prefix}${f.name} is required.` });
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
errors.push(...checkRequiredInParagraphItems(itemFields, item, registry, prefix));
|
|
2441
|
+
}
|
|
2442
|
+
return errors;
|
|
2443
|
+
}
|
|
2444
|
+
function quoteIdent3(name) {
|
|
2445
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
2446
|
+
return `"${name}"`;
|
|
2447
|
+
}
|
|
2448
|
+
function collectReferences(fields, body, registry, path, out) {
|
|
2449
|
+
for (const f of fields) {
|
|
2450
|
+
if (!(f.name in body) || !f.db_column) continue;
|
|
2451
|
+
const table = f.db_column.junction ? f.db_column.junction.right_table : f.db_column.foreign_key?.table;
|
|
2452
|
+
if (!table) continue;
|
|
2453
|
+
const value = body[f.name];
|
|
2454
|
+
const name = `${path}${f.name}`;
|
|
2455
|
+
if (typeof value === "string") {
|
|
2456
|
+
out.push({ table, id: value, field: name });
|
|
2457
|
+
} else if (Array.isArray(value)) {
|
|
2458
|
+
value.forEach((v, i) => {
|
|
2459
|
+
if (typeof v === "string") out.push({ table, id: v, field: `${name}[${i}]` });
|
|
2460
|
+
});
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
for (const { item, fields: itemFields, prefix } of paragraphItemsIn(fields, body, registry, path)) {
|
|
2464
|
+
collectReferences(itemFields, item, registry, prefix, out);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
async function findMissingReferences(db, fields, body, registry) {
|
|
2468
|
+
const uses = [];
|
|
2469
|
+
collectReferences(fields, body, registry, "", uses);
|
|
2470
|
+
if (uses.length === 0) return [];
|
|
2471
|
+
const lookups = [...new Set(uses.map((u) => u.table))].map(async (table) => {
|
|
2472
|
+
const ids = [...new Set(uses.filter((u) => u.table === table && isUuid(u.id)).map((u) => u.id.toLowerCase()))];
|
|
2473
|
+
if (ids.length === 0) return [table, /* @__PURE__ */ new Set()];
|
|
2474
|
+
const result = await db.execute(
|
|
2475
|
+
import_drizzle_orm4.sql`SELECT id FROM ${import_drizzle_orm4.sql.raw(quoteIdent3(table))} WHERE id IN (${import_drizzle_orm4.sql.join(ids.map((id) => import_drizzle_orm4.sql`${id}`), import_drizzle_orm4.sql`, `)})`
|
|
2476
|
+
);
|
|
2477
|
+
return [table, new Set(result.rows.map((r) => String(r.id).toLowerCase()))];
|
|
2478
|
+
});
|
|
2479
|
+
const found = new Map(await Promise.all(lookups));
|
|
2480
|
+
return uses.filter((u) => !found.get(u.table).has(u.id.toLowerCase())).map((u) => ({ field: u.field, message: `${u.field} refers to an item that does not exist.` }));
|
|
2481
|
+
}
|
|
2405
2482
|
|
|
2406
2483
|
// src/routes/admin/content.ts
|
|
2407
2484
|
init_field_keys();
|
|
2408
|
-
function
|
|
2485
|
+
function quoteIdent4(name) {
|
|
2409
2486
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
2410
2487
|
return `"${name}"`;
|
|
2411
2488
|
}
|
|
2412
2489
|
async function loadParagraphRows(db, registry, pType, parentId, fieldName) {
|
|
2413
2490
|
const result = await db.execute(
|
|
2414
|
-
|
|
2491
|
+
import_drizzle_orm5.sql`SELECT * FROM ${import_drizzle_orm5.sql.raw(quoteIdent4(pType.db.table_name))} WHERE parent_id = ${parentId} AND parent_field = ${fieldName} ORDER BY "order" ASC`
|
|
2415
2492
|
);
|
|
2416
2493
|
const rows = result.rows;
|
|
2417
2494
|
for (const nf of pType.fields) {
|
|
@@ -2429,29 +2506,67 @@ async function loadParagraphRows(db, registry, pType, parentId, fieldName) {
|
|
|
2429
2506
|
}
|
|
2430
2507
|
return rows;
|
|
2431
2508
|
}
|
|
2509
|
+
async function loadRelationsForEdit(db, registry, fields, id, row) {
|
|
2510
|
+
for (const f of fields) {
|
|
2511
|
+
if (f.db_column === null) {
|
|
2512
|
+
const comp = f.ui_component;
|
|
2513
|
+
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
2514
|
+
const pType = registry.paragraph_types[comp.ref];
|
|
2515
|
+
if (!pType) continue;
|
|
2516
|
+
row[f.name] = oneOrMany((0, import_manguito_cms_core4.relationCardinality)(f) ?? "many", await loadParagraphRows(db, registry, pType, id, f.name));
|
|
2517
|
+
} else if (f.db_column.junction) {
|
|
2518
|
+
const j = f.db_column.junction;
|
|
2519
|
+
const r = await db.execute(
|
|
2520
|
+
import_drizzle_orm5.sql`SELECT ${import_drizzle_orm5.sql.raw(quoteIdent4(j.right_column))} FROM ${import_drizzle_orm5.sql.raw(quoteIdent4(j.table_name))} WHERE ${import_drizzle_orm5.sql.raw(quoteIdent4(j.left_column))} = ${id}`
|
|
2521
|
+
);
|
|
2522
|
+
row[f.name] = r.rows.map((linked) => linked[j.right_column]);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
async function valuesForPublishCheck(db, registry, requiredFields, id, storedLabels, body) {
|
|
2527
|
+
const stored = { ...storedLabels };
|
|
2528
|
+
if (db) await loadRelationsForEdit(db, registry, requiredFields.filter((f) => !(f.name in body)), id, stored);
|
|
2529
|
+
return { ...stored, ...body };
|
|
2530
|
+
}
|
|
2531
|
+
async function persistRelationFields(db, registry, fields, body, itemId, ownerTable) {
|
|
2532
|
+
const deltas = [];
|
|
2533
|
+
for (const f of fields) {
|
|
2534
|
+
if (!(f.name in body)) continue;
|
|
2535
|
+
if (f.db_column === null) {
|
|
2536
|
+
const comp = f.ui_component;
|
|
2537
|
+
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
2538
|
+
const pType = registry.paragraph_types[comp.ref];
|
|
2539
|
+
if (!pType) continue;
|
|
2540
|
+
deltas.push(await persistParagraphField(db, itemId, ownerTable, f.name, pType, paragraphItems(f, body[f.name]), registry));
|
|
2541
|
+
} else if (f.db_column.junction) {
|
|
2542
|
+
const value = body[f.name];
|
|
2543
|
+
const relatedIds = Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
2544
|
+
await persistJunctionField(db, itemId, f.db_column.junction, relatedIds);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
return deltas;
|
|
2548
|
+
}
|
|
2432
2549
|
async function lookupBasePathId(db, pathOrName) {
|
|
2433
2550
|
const r = await db.execute(
|
|
2434
|
-
|
|
2551
|
+
import_drizzle_orm5.sql`SELECT id FROM base_paths WHERE path = ${pathOrName} OR name = ${pathOrName} LIMIT 1`
|
|
2435
2552
|
);
|
|
2436
2553
|
return r.rows[0]?.id ?? null;
|
|
2437
2554
|
}
|
|
2438
2555
|
var SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2439
|
-
function isEmpty(value) {
|
|
2440
|
-
if (value === null || value === void 0) return true;
|
|
2441
|
-
if (typeof value === "string" && value.trim() === "") return true;
|
|
2442
|
-
if (Array.isArray(value) && value.length === 0) return true;
|
|
2443
|
-
return false;
|
|
2444
|
-
}
|
|
2445
2556
|
function checkRequiredFields(fields, data) {
|
|
2446
|
-
return fields.filter((f) => f.required &&
|
|
2447
|
-
}
|
|
2448
|
-
function
|
|
2449
|
-
const
|
|
2450
|
-
|
|
2451
|
-
return
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
)
|
|
2557
|
+
return fields.filter((f) => f.required && isMissing(data[f.name])).map((f) => ({ field: f.name, message: `${f.label} is required` }));
|
|
2558
|
+
}
|
|
2559
|
+
async function relationInputError(c, fields, body, registry, db) {
|
|
2560
|
+
const refuse = (message, details) => c.json({ ok: false, error: { code: "VALIDATION_ERROR", message, details } }, 422);
|
|
2561
|
+
const shape = checkRelationInput(fields, body, registry);
|
|
2562
|
+
if (shape.length > 0) return refuse("Relation fields have the wrong shape", shape);
|
|
2563
|
+
const required = checkRequiredInParagraphItems(fields, body, registry);
|
|
2564
|
+
if (required.length > 0) return refuse("Required fields are missing", required);
|
|
2565
|
+
if (db) {
|
|
2566
|
+
const missing = await findMissingReferences(db, fields, body, registry);
|
|
2567
|
+
if (missing.length > 0) return refuse("Relation fields refer to items that do not exist", missing);
|
|
2568
|
+
}
|
|
2569
|
+
return null;
|
|
2455
2570
|
}
|
|
2456
2571
|
function parseListQuery(url, schemaFieldNames, relationFieldNames, columnFor) {
|
|
2457
2572
|
const searchParams = new URL(url).searchParams;
|
|
@@ -2586,26 +2701,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
2586
2701
|
404
|
|
2587
2702
|
);
|
|
2588
2703
|
}
|
|
2589
|
-
if (db)
|
|
2590
|
-
const row = item;
|
|
2591
|
-
for (const f of contentType.fields) {
|
|
2592
|
-
if (f.db_column === null) {
|
|
2593
|
-
const comp = f.ui_component;
|
|
2594
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
2595
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
2596
|
-
if (!pType) continue;
|
|
2597
|
-
row[f.name] = oneOrMany((0, import_manguito_cms_core4.relationCardinality)(f) ?? "many", await loadParagraphRows(db, registry, pType, id, f.name));
|
|
2598
|
-
} else if (f.db_column.junction) {
|
|
2599
|
-
const j = f.db_column.junction;
|
|
2600
|
-
const r = await db.execute(
|
|
2601
|
-
import_drizzle_orm4.sql`SELECT ${import_drizzle_orm4.sql.raw(quoteIdent3(j.right_column))} FROM ${import_drizzle_orm4.sql.raw(quoteIdent3(j.table_name))} WHERE ${import_drizzle_orm4.sql.raw(quoteIdent3(j.left_column))} = ${id}`
|
|
2602
|
-
);
|
|
2603
|
-
row[f.name] = r.rows.map(
|
|
2604
|
-
(row2) => row2[j.right_column]
|
|
2605
|
-
);
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
}
|
|
2704
|
+
if (db) await loadRelationsForEdit(db, registry, contentType.fields, id, item);
|
|
2609
2705
|
const data = projectRow(item, typeName, projectors);
|
|
2610
2706
|
return c.json({ ok: true, data });
|
|
2611
2707
|
}
|
|
@@ -2674,8 +2770,8 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
2674
2770
|
}
|
|
2675
2771
|
);
|
|
2676
2772
|
const writeNewItem = async (c, body) => {
|
|
2677
|
-
const
|
|
2678
|
-
if (
|
|
2773
|
+
const inputError = await relationInputError(c, contentType.fields, body, registry, db);
|
|
2774
|
+
if (inputError) return inputError;
|
|
2679
2775
|
const storageBody = fieldKeys.toStorage(body);
|
|
2680
2776
|
const fieldErrors = checkRequiredFields(requiredFields, body);
|
|
2681
2777
|
if (fieldErrors.length > 0) {
|
|
@@ -2696,10 +2792,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
2696
2792
|
});
|
|
2697
2793
|
if (publishDeny) return publishDeny;
|
|
2698
2794
|
}
|
|
2699
|
-
const paragraphFields = contentType.fields.filter((f) => f.db_column === null);
|
|
2700
|
-
const junctionFields = contentType.fields.filter(
|
|
2701
|
-
(f) => f.db_column !== null && f.db_column.junction !== void 0
|
|
2702
|
-
);
|
|
2703
2795
|
const columnBackedFields = contentType.fields.filter(isColumnBacked);
|
|
2704
2796
|
const columnData = {
|
|
2705
2797
|
published: body["published"] ?? false
|
|
@@ -2734,19 +2826,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
2734
2826
|
const itemId = item["id"];
|
|
2735
2827
|
const mediaDeltas = [topLevelMediaDelta(mediaFields, null, storageBody)];
|
|
2736
2828
|
if (db) {
|
|
2737
|
-
|
|
2738
|
-
const comp = f.ui_component;
|
|
2739
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
2740
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
2741
|
-
if (!pType) continue;
|
|
2742
|
-
const items = paragraphItems(f, body[f.name]);
|
|
2743
|
-
mediaDeltas.push(await persistParagraphField(db, itemId, contentType.db.table_name, f.name, pType, items, registry));
|
|
2744
|
-
}
|
|
2745
|
-
for (const f of junctionFields) {
|
|
2746
|
-
const junction = f.db_column.junction;
|
|
2747
|
-
const relatedIds = Array.isArray(body[f.name]) ? body[f.name].filter((v) => typeof v === "string") : [];
|
|
2748
|
-
await persistJunctionField(db, itemId, junction, relatedIds);
|
|
2749
|
-
}
|
|
2829
|
+
mediaDeltas.push(...await persistRelationFields(db, registry, contentType.fields, body, itemId, contentType.db.table_name));
|
|
2750
2830
|
}
|
|
2751
2831
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
2752
2832
|
const data = projectRow(item, typeName, projectors);
|
|
@@ -2810,14 +2890,14 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
2810
2890
|
}
|
|
2811
2891
|
);
|
|
2812
2892
|
const writeExistingItem = async (c, id, existing, body) => {
|
|
2813
|
-
const
|
|
2814
|
-
if (
|
|
2893
|
+
const inputError = await relationInputError(c, contentType.fields, body, registry, db);
|
|
2894
|
+
if (inputError) return inputError;
|
|
2815
2895
|
const storageBody = fieldKeys.toStorage(body);
|
|
2816
2896
|
if (body["published"] === true) {
|
|
2817
2897
|
const publishDeny = await requirePermission("content:edit")(c, async () => {
|
|
2818
2898
|
});
|
|
2819
2899
|
if (publishDeny) return publishDeny;
|
|
2820
|
-
const merged =
|
|
2900
|
+
const merged = await valuesForPublishCheck(db, registry, requiredFields, id, fieldKeys.toLabels(existing), body);
|
|
2821
2901
|
const fieldErrors = checkRequiredFields(requiredFields, merged);
|
|
2822
2902
|
if (fieldErrors.length > 0) {
|
|
2823
2903
|
return c.json(
|
|
@@ -2833,10 +2913,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
2833
2913
|
);
|
|
2834
2914
|
}
|
|
2835
2915
|
}
|
|
2836
|
-
const patchParagraphFields = contentType.fields.filter((f) => f.db_column === null);
|
|
2837
|
-
const patchJunctionFields = contentType.fields.filter(
|
|
2838
|
-
(f) => f.db_column !== null && f.db_column.junction !== void 0
|
|
2839
|
-
);
|
|
2840
2916
|
const patchColumnBackedFields = contentType.fields.filter(isColumnBacked);
|
|
2841
2917
|
const patchData = {};
|
|
2842
2918
|
if (!contentType.only_one && "slug" in body) patchData["slug"] = body["slug"];
|
|
@@ -2857,21 +2933,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
2857
2933
|
topLevelMediaDelta(mediaFields, existing, storageBody)
|
|
2858
2934
|
];
|
|
2859
2935
|
if (db) {
|
|
2860
|
-
|
|
2861
|
-
if (!(f.name in body)) continue;
|
|
2862
|
-
const comp = f.ui_component;
|
|
2863
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
2864
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
2865
|
-
if (!pType) continue;
|
|
2866
|
-
const items = paragraphItems(f, body[f.name]);
|
|
2867
|
-
mediaDeltas.push(await persistParagraphField(db, id, contentType.db.table_name, f.name, pType, items, registry));
|
|
2868
|
-
}
|
|
2869
|
-
for (const f of patchJunctionFields) {
|
|
2870
|
-
if (!(f.name in body)) continue;
|
|
2871
|
-
const junction = f.db_column.junction;
|
|
2872
|
-
const relatedIds = Array.isArray(body[f.name]) ? body[f.name].filter((v) => typeof v === "string") : [];
|
|
2873
|
-
await persistJunctionField(db, id, junction, relatedIds);
|
|
2874
|
-
}
|
|
2936
|
+
mediaDeltas.push(...await persistRelationFields(db, registry, contentType.fields, body, id, contentType.db.table_name));
|
|
2875
2937
|
}
|
|
2876
2938
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
2877
2939
|
const data = projectRow(updated, typeName, projectors);
|
|
@@ -3000,6 +3062,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
3000
3062
|
404
|
|
3001
3063
|
);
|
|
3002
3064
|
}
|
|
3065
|
+
if (db) await loadRelationsForEdit(db, registry, taxonomyType.fields, id, item);
|
|
3003
3066
|
const data = projectRow(item, typeName, projectors);
|
|
3004
3067
|
return c.json({ ok: true, data });
|
|
3005
3068
|
}
|
|
@@ -3009,8 +3072,8 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
3009
3072
|
requirePermission("content:create"),
|
|
3010
3073
|
async (c) => {
|
|
3011
3074
|
const body = await c.req.json();
|
|
3012
|
-
const
|
|
3013
|
-
if (
|
|
3075
|
+
const inputError = await relationInputError(c, taxonomyType.fields, body, registry, db);
|
|
3076
|
+
if (inputError) return inputError;
|
|
3014
3077
|
const storageBody = fieldKeys.toStorage(body);
|
|
3015
3078
|
const fieldErrors = checkRequiredFields(requiredFields, body);
|
|
3016
3079
|
if (fieldErrors.length > 0) {
|
|
@@ -3031,7 +3094,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
3031
3094
|
});
|
|
3032
3095
|
if (publishDeny) return publishDeny;
|
|
3033
3096
|
}
|
|
3034
|
-
const taxParagraphFields = taxonomyType.fields.filter((f) => f.db_column === null);
|
|
3035
3097
|
const taxColumnBackedFields = taxonomyType.fields.filter(isColumnBacked);
|
|
3036
3098
|
const taxColumnData = {
|
|
3037
3099
|
published: body["published"] ?? false
|
|
@@ -3045,14 +3107,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
3045
3107
|
const taxItemId = item["id"];
|
|
3046
3108
|
const mediaDeltas = [topLevelMediaDelta(mediaFields, null, storageBody)];
|
|
3047
3109
|
if (db) {
|
|
3048
|
-
|
|
3049
|
-
const comp = f.ui_component;
|
|
3050
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
3051
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
3052
|
-
if (!pType) continue;
|
|
3053
|
-
const items = paragraphItems(f, body[f.name]);
|
|
3054
|
-
mediaDeltas.push(await persistParagraphField(db, taxItemId, taxonomyType.db.table_name, f.name, pType, items, registry));
|
|
3055
|
-
}
|
|
3110
|
+
mediaDeltas.push(...await persistRelationFields(db, registry, taxonomyType.fields, body, taxItemId, taxonomyType.db.table_name));
|
|
3056
3111
|
}
|
|
3057
3112
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
3058
3113
|
const data = projectRow(item, typeName, projectors);
|
|
@@ -3073,13 +3128,13 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
3073
3128
|
404
|
|
3074
3129
|
);
|
|
3075
3130
|
}
|
|
3076
|
-
const
|
|
3077
|
-
if (
|
|
3131
|
+
const inputError = await relationInputError(c, taxonomyType.fields, body, registry, db);
|
|
3132
|
+
if (inputError) return inputError;
|
|
3078
3133
|
if (body["published"] === true) {
|
|
3079
3134
|
const publishDeny = await requirePermission("content:edit")(c, async () => {
|
|
3080
3135
|
});
|
|
3081
3136
|
if (publishDeny) return publishDeny;
|
|
3082
|
-
const merged =
|
|
3137
|
+
const merged = await valuesForPublishCheck(db, registry, requiredFields, id, fieldKeys.toLabels(existing), body);
|
|
3083
3138
|
const fieldErrors = checkRequiredFields(requiredFields, merged);
|
|
3084
3139
|
if (fieldErrors.length > 0) {
|
|
3085
3140
|
return c.json(
|
|
@@ -3095,7 +3150,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
3095
3150
|
);
|
|
3096
3151
|
}
|
|
3097
3152
|
}
|
|
3098
|
-
const taxPatchParagraphFields = taxonomyType.fields.filter((f) => f.db_column === null);
|
|
3099
3153
|
const taxPatchColumnBackedFields = taxonomyType.fields.filter(isColumnBacked);
|
|
3100
3154
|
const taxPatchData = {};
|
|
3101
3155
|
if ("published" in body) taxPatchData["published"] = body["published"];
|
|
@@ -3115,15 +3169,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
3115
3169
|
topLevelMediaDelta(mediaFields, existing, storageBody)
|
|
3116
3170
|
];
|
|
3117
3171
|
if (db) {
|
|
3118
|
-
|
|
3119
|
-
if (!(f.name in body)) continue;
|
|
3120
|
-
const comp = f.ui_component;
|
|
3121
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
3122
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
3123
|
-
if (!pType) continue;
|
|
3124
|
-
const items = paragraphItems(f, body[f.name]);
|
|
3125
|
-
mediaDeltas.push(await persistParagraphField(db, id, taxonomyType.db.table_name, f.name, pType, items, registry));
|
|
3126
|
-
}
|
|
3172
|
+
mediaDeltas.push(...await persistRelationFields(db, registry, taxonomyType.fields, body, id, taxonomyType.db.table_name));
|
|
3127
3173
|
}
|
|
3128
3174
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
3129
3175
|
const data = projectRow(updated, typeName, projectors);
|
|
@@ -3628,7 +3674,7 @@ function registerAdminMediaRoutes(app, mediaRepo, storage, requirePermission, ma
|
|
|
3628
3674
|
|
|
3629
3675
|
// src/routes/admin/auth.ts
|
|
3630
3676
|
var import_cookie3 = require("hono/cookie");
|
|
3631
|
-
var
|
|
3677
|
+
var import_drizzle_orm6 = require("drizzle-orm");
|
|
3632
3678
|
|
|
3633
3679
|
// src/auth/password.ts
|
|
3634
3680
|
var import_manguito_cms_core5 = require("@bobbykim/manguito-cms-core");
|
|
@@ -3694,7 +3740,7 @@ function registerAuthRoutes(app, db) {
|
|
|
3694
3740
|
);
|
|
3695
3741
|
}
|
|
3696
3742
|
const userResult = await db.execute(
|
|
3697
|
-
|
|
3743
|
+
import_drizzle_orm6.sql`SELECT id, email, password_hash, role_id, token_version
|
|
3698
3744
|
FROM users WHERE email = ${email} LIMIT 1`
|
|
3699
3745
|
);
|
|
3700
3746
|
const user = userResult.rows[0];
|
|
@@ -3707,7 +3753,7 @@ function registerAuthRoutes(app, db) {
|
|
|
3707
3753
|
return c.json(invalidCredentials, 401);
|
|
3708
3754
|
}
|
|
3709
3755
|
const roleResult = await db.execute(
|
|
3710
|
-
|
|
3756
|
+
import_drizzle_orm6.sql`SELECT r.name AS name, u.must_change_password
|
|
3711
3757
|
FROM roles r
|
|
3712
3758
|
JOIN users u ON u.role_id = r.id
|
|
3713
3759
|
WHERE r.id = ${user.role_id} AND u.id = ${user.id}
|
|
@@ -3767,7 +3813,7 @@ function registerAuthRoutes(app, db) {
|
|
|
3767
3813
|
);
|
|
3768
3814
|
}
|
|
3769
3815
|
const rowResult = await db.execute(
|
|
3770
|
-
|
|
3816
|
+
import_drizzle_orm6.sql`SELECT u.token_version, r.name AS role
|
|
3771
3817
|
FROM users u
|
|
3772
3818
|
JOIN roles r ON r.id = u.role_id
|
|
3773
3819
|
WHERE u.id = ${payload.user_id}
|
|
@@ -3791,7 +3837,7 @@ function registerAuthRoutes(app, db) {
|
|
|
3791
3837
|
app.post("/admin/api/auth/logout", authMiddleware, async (c) => {
|
|
3792
3838
|
const user = c.get("user");
|
|
3793
3839
|
await db.execute(
|
|
3794
|
-
|
|
3840
|
+
import_drizzle_orm6.sql`UPDATE users SET token_version = token_version + 1 WHERE id = ${user.id}`
|
|
3795
3841
|
);
|
|
3796
3842
|
clearAuthCookie(c, "auth_token");
|
|
3797
3843
|
clearAuthCookie(c, "refresh_token", "/admin/api/auth");
|
|
@@ -3800,7 +3846,7 @@ function registerAuthRoutes(app, db) {
|
|
|
3800
3846
|
}
|
|
3801
3847
|
|
|
3802
3848
|
// src/routes/admin/users.ts
|
|
3803
|
-
var
|
|
3849
|
+
var import_drizzle_orm7 = require("drizzle-orm");
|
|
3804
3850
|
var import_node_crypto2 = require("crypto");
|
|
3805
3851
|
function getActingUser(c) {
|
|
3806
3852
|
return c.get("user");
|
|
@@ -3835,7 +3881,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3835
3881
|
);
|
|
3836
3882
|
}
|
|
3837
3883
|
const hashResult = await db.execute(
|
|
3838
|
-
|
|
3884
|
+
import_drizzle_orm7.sql`SELECT password_hash FROM users WHERE id = ${actingUser.id} LIMIT 1`
|
|
3839
3885
|
);
|
|
3840
3886
|
const hashRow = hashResult.rows[0];
|
|
3841
3887
|
if (!hashRow || !await (0, import_manguito_cms_core5.verifyPassword)(currentPassword, hashRow.password_hash)) {
|
|
@@ -3846,7 +3892,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3846
3892
|
}
|
|
3847
3893
|
const newHash = await (0, import_manguito_cms_core5.hashPassword)(newPassword);
|
|
3848
3894
|
await db.execute(
|
|
3849
|
-
|
|
3895
|
+
import_drizzle_orm7.sql`UPDATE users
|
|
3850
3896
|
SET password_hash = ${newHash},
|
|
3851
3897
|
must_change_password = false,
|
|
3852
3898
|
token_version = token_version + 1,
|
|
@@ -3857,7 +3903,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3857
3903
|
});
|
|
3858
3904
|
app.get("/admin/api/users", requirePermission("users:read"), async (c) => {
|
|
3859
3905
|
const result = await db.execute(
|
|
3860
|
-
|
|
3906
|
+
import_drizzle_orm7.sql`SELECT u.id, u.email, r.name AS role, u.must_change_password,
|
|
3861
3907
|
u.created_at, u.updated_at
|
|
3862
3908
|
FROM users u
|
|
3863
3909
|
JOIN roles r ON r.id = u.role_id
|
|
@@ -3868,7 +3914,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3868
3914
|
app.get("/admin/api/users/:id", requirePermission("users:read"), async (c) => {
|
|
3869
3915
|
const id = c.req.param("id");
|
|
3870
3916
|
const result = await db.execute(
|
|
3871
|
-
|
|
3917
|
+
import_drizzle_orm7.sql`SELECT u.id, u.email, r.name AS role, u.must_change_password,
|
|
3872
3918
|
u.created_at, u.updated_at
|
|
3873
3919
|
FROM users u
|
|
3874
3920
|
JOIN roles r ON r.id = u.role_id
|
|
@@ -3913,7 +3959,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3913
3959
|
);
|
|
3914
3960
|
}
|
|
3915
3961
|
const roleResult = await db.execute(
|
|
3916
|
-
|
|
3962
|
+
import_drizzle_orm7.sql`SELECT id FROM roles WHERE name = ${roleName} LIMIT 1`
|
|
3917
3963
|
);
|
|
3918
3964
|
const roleRow = roleResult.rows[0];
|
|
3919
3965
|
if (!roleRow) {
|
|
@@ -3928,7 +3974,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3928
3974
|
const tempPassword = generateTempPassword();
|
|
3929
3975
|
const passwordHash = await (0, import_manguito_cms_core5.hashPassword)(tempPassword);
|
|
3930
3976
|
const insertResult = await db.execute(
|
|
3931
|
-
|
|
3977
|
+
import_drizzle_orm7.sql`INSERT INTO users
|
|
3932
3978
|
(id, email, password_hash, role_id, must_change_password, token_version,
|
|
3933
3979
|
created_at, updated_at)
|
|
3934
3980
|
VALUES
|
|
@@ -3983,7 +4029,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3983
4029
|
);
|
|
3984
4030
|
}
|
|
3985
4031
|
const existingResult = await db.execute(
|
|
3986
|
-
|
|
4032
|
+
import_drizzle_orm7.sql`SELECT id, email, role_id FROM users WHERE id = ${id} LIMIT 1`
|
|
3987
4033
|
);
|
|
3988
4034
|
const existing = existingResult.rows[0];
|
|
3989
4035
|
if (!existing) {
|
|
@@ -3997,7 +4043,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3997
4043
|
let changeRole = false;
|
|
3998
4044
|
if (typeof body.role === "string" && body.role) {
|
|
3999
4045
|
const roleResult = await db.execute(
|
|
4000
|
-
|
|
4046
|
+
import_drizzle_orm7.sql`SELECT id FROM roles WHERE name = ${body.role} LIMIT 1`
|
|
4001
4047
|
);
|
|
4002
4048
|
const roleRow = roleResult.rows[0];
|
|
4003
4049
|
if (!roleRow) {
|
|
@@ -4017,7 +4063,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
4017
4063
|
}
|
|
4018
4064
|
if (changeRole) {
|
|
4019
4065
|
await db.execute(
|
|
4020
|
-
|
|
4066
|
+
import_drizzle_orm7.sql`UPDATE users
|
|
4021
4067
|
SET email = ${newEmail},
|
|
4022
4068
|
role_id = ${newRoleId},
|
|
4023
4069
|
token_version = token_version + 1,
|
|
@@ -4026,7 +4072,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
4026
4072
|
);
|
|
4027
4073
|
} else {
|
|
4028
4074
|
await db.execute(
|
|
4029
|
-
|
|
4075
|
+
import_drizzle_orm7.sql`UPDATE users
|
|
4030
4076
|
SET email = ${newEmail},
|
|
4031
4077
|
role_id = ${newRoleId},
|
|
4032
4078
|
updated_at = NOW()
|
|
@@ -4034,7 +4080,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
4034
4080
|
);
|
|
4035
4081
|
}
|
|
4036
4082
|
const updatedResult = await db.execute(
|
|
4037
|
-
|
|
4083
|
+
import_drizzle_orm7.sql`SELECT u.id, u.email, r.name AS role, u.must_change_password,
|
|
4038
4084
|
u.created_at, u.updated_at
|
|
4039
4085
|
FROM users u
|
|
4040
4086
|
JOIN roles r ON r.id = u.role_id
|
|
@@ -4071,7 +4117,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
4071
4117
|
);
|
|
4072
4118
|
}
|
|
4073
4119
|
const result = await db.execute(
|
|
4074
|
-
|
|
4120
|
+
import_drizzle_orm7.sql`DELETE FROM users WHERE id = ${id} RETURNING id`
|
|
4075
4121
|
);
|
|
4076
4122
|
if (result.rows.length === 0) {
|
|
4077
4123
|
return c.json(
|
|
@@ -4102,7 +4148,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
4102
4148
|
);
|
|
4103
4149
|
}
|
|
4104
4150
|
const existingResult = await db.execute(
|
|
4105
|
-
|
|
4151
|
+
import_drizzle_orm7.sql`SELECT id FROM users WHERE id = ${id} LIMIT 1`
|
|
4106
4152
|
);
|
|
4107
4153
|
const existing = existingResult.rows[0];
|
|
4108
4154
|
if (!existing) {
|
|
@@ -4114,7 +4160,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
4114
4160
|
const tempPassword = generateTempPassword();
|
|
4115
4161
|
const passwordHash = await (0, import_manguito_cms_core5.hashPassword)(tempPassword);
|
|
4116
4162
|
await db.execute(
|
|
4117
|
-
|
|
4163
|
+
import_drizzle_orm7.sql`UPDATE users
|
|
4118
4164
|
SET password_hash = ${passwordHash},
|
|
4119
4165
|
must_change_password = true,
|
|
4120
4166
|
token_version = token_version + 1,
|
|
@@ -4127,10 +4173,10 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
4127
4173
|
}
|
|
4128
4174
|
|
|
4129
4175
|
// src/routes/admin/config.ts
|
|
4130
|
-
var
|
|
4176
|
+
var import_drizzle_orm8 = require("drizzle-orm");
|
|
4131
4177
|
var API_VERSION = (() => {
|
|
4132
4178
|
try {
|
|
4133
|
-
return "0.8.
|
|
4179
|
+
return "0.8.2";
|
|
4134
4180
|
} catch {
|
|
4135
4181
|
return "0.0.0";
|
|
4136
4182
|
}
|
|
@@ -4149,7 +4195,7 @@ function registerConfigRoute(app, config, registry, db) {
|
|
|
4149
4195
|
permissions: r.permissions
|
|
4150
4196
|
}));
|
|
4151
4197
|
const userResult = await db.execute(
|
|
4152
|
-
|
|
4198
|
+
import_drizzle_orm8.sql`SELECT email, must_change_password FROM users WHERE id = ${actingUser.id} LIMIT 1`
|
|
4153
4199
|
);
|
|
4154
4200
|
const userRow = userResult.rows[0];
|
|
4155
4201
|
return c.json({
|
|
@@ -4175,7 +4221,7 @@ function registerConfigRoute(app, config, registry, db) {
|
|
|
4175
4221
|
}
|
|
4176
4222
|
|
|
4177
4223
|
// src/routes/admin/schema.ts
|
|
4178
|
-
var
|
|
4224
|
+
var import_drizzle_orm9 = require("drizzle-orm");
|
|
4179
4225
|
function liveFields(fields) {
|
|
4180
4226
|
return fields.filter((f) => f.removed !== true);
|
|
4181
4227
|
}
|
|
@@ -4215,7 +4261,7 @@ function registerSchemaRoute(app, registry, db) {
|
|
|
4215
4261
|
const data = await Promise.all(
|
|
4216
4262
|
Object.values(registry.content_types).map(async (ct) => {
|
|
4217
4263
|
const result = await db.execute(
|
|
4218
|
-
|
|
4264
|
+
import_drizzle_orm9.sql`SELECT COUNT(*)::int AS count FROM ${import_drizzle_orm9.sql.raw(`"${ct.db.table_name}"`)}`
|
|
4219
4265
|
);
|
|
4220
4266
|
const count = Number(result.rows[0].count);
|
|
4221
4267
|
return { name: ct.name, label: ct.label, only_one: ct.only_one, count };
|
|
@@ -4227,7 +4273,7 @@ function registerSchemaRoute(app, registry, db) {
|
|
|
4227
4273
|
const data = await Promise.all(
|
|
4228
4274
|
Object.values(registry.taxonomy_types).map(async (tt) => {
|
|
4229
4275
|
const result = await db.execute(
|
|
4230
|
-
|
|
4276
|
+
import_drizzle_orm9.sql`SELECT COUNT(*)::int AS count FROM ${import_drizzle_orm9.sql.raw(`"${tt.db.table_name}"`)}`
|
|
4231
4277
|
);
|
|
4232
4278
|
const count = Number(result.rows[0].count);
|
|
4233
4279
|
return { name: tt.name, label: tt.label, count };
|
|
@@ -4239,7 +4285,7 @@ function registerSchemaRoute(app, registry, db) {
|
|
|
4239
4285
|
|
|
4240
4286
|
// src/repositories/content.ts
|
|
4241
4287
|
var import_node_crypto3 = __toESM(require("crypto"));
|
|
4242
|
-
var
|
|
4288
|
+
var import_drizzle_orm10 = require("drizzle-orm");
|
|
4243
4289
|
init_relations();
|
|
4244
4290
|
var SORTABLE_FIELDS2 = /* @__PURE__ */ new Set(["title", "created_at", "updated_at"]);
|
|
4245
4291
|
function codeError(code, message) {
|
|
@@ -4247,7 +4293,7 @@ function codeError(code, message) {
|
|
|
4247
4293
|
err.code = code;
|
|
4248
4294
|
return err;
|
|
4249
4295
|
}
|
|
4250
|
-
function
|
|
4296
|
+
function quoteIdent5(name) {
|
|
4251
4297
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) {
|
|
4252
4298
|
throw new Error(`Unsafe SQL identifier: ${name}`);
|
|
4253
4299
|
}
|
|
@@ -4262,43 +4308,43 @@ function quoteField(name) {
|
|
|
4262
4308
|
function buildConditions(published_only, filters, search) {
|
|
4263
4309
|
const conds = [];
|
|
4264
4310
|
if (published_only) {
|
|
4265
|
-
conds.push(
|
|
4311
|
+
conds.push(import_drizzle_orm10.sql`published = true`);
|
|
4266
4312
|
}
|
|
4267
4313
|
if (search && search.term.trim() !== "" && search.columns.length > 0) {
|
|
4268
4314
|
const pattern = `%${search.term.trim()}%`;
|
|
4269
|
-
const orConds = search.columns.map((col) =>
|
|
4270
|
-
conds.push(
|
|
4315
|
+
const orConds = search.columns.map((col) => import_drizzle_orm10.sql`${import_drizzle_orm10.sql.raw(quoteField(col))} ILIKE ${pattern}`);
|
|
4316
|
+
conds.push(import_drizzle_orm10.sql`(${import_drizzle_orm10.sql.join(orConds, import_drizzle_orm10.sql` OR `)})`);
|
|
4271
4317
|
}
|
|
4272
4318
|
if (filters) {
|
|
4273
4319
|
for (const [field, value] of Object.entries(filters)) {
|
|
4274
|
-
const col =
|
|
4320
|
+
const col = import_drizzle_orm10.sql.raw(quoteField(field));
|
|
4275
4321
|
if (Array.isArray(value)) {
|
|
4276
4322
|
if (value.length === 0) continue;
|
|
4277
|
-
const inList =
|
|
4278
|
-
value.map((v) =>
|
|
4279
|
-
|
|
4323
|
+
const inList = import_drizzle_orm10.sql.join(
|
|
4324
|
+
value.map((v) => import_drizzle_orm10.sql`${v}`),
|
|
4325
|
+
import_drizzle_orm10.sql`, `
|
|
4280
4326
|
);
|
|
4281
|
-
conds.push(
|
|
4327
|
+
conds.push(import_drizzle_orm10.sql`${col} IN (${inList})`);
|
|
4282
4328
|
} else if (typeof value === "object" && value !== null) {
|
|
4283
4329
|
const op = value;
|
|
4284
|
-
if (op.gt !== void 0) conds.push(
|
|
4285
|
-
if (op.gte !== void 0) conds.push(
|
|
4286
|
-
if (op.lt !== void 0) conds.push(
|
|
4287
|
-
if (op.lte !== void 0) conds.push(
|
|
4330
|
+
if (op.gt !== void 0) conds.push(import_drizzle_orm10.sql`${col} > ${op.gt}`);
|
|
4331
|
+
if (op.gte !== void 0) conds.push(import_drizzle_orm10.sql`${col} >= ${op.gte}`);
|
|
4332
|
+
if (op.lt !== void 0) conds.push(import_drizzle_orm10.sql`${col} < ${op.lt}`);
|
|
4333
|
+
if (op.lte !== void 0) conds.push(import_drizzle_orm10.sql`${col} <= ${op.lte}`);
|
|
4288
4334
|
} else {
|
|
4289
|
-
conds.push(
|
|
4335
|
+
conds.push(import_drizzle_orm10.sql`${col} = ${value}`);
|
|
4290
4336
|
}
|
|
4291
4337
|
}
|
|
4292
4338
|
}
|
|
4293
4339
|
return conds;
|
|
4294
4340
|
}
|
|
4295
4341
|
function whereFragment(conds) {
|
|
4296
|
-
return conds.length > 0 ?
|
|
4342
|
+
return conds.length > 0 ? import_drizzle_orm10.sql` WHERE ${import_drizzle_orm10.sql.join(conds, import_drizzle_orm10.sql` AND `)}` : import_drizzle_orm10.sql``;
|
|
4297
4343
|
}
|
|
4298
4344
|
function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
4299
4345
|
const { relations = {}, publishedRelations = false, sortableColumns } = options;
|
|
4300
4346
|
function tableRaw() {
|
|
4301
|
-
return
|
|
4347
|
+
return import_drizzle_orm10.sql.raw(quoteIdent5(tableName));
|
|
4302
4348
|
}
|
|
4303
4349
|
async function resolveRows(rows, include, cache) {
|
|
4304
4350
|
if (rows.length === 0) return rows;
|
|
@@ -4341,14 +4387,14 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
4341
4387
|
const offset = (page - 1) * per_page;
|
|
4342
4388
|
const conds = buildConditions(published_only, filters, search);
|
|
4343
4389
|
const where = whereFragment(conds);
|
|
4344
|
-
const sortCol =
|
|
4345
|
-
const sortDir =
|
|
4390
|
+
const sortCol = import_drizzle_orm10.sql.raw(quoteIdent5(sort_by));
|
|
4391
|
+
const sortDir = import_drizzle_orm10.sql.raw(sort_order === "desc" ? "DESC" : "ASC");
|
|
4346
4392
|
const countResult = await db.execute(
|
|
4347
|
-
|
|
4393
|
+
import_drizzle_orm10.sql`SELECT COUNT(*) AS total FROM ${tableRaw()}${where}`
|
|
4348
4394
|
);
|
|
4349
4395
|
const total = Number(countResult.rows[0]?.["total"] ?? 0);
|
|
4350
4396
|
const dataResult = await db.execute(
|
|
4351
|
-
|
|
4397
|
+
import_drizzle_orm10.sql`SELECT * FROM ${tableRaw()}${where} ORDER BY ${sortCol} ${sortDir} LIMIT ${per_page} OFFSET ${offset}`
|
|
4352
4398
|
);
|
|
4353
4399
|
const rows = dataResult.rows;
|
|
4354
4400
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -4369,7 +4415,7 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
4369
4415
|
},
|
|
4370
4416
|
async findOne(id, include = []) {
|
|
4371
4417
|
const result = await db.execute(
|
|
4372
|
-
|
|
4418
|
+
import_drizzle_orm10.sql`SELECT * FROM ${tableRaw()} WHERE id = ${id} LIMIT 1`
|
|
4373
4419
|
);
|
|
4374
4420
|
if (result.rows.length === 0) return null;
|
|
4375
4421
|
const rows = result.rows;
|
|
@@ -4379,7 +4425,7 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
4379
4425
|
},
|
|
4380
4426
|
async findBySlug(slug, include = []) {
|
|
4381
4427
|
const result = await db.execute(
|
|
4382
|
-
|
|
4428
|
+
import_drizzle_orm10.sql`SELECT * FROM ${tableRaw()} WHERE slug = ${slug} LIMIT 1`
|
|
4383
4429
|
);
|
|
4384
4430
|
if (result.rows.length === 0) return null;
|
|
4385
4431
|
const rows = result.rows;
|
|
@@ -4395,10 +4441,10 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
4395
4441
|
updated_at: /* @__PURE__ */ new Date()
|
|
4396
4442
|
};
|
|
4397
4443
|
const entries = Object.entries(record).filter(([, v]) => v !== void 0);
|
|
4398
|
-
const colsSql =
|
|
4399
|
-
const valsSql =
|
|
4444
|
+
const colsSql = import_drizzle_orm10.sql.join(entries.map(([k]) => import_drizzle_orm10.sql.raw(quoteIdent5(k))), import_drizzle_orm10.sql`, `);
|
|
4445
|
+
const valsSql = import_drizzle_orm10.sql.join(entries.map(([, v]) => import_drizzle_orm10.sql`${v}`), import_drizzle_orm10.sql`, `);
|
|
4400
4446
|
const result = await db.execute(
|
|
4401
|
-
|
|
4447
|
+
import_drizzle_orm10.sql`INSERT INTO ${tableRaw()} (${colsSql}) VALUES (${valsSql}) RETURNING *`
|
|
4402
4448
|
);
|
|
4403
4449
|
return result.rows[0];
|
|
4404
4450
|
},
|
|
@@ -4411,25 +4457,25 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
4411
4457
|
if (entries.length === 0) {
|
|
4412
4458
|
return this.findOne(id);
|
|
4413
4459
|
}
|
|
4414
|
-
const setClauses =
|
|
4415
|
-
entries.map(([k, v]) =>
|
|
4416
|
-
|
|
4460
|
+
const setClauses = import_drizzle_orm10.sql.join(
|
|
4461
|
+
entries.map(([k, v]) => import_drizzle_orm10.sql`${import_drizzle_orm10.sql.raw(quoteIdent5(k))} = ${v}`),
|
|
4462
|
+
import_drizzle_orm10.sql`, `
|
|
4417
4463
|
);
|
|
4418
4464
|
const result = await db.execute(
|
|
4419
|
-
|
|
4465
|
+
import_drizzle_orm10.sql`UPDATE ${tableRaw()} SET ${setClauses} WHERE id = ${id} RETURNING *`
|
|
4420
4466
|
);
|
|
4421
4467
|
if (result.rows.length === 0) return null;
|
|
4422
4468
|
return result.rows[0];
|
|
4423
4469
|
},
|
|
4424
4470
|
async delete(id) {
|
|
4425
|
-
await db.execute(
|
|
4471
|
+
await db.execute(import_drizzle_orm10.sql`DELETE FROM ${tableRaw()} WHERE id = ${id}`);
|
|
4426
4472
|
},
|
|
4427
4473
|
async findAll(opts = {}) {
|
|
4428
4474
|
const { include = [], published_only } = opts;
|
|
4429
4475
|
const conds = buildConditions(published_only, void 0, void 0);
|
|
4430
4476
|
const where = whereFragment(conds);
|
|
4431
4477
|
const result = await db.execute(
|
|
4432
|
-
|
|
4478
|
+
import_drizzle_orm10.sql`SELECT * FROM ${tableRaw()}${where} ORDER BY created_at ASC`
|
|
4433
4479
|
);
|
|
4434
4480
|
const rows = result.rows;
|
|
4435
4481
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -4444,23 +4490,23 @@ init_relations();
|
|
|
4444
4490
|
|
|
4445
4491
|
// src/repositories/media.ts
|
|
4446
4492
|
var import_node_crypto4 = __toESM(require("crypto"));
|
|
4447
|
-
var
|
|
4493
|
+
var import_drizzle_orm11 = require("drizzle-orm");
|
|
4448
4494
|
function createMediaRepository(db) {
|
|
4449
|
-
const TABLE =
|
|
4495
|
+
const TABLE = import_drizzle_orm11.sql.raw('"media"');
|
|
4450
4496
|
return {
|
|
4451
4497
|
async findMany(opts = {}) {
|
|
4452
4498
|
const { page = 1, per_page = 10, type, orphaned } = opts;
|
|
4453
4499
|
const offset = (page - 1) * per_page;
|
|
4454
4500
|
const conds = [];
|
|
4455
|
-
if (type) conds.push(
|
|
4456
|
-
if (orphaned) conds.push(
|
|
4457
|
-
const where = conds.length > 0 ?
|
|
4458
|
-
const countResult = await db.execute(
|
|
4501
|
+
if (type) conds.push(import_drizzle_orm11.sql`type = ${type}`);
|
|
4502
|
+
if (orphaned) conds.push(import_drizzle_orm11.sql`reference_count = 0`);
|
|
4503
|
+
const where = conds.length > 0 ? import_drizzle_orm11.sql` WHERE ${import_drizzle_orm11.sql.join(conds, import_drizzle_orm11.sql` AND `)}` : import_drizzle_orm11.sql``;
|
|
4504
|
+
const countResult = await db.execute(import_drizzle_orm11.sql`SELECT COUNT(*) AS total FROM ${TABLE}${where}`);
|
|
4459
4505
|
const total = Number(
|
|
4460
4506
|
countResult.rows[0]?.["total"] ?? 0
|
|
4461
4507
|
);
|
|
4462
4508
|
const dataResult = await db.execute(
|
|
4463
|
-
|
|
4509
|
+
import_drizzle_orm11.sql`SELECT * FROM ${TABLE}${where} ORDER BY created_at DESC LIMIT ${per_page} OFFSET ${offset}`
|
|
4464
4510
|
);
|
|
4465
4511
|
const total_pages = Math.ceil(total / per_page);
|
|
4466
4512
|
return {
|
|
@@ -4478,7 +4524,7 @@ function createMediaRepository(db) {
|
|
|
4478
4524
|
},
|
|
4479
4525
|
async findOne(id) {
|
|
4480
4526
|
const result = await db.execute(
|
|
4481
|
-
|
|
4527
|
+
import_drizzle_orm11.sql`SELECT * FROM ${TABLE} WHERE id = ${id} LIMIT 1`
|
|
4482
4528
|
);
|
|
4483
4529
|
if (result.rows.length === 0) return null;
|
|
4484
4530
|
return result.rows[0];
|
|
@@ -4492,10 +4538,10 @@ function createMediaRepository(db) {
|
|
|
4492
4538
|
updated_at: /* @__PURE__ */ new Date()
|
|
4493
4539
|
};
|
|
4494
4540
|
const entries = Object.entries(record).filter(([, v]) => v !== void 0);
|
|
4495
|
-
const colsSql =
|
|
4496
|
-
const valsSql =
|
|
4541
|
+
const colsSql = import_drizzle_orm11.sql.join(entries.map(([k]) => import_drizzle_orm11.sql.raw(`"${k}"`)), import_drizzle_orm11.sql`, `);
|
|
4542
|
+
const valsSql = import_drizzle_orm11.sql.join(entries.map(([, v]) => import_drizzle_orm11.sql`${v}`), import_drizzle_orm11.sql`, `);
|
|
4497
4543
|
const result = await db.execute(
|
|
4498
|
-
|
|
4544
|
+
import_drizzle_orm11.sql`INSERT INTO ${TABLE} (${colsSql}) VALUES (${valsSql}) RETURNING *`
|
|
4499
4545
|
);
|
|
4500
4546
|
return result.rows[0];
|
|
4501
4547
|
},
|
|
@@ -4505,31 +4551,31 @@ function createMediaRepository(db) {
|
|
|
4505
4551
|
([k, v]) => k !== "id" && v !== void 0
|
|
4506
4552
|
);
|
|
4507
4553
|
if (entries.length === 0) return this.findOne(id);
|
|
4508
|
-
const setClauses =
|
|
4509
|
-
entries.map(([k, v]) =>
|
|
4510
|
-
|
|
4554
|
+
const setClauses = import_drizzle_orm11.sql.join(
|
|
4555
|
+
entries.map(([k, v]) => import_drizzle_orm11.sql`${import_drizzle_orm11.sql.raw(`"${k}"`)} = ${v}`),
|
|
4556
|
+
import_drizzle_orm11.sql`, `
|
|
4511
4557
|
);
|
|
4512
4558
|
const result = await db.execute(
|
|
4513
|
-
|
|
4559
|
+
import_drizzle_orm11.sql`UPDATE ${TABLE} SET ${setClauses} WHERE id = ${id} RETURNING *`
|
|
4514
4560
|
);
|
|
4515
4561
|
if (result.rows.length === 0) return null;
|
|
4516
4562
|
return result.rows[0];
|
|
4517
4563
|
},
|
|
4518
4564
|
async delete(id) {
|
|
4519
|
-
await db.execute(
|
|
4565
|
+
await db.execute(import_drizzle_orm11.sql`DELETE FROM ${TABLE} WHERE id = ${id}`);
|
|
4520
4566
|
},
|
|
4521
4567
|
async incrementReferenceCount(ids) {
|
|
4522
4568
|
if (ids.length === 0) return;
|
|
4523
|
-
const inList =
|
|
4569
|
+
const inList = import_drizzle_orm11.sql.join(ids.map((id) => import_drizzle_orm11.sql`${id}`), import_drizzle_orm11.sql`, `);
|
|
4524
4570
|
await db.execute(
|
|
4525
|
-
|
|
4571
|
+
import_drizzle_orm11.sql`UPDATE ${TABLE} SET reference_count = reference_count + 1, updated_at = NOW() WHERE id IN (${inList})`
|
|
4526
4572
|
);
|
|
4527
4573
|
},
|
|
4528
4574
|
async decrementReferenceCount(ids) {
|
|
4529
4575
|
if (ids.length === 0) return;
|
|
4530
|
-
const inList =
|
|
4576
|
+
const inList = import_drizzle_orm11.sql.join(ids.map((id) => import_drizzle_orm11.sql`${id}`), import_drizzle_orm11.sql`, `);
|
|
4531
4577
|
await db.execute(
|
|
4532
|
-
|
|
4578
|
+
import_drizzle_orm11.sql`UPDATE ${TABLE} SET reference_count = GREATEST(reference_count - 1, 0), updated_at = NOW() WHERE id IN (${inList})`
|
|
4533
4579
|
);
|
|
4534
4580
|
}
|
|
4535
4581
|
};
|
|
@@ -4666,7 +4712,7 @@ function createCmsApp(options) {
|
|
|
4666
4712
|
const requirePermission = createPermissionMiddleware(rolesRegistry);
|
|
4667
4713
|
const getUserRole = async (userId) => {
|
|
4668
4714
|
const result = await db.execute(
|
|
4669
|
-
|
|
4715
|
+
import_drizzle_orm12.sql`SELECT r.name AS role
|
|
4670
4716
|
FROM users u
|
|
4671
4717
|
JOIN roles r ON r.id = u.role_id
|
|
4672
4718
|
WHERE u.id = ${userId}
|