@bobbykim/manguito-cms-api 0.4.0 → 0.4.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/{chunk-4F2CRXPG.mjs → chunk-4Y4G564P.mjs} +14 -2
- package/dist/{chunk-4F2CRXPG.mjs.map → chunk-4Y4G564P.mjs.map} +1 -1
- package/dist/{chunk-B5LWIR4U.mjs → chunk-HEYT5CH5.mjs} +9 -7
- package/dist/chunk-HEYT5CH5.mjs.map +1 -0
- package/dist/graphql/index.js +7 -5
- package/dist/graphql/index.js.map +1 -1
- package/dist/graphql/index.mjs +2 -2
- package/dist/handler-45O5OX2C.mjs +8 -0
- package/dist/index.js +146 -53
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +128 -49
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-B5LWIR4U.mjs.map +0 -1
- package/dist/handler-TPEULQ5W.mjs +0 -8
- /package/dist/{handler-TPEULQ5W.mjs.map → handler-45O5OX2C.mjs.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -344,10 +344,16 @@ function isColumnBacked(field) {
|
|
|
344
344
|
function createFieldKeyMap(fields) {
|
|
345
345
|
const labelToColumn = /* @__PURE__ */ new Map();
|
|
346
346
|
const columnToLabel = /* @__PURE__ */ new Map();
|
|
347
|
+
const droppedKeys = /* @__PURE__ */ new Set();
|
|
347
348
|
for (const f of fields) {
|
|
348
349
|
if (!isColumnBacked(f)) continue;
|
|
349
|
-
labelToColumn.set(f.name, f.db_column.column_name);
|
|
350
350
|
columnToLabel.set(f.db_column.column_name, f.name);
|
|
351
|
+
if (f.removed === true) {
|
|
352
|
+
droppedKeys.add(f.name);
|
|
353
|
+
droppedKeys.add(f.db_column.column_name);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
labelToColumn.set(f.name, f.db_column.column_name);
|
|
351
357
|
}
|
|
352
358
|
for (const f of fields) {
|
|
353
359
|
const columnOwner = columnToLabel.get(f.name);
|
|
@@ -357,10 +363,16 @@ function createFieldKeyMap(fields) {
|
|
|
357
363
|
);
|
|
358
364
|
}
|
|
359
365
|
}
|
|
366
|
+
for (const f of fields) {
|
|
367
|
+
if (isColumnBacked(f) && f.removed === true) {
|
|
368
|
+
columnToLabel.delete(f.db_column.column_name);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
360
371
|
const diverges = [...labelToColumn].some(([label, column]) => label !== column);
|
|
361
372
|
function remap(input, lookup) {
|
|
362
373
|
const out = {};
|
|
363
374
|
for (const key of Object.keys(input)) {
|
|
375
|
+
if (droppedKeys.has(key)) continue;
|
|
364
376
|
out[lookup.get(key) ?? key] = input[key];
|
|
365
377
|
}
|
|
366
378
|
return out;
|
|
@@ -500,7 +512,7 @@ var init_naming = __esm({
|
|
|
500
512
|
function buildSortFieldEnum(typeName) {
|
|
501
513
|
return new import_graphql3.GraphQLEnumType({
|
|
502
514
|
name: `${typeName}SortField`,
|
|
503
|
-
values: Object.fromEntries(SORTABLE.map((s) => [s.gql, { value: s.
|
|
515
|
+
values: Object.fromEntries(SORTABLE.map((s) => [s.gql, { value: s.value }]))
|
|
504
516
|
});
|
|
505
517
|
}
|
|
506
518
|
function comparableFilterInput(name, scalar) {
|
|
@@ -586,9 +598,9 @@ var init_filters = __esm({
|
|
|
586
598
|
values: { ASC: { value: "asc" }, DESC: { value: "desc" } }
|
|
587
599
|
});
|
|
588
600
|
SORTABLE = [
|
|
589
|
-
{ gql: "title",
|
|
590
|
-
{ gql: "createdAt",
|
|
591
|
-
{ gql: "updatedAt",
|
|
601
|
+
{ gql: "title", value: "title" },
|
|
602
|
+
{ gql: "createdAt", value: "created_at" },
|
|
603
|
+
{ gql: "updatedAt", value: "updated_at" }
|
|
592
604
|
];
|
|
593
605
|
StringFilter = equalityFilterInput("StringFilter", import_graphql3.GraphQLString);
|
|
594
606
|
BooleanFilter = new import_graphql3.GraphQLInputObjectType({
|
|
@@ -647,11 +659,13 @@ function collectionResolver(typeName, nameMap, fieldKeys) {
|
|
|
647
659
|
throw codeError2("INVALID_PAGINATION", "perPage must be between 1 and 100");
|
|
648
660
|
}
|
|
649
661
|
const repo = ctx.repos[typeName];
|
|
662
|
+
const sortBy = args.sortBy ?? "created_at";
|
|
663
|
+
const sortColumn = fieldKeys?.columnFor(sortBy) ?? sortBy;
|
|
650
664
|
const result = await repo.findMany({
|
|
651
665
|
published_only: true,
|
|
652
666
|
page,
|
|
653
667
|
per_page: perPage,
|
|
654
|
-
sort_by:
|
|
668
|
+
sort_by: sortColumn,
|
|
655
669
|
sort_order: args.sortOrder ?? "asc",
|
|
656
670
|
filters: translateFilters(args.filter, nameMap, fieldKeys?.columnFor)
|
|
657
671
|
});
|
|
@@ -1643,6 +1657,54 @@ function createProgrammaticResolver(resolvers, options) {
|
|
|
1643
1657
|
return { resolveItem, resolveList, hasSchema };
|
|
1644
1658
|
}
|
|
1645
1659
|
|
|
1660
|
+
// src/projector.ts
|
|
1661
|
+
function isPlainRow(v) {
|
|
1662
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof Date);
|
|
1663
|
+
}
|
|
1664
|
+
var MEDIA_FIELD_TYPES = /* @__PURE__ */ new Set(["image", "video", "file"]);
|
|
1665
|
+
function nestedTargets(fields) {
|
|
1666
|
+
const out = [];
|
|
1667
|
+
for (const f of fields) {
|
|
1668
|
+
if (MEDIA_FIELD_TYPES.has(f.field_type)) continue;
|
|
1669
|
+
if (f.field_type !== "paragraph" && f.field_type !== "reference") continue;
|
|
1670
|
+
const ref = f.ui_component.ref;
|
|
1671
|
+
if (!ref) continue;
|
|
1672
|
+
out.push({ label: f.name, target: ref });
|
|
1673
|
+
}
|
|
1674
|
+
return out;
|
|
1675
|
+
}
|
|
1676
|
+
function buildProjectors(registry, fieldKeyMaps) {
|
|
1677
|
+
const projectors = {};
|
|
1678
|
+
const sources = [
|
|
1679
|
+
registry.content_types,
|
|
1680
|
+
registry.taxonomy_types,
|
|
1681
|
+
registry.paragraph_types
|
|
1682
|
+
];
|
|
1683
|
+
for (const source of sources) {
|
|
1684
|
+
for (const [typeName, type] of Object.entries(source)) {
|
|
1685
|
+
const map = fieldKeyMaps[typeName];
|
|
1686
|
+
if (!map) continue;
|
|
1687
|
+
projectors[typeName] = { map, nested: nestedTargets(type.fields) };
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
return projectors;
|
|
1691
|
+
}
|
|
1692
|
+
function projectRow(row, typeName, projectors) {
|
|
1693
|
+
const p = projectors[typeName];
|
|
1694
|
+
if (!p) return row;
|
|
1695
|
+
const out = p.map.toLabels(row);
|
|
1696
|
+
for (const { label, target } of p.nested) {
|
|
1697
|
+
const v = out[label];
|
|
1698
|
+
if (v === null || v === void 0) continue;
|
|
1699
|
+
if (Array.isArray(v)) {
|
|
1700
|
+
out[label] = v.map((item) => isPlainRow(item) ? projectRow(item, target, projectors) : item);
|
|
1701
|
+
} else if (isPlainRow(v)) {
|
|
1702
|
+
out[label] = projectRow(v, target, projectors);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
return out;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1646
1708
|
// src/routes/query-params.ts
|
|
1647
1709
|
var SORTABLE_FIELDS = /* @__PURE__ */ new Set(["title", "created_at", "updated_at"]);
|
|
1648
1710
|
var RELATION_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -1697,7 +1759,7 @@ function parseFilters(url, validFields, columnFor) {
|
|
|
1697
1759
|
function isPublished(item) {
|
|
1698
1760
|
return item["published"] === true;
|
|
1699
1761
|
}
|
|
1700
|
-
function registerPublicContentRoutes(app, registry, repos,
|
|
1762
|
+
function registerPublicContentRoutes(app, registry, repos, projectors, paths, listRateLimit, resolver) {
|
|
1701
1763
|
function registerListRoute(path, handler) {
|
|
1702
1764
|
if (listRateLimit) {
|
|
1703
1765
|
app.get(path, listRateLimit, handler);
|
|
@@ -1740,13 +1802,14 @@ function registerPublicContentRoutes(app, registry, repos, fieldKeyMaps, paths,
|
|
|
1740
1802
|
404
|
|
1741
1803
|
);
|
|
1742
1804
|
}
|
|
1743
|
-
let data =
|
|
1805
|
+
let data = projectRow(result.data[0], typeName, projectors);
|
|
1744
1806
|
if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
|
|
1745
1807
|
return c.json({ ok: true, data });
|
|
1746
1808
|
});
|
|
1747
1809
|
} else {
|
|
1748
1810
|
registerListRoute(paths.collection(basePath), async (c) => {
|
|
1749
|
-
const
|
|
1811
|
+
const projector = projectors[typeName];
|
|
1812
|
+
const fieldKeys = projector.map;
|
|
1750
1813
|
const pagination = parsePagination(c.req.query("page"), c.req.query("per_page"));
|
|
1751
1814
|
if (!pagination.ok) {
|
|
1752
1815
|
return c.json(
|
|
@@ -1814,17 +1877,18 @@ function registerPublicContentRoutes(app, registry, repos, fieldKeyMaps, paths,
|
|
|
1814
1877
|
);
|
|
1815
1878
|
}
|
|
1816
1879
|
}
|
|
1880
|
+
const sortColumn = fieldKeys.columnFor(sortBy) ?? sortBy;
|
|
1817
1881
|
const result = await repo.findMany({
|
|
1818
1882
|
published_only: true,
|
|
1819
1883
|
page: pagination.page,
|
|
1820
1884
|
per_page: pagination.per_page,
|
|
1821
|
-
sort_by:
|
|
1885
|
+
sort_by: sortColumn,
|
|
1822
1886
|
sort_order: sortOrder,
|
|
1823
1887
|
filters: filtersResult.filters,
|
|
1824
1888
|
include
|
|
1825
1889
|
});
|
|
1826
1890
|
const labeled = result.data.map(
|
|
1827
|
-
(row) =>
|
|
1891
|
+
(row) => projectRow(row, typeName, projectors)
|
|
1828
1892
|
);
|
|
1829
1893
|
const data = resolver?.hasSchema(typeName) ? await resolver.resolveList(typeName, labeled) : labeled;
|
|
1830
1894
|
return c.json({ ...result, data });
|
|
@@ -1856,7 +1920,7 @@ function registerPublicContentRoutes(app, registry, repos, fieldKeyMaps, paths,
|
|
|
1856
1920
|
404
|
|
1857
1921
|
);
|
|
1858
1922
|
}
|
|
1859
|
-
let data =
|
|
1923
|
+
let data = projectRow(item, typeName, projectors);
|
|
1860
1924
|
if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
|
|
1861
1925
|
return c.json({ ok: true, data });
|
|
1862
1926
|
});
|
|
@@ -1884,9 +1948,8 @@ function registerPublicContentRoutes(app, registry, repos, fieldKeyMaps, paths,
|
|
|
1884
1948
|
page: pagination.page,
|
|
1885
1949
|
per_page: pagination.per_page
|
|
1886
1950
|
});
|
|
1887
|
-
const fieldKeys = fieldKeyMaps[typeName];
|
|
1888
1951
|
const labeled = result.data.map(
|
|
1889
|
-
(row) =>
|
|
1952
|
+
(row) => projectRow(row, typeName, projectors)
|
|
1890
1953
|
);
|
|
1891
1954
|
const data = resolver?.hasSchema(typeName) ? await resolver.resolveList(typeName, labeled) : labeled;
|
|
1892
1955
|
return c.json({ ...result, data });
|
|
@@ -1903,7 +1966,7 @@ function registerPublicContentRoutes(app, registry, repos, fieldKeyMaps, paths,
|
|
|
1903
1966
|
404
|
|
1904
1967
|
);
|
|
1905
1968
|
}
|
|
1906
|
-
let data =
|
|
1969
|
+
let data = projectRow(item, typeName, projectors);
|
|
1907
1970
|
if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
|
|
1908
1971
|
return c.json({ ok: true, data });
|
|
1909
1972
|
});
|
|
@@ -2119,7 +2182,7 @@ function parseListQuery(url, schemaFieldNames, relationFieldNames, columnFor) {
|
|
|
2119
2182
|
search
|
|
2120
2183
|
};
|
|
2121
2184
|
}
|
|
2122
|
-
function registerAdminContentRoutes(app, registry, repos,
|
|
2185
|
+
function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo, requirePermission, db) {
|
|
2123
2186
|
for (const [typeName, contentType] of Object.entries(registry.content_types)) {
|
|
2124
2187
|
const basePath = `content/${typeName}`;
|
|
2125
2188
|
const repo = repos[typeName];
|
|
@@ -2131,7 +2194,8 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2131
2194
|
const relationFieldNames = new Set(
|
|
2132
2195
|
contentType.fields.filter((f) => RELATION_FIELD_TYPES.has(f.field_type)).map((f) => f.name)
|
|
2133
2196
|
);
|
|
2134
|
-
const
|
|
2197
|
+
const projector = projectors[typeName];
|
|
2198
|
+
const fieldKeys = projector.map;
|
|
2135
2199
|
const requiredFields = contentType.fields.filter((f) => f.required);
|
|
2136
2200
|
const mediaFields = contentType.fields.filter(
|
|
2137
2201
|
(f) => f.field_type === "image" || f.field_type === "video" || f.field_type === "file"
|
|
@@ -2152,10 +2216,11 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2152
2216
|
const publishedParam = c.req.query("published");
|
|
2153
2217
|
const extraFilters = {};
|
|
2154
2218
|
if (publishedParam === "false") extraFilters["published"] = false;
|
|
2219
|
+
const sortColumn = fieldKeys.columnFor(parsed.sortBy) ?? parsed.sortBy;
|
|
2155
2220
|
const findOpts = {
|
|
2156
2221
|
page: parsed.pagination.page,
|
|
2157
2222
|
per_page: parsed.pagination.per_page,
|
|
2158
|
-
sort_by:
|
|
2223
|
+
sort_by: sortColumn,
|
|
2159
2224
|
sort_order: parsed.sortOrder,
|
|
2160
2225
|
filters: { ...parsed.filters, ...extraFilters },
|
|
2161
2226
|
include: parsed.include
|
|
@@ -2165,7 +2230,9 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2165
2230
|
findOpts.search = { term: parsed.search, columns: searchableColumns };
|
|
2166
2231
|
}
|
|
2167
2232
|
const result = await repo.findMany(findOpts);
|
|
2168
|
-
const data = result.data.map(
|
|
2233
|
+
const data = result.data.map(
|
|
2234
|
+
(row) => projectRow(row, typeName, projectors)
|
|
2235
|
+
);
|
|
2169
2236
|
return c.json({ ...result, data });
|
|
2170
2237
|
}
|
|
2171
2238
|
);
|
|
@@ -2201,7 +2268,7 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2201
2268
|
}
|
|
2202
2269
|
}
|
|
2203
2270
|
}
|
|
2204
|
-
const data =
|
|
2271
|
+
const data = projectRow(item, typeName, projectors);
|
|
2205
2272
|
return c.json({ ok: true, data });
|
|
2206
2273
|
}
|
|
2207
2274
|
);
|
|
@@ -2342,7 +2409,7 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2342
2409
|
}
|
|
2343
2410
|
}
|
|
2344
2411
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
2345
|
-
const data =
|
|
2412
|
+
const data = projectRow(item, typeName, projectors);
|
|
2346
2413
|
return c.json({ ok: true, data }, 201);
|
|
2347
2414
|
};
|
|
2348
2415
|
app.patch(
|
|
@@ -2463,7 +2530,7 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2463
2530
|
}
|
|
2464
2531
|
}
|
|
2465
2532
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
2466
|
-
const data =
|
|
2533
|
+
const data = projectRow(updated, typeName, projectors);
|
|
2467
2534
|
return c.json({ ok: true, data });
|
|
2468
2535
|
};
|
|
2469
2536
|
if (contentType.only_one) {
|
|
@@ -2524,7 +2591,8 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2524
2591
|
const relationFieldNames = new Set(
|
|
2525
2592
|
taxonomyType.fields.filter((f) => RELATION_FIELD_TYPES.has(f.field_type)).map((f) => f.name)
|
|
2526
2593
|
);
|
|
2527
|
-
const
|
|
2594
|
+
const projector = projectors[typeName];
|
|
2595
|
+
const fieldKeys = projector.map;
|
|
2528
2596
|
const requiredFields = taxonomyType.fields.filter((f) => f.required);
|
|
2529
2597
|
const mediaFields = taxonomyType.fields.filter(
|
|
2530
2598
|
(f) => f.field_type === "image" || f.field_type === "video" || f.field_type === "file"
|
|
@@ -2545,10 +2613,11 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2545
2613
|
const publishedParam = c.req.query("published");
|
|
2546
2614
|
const extraFilters = {};
|
|
2547
2615
|
if (publishedParam === "false") extraFilters["published"] = false;
|
|
2616
|
+
const sortColumn = fieldKeys.columnFor(parsed.sortBy) ?? parsed.sortBy;
|
|
2548
2617
|
const findOpts = {
|
|
2549
2618
|
page: parsed.pagination.page,
|
|
2550
2619
|
per_page: parsed.pagination.per_page,
|
|
2551
|
-
sort_by:
|
|
2620
|
+
sort_by: sortColumn,
|
|
2552
2621
|
sort_order: parsed.sortOrder,
|
|
2553
2622
|
filters: { ...parsed.filters, ...extraFilters },
|
|
2554
2623
|
include: parsed.include
|
|
@@ -2558,7 +2627,9 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2558
2627
|
findOpts.search = { term: parsed.search, columns: searchableColumns };
|
|
2559
2628
|
}
|
|
2560
2629
|
const result = await repo.findMany(findOpts);
|
|
2561
|
-
const data = result.data.map(
|
|
2630
|
+
const data = result.data.map(
|
|
2631
|
+
(row) => projectRow(row, typeName, projectors)
|
|
2632
|
+
);
|
|
2562
2633
|
return c.json({ ...result, data });
|
|
2563
2634
|
}
|
|
2564
2635
|
);
|
|
@@ -2574,7 +2645,7 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2574
2645
|
404
|
|
2575
2646
|
);
|
|
2576
2647
|
}
|
|
2577
|
-
const data =
|
|
2648
|
+
const data = projectRow(item, typeName, projectors);
|
|
2578
2649
|
return c.json({ ok: true, data });
|
|
2579
2650
|
}
|
|
2580
2651
|
);
|
|
@@ -2627,7 +2698,7 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2627
2698
|
}
|
|
2628
2699
|
}
|
|
2629
2700
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
2630
|
-
const data =
|
|
2701
|
+
const data = projectRow(item, typeName, projectors);
|
|
2631
2702
|
return c.json({ ok: true, data }, 201);
|
|
2632
2703
|
}
|
|
2633
2704
|
);
|
|
@@ -2695,7 +2766,7 @@ function registerAdminContentRoutes(app, registry, repos, fieldKeyMaps, mediaRep
|
|
|
2695
2766
|
}
|
|
2696
2767
|
}
|
|
2697
2768
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
2698
|
-
const data =
|
|
2769
|
+
const data = projectRow(updated, typeName, projectors);
|
|
2699
2770
|
return c.json({ ok: true, data });
|
|
2700
2771
|
}
|
|
2701
2772
|
);
|
|
@@ -3688,7 +3759,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3688
3759
|
var import_drizzle_orm6 = require("drizzle-orm");
|
|
3689
3760
|
var API_VERSION = (() => {
|
|
3690
3761
|
try {
|
|
3691
|
-
return "0.4.
|
|
3762
|
+
return "0.4.2";
|
|
3692
3763
|
} catch {
|
|
3693
3764
|
return "0.0.0";
|
|
3694
3765
|
}
|
|
@@ -3734,6 +3805,9 @@ function registerConfigRoute(app, config, registry, db) {
|
|
|
3734
3805
|
|
|
3735
3806
|
// src/routes/admin/schema.ts
|
|
3736
3807
|
var import_drizzle_orm7 = require("drizzle-orm");
|
|
3808
|
+
function liveFields(fields) {
|
|
3809
|
+
return fields.filter((f) => f.removed !== true);
|
|
3810
|
+
}
|
|
3737
3811
|
function registerSchemaRoute(app, registry, db) {
|
|
3738
3812
|
app.get("/admin/api/schema", (c) => {
|
|
3739
3813
|
const content_types = Object.values(registry.content_types).map((ct) => ({
|
|
@@ -3742,19 +3816,19 @@ function registerSchemaRoute(app, registry, db) {
|
|
|
3742
3816
|
only_one: ct.only_one,
|
|
3743
3817
|
ui: ct.ui,
|
|
3744
3818
|
system_fields: ct.system_fields,
|
|
3745
|
-
fields: ct.fields
|
|
3819
|
+
fields: liveFields(ct.fields)
|
|
3746
3820
|
}));
|
|
3747
3821
|
const taxonomy_types = Object.values(registry.taxonomy_types).map((tt) => ({
|
|
3748
3822
|
name: tt.name,
|
|
3749
3823
|
label: tt.label,
|
|
3750
3824
|
system_fields: tt.system_fields,
|
|
3751
|
-
fields: tt.fields
|
|
3825
|
+
fields: liveFields(tt.fields)
|
|
3752
3826
|
}));
|
|
3753
3827
|
const paragraph_types = Object.values(registry.paragraph_types).map((pt) => ({
|
|
3754
3828
|
name: pt.name,
|
|
3755
3829
|
label: pt.label,
|
|
3756
3830
|
system_fields: pt.system_fields,
|
|
3757
|
-
fields: pt.fields
|
|
3831
|
+
fields: liveFields(pt.fields)
|
|
3758
3832
|
}));
|
|
3759
3833
|
const enum_types = Object.values(registry.enum_types).map((et) => ({
|
|
3760
3834
|
name: et.name,
|
|
@@ -3851,7 +3925,7 @@ function whereFragment(conds) {
|
|
|
3851
3925
|
return conds.length > 0 ? import_drizzle_orm8.sql` WHERE ${import_drizzle_orm8.sql.join(conds, import_drizzle_orm8.sql` AND `)}` : import_drizzle_orm8.sql``;
|
|
3852
3926
|
}
|
|
3853
3927
|
function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
3854
|
-
const { relations = {}, publishedRelations = false } = options;
|
|
3928
|
+
const { relations = {}, publishedRelations = false, sortableColumns } = options;
|
|
3855
3929
|
function tableRaw() {
|
|
3856
3930
|
return import_drizzle_orm8.sql.raw(quoteIdent3(tableName));
|
|
3857
3931
|
}
|
|
@@ -3889,8 +3963,9 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
3889
3963
|
sort_order = "asc",
|
|
3890
3964
|
search
|
|
3891
3965
|
} = opts;
|
|
3892
|
-
|
|
3893
|
-
|
|
3966
|
+
const allowed = sortableColumns ?? SORTABLE_FIELDS2;
|
|
3967
|
+
if (!allowed.has(sort_by)) {
|
|
3968
|
+
throw codeError("INVALID_SORT_FIELD", `'${sort_by}' is not sortable.`);
|
|
3894
3969
|
}
|
|
3895
3970
|
const offset = (page - 1) * per_page;
|
|
3896
3971
|
const conds = buildConditions(published_only, filters, search);
|
|
@@ -4139,16 +4214,38 @@ function createCmsApp(options) {
|
|
|
4139
4214
|
return result.rows[0]?.role ?? null;
|
|
4140
4215
|
};
|
|
4141
4216
|
const requireHierarchy = createHierarchyMiddleware(rolesRegistry, getUserRole);
|
|
4217
|
+
const fieldKeyMaps = Object.fromEntries([
|
|
4218
|
+
...Object.entries(registry.content_types).map(([typeName, ct]) => [
|
|
4219
|
+
typeName,
|
|
4220
|
+
createFieldKeyMap(ct.fields)
|
|
4221
|
+
]),
|
|
4222
|
+
...Object.entries(registry.taxonomy_types).map(([typeName, tt]) => [
|
|
4223
|
+
typeName,
|
|
4224
|
+
createFieldKeyMap(tt.fields)
|
|
4225
|
+
]),
|
|
4226
|
+
...Object.entries(registry.paragraph_types).map(([typeName, pt]) => [
|
|
4227
|
+
typeName,
|
|
4228
|
+
createFieldKeyMap(pt.fields)
|
|
4229
|
+
])
|
|
4230
|
+
]);
|
|
4231
|
+
const projectors = buildProjectors(registry, fieldKeyMaps);
|
|
4232
|
+
const sortableColumnsFor = (typeName) => new Set(
|
|
4233
|
+
[...SORTABLE_FIELDS].map((label) => fieldKeyMaps[typeName].columnFor(label) ?? label)
|
|
4234
|
+
);
|
|
4142
4235
|
const contentRepos = Object.fromEntries(
|
|
4143
4236
|
Object.entries(registry.content_types).map(([typeName, ct]) => [
|
|
4144
4237
|
typeName,
|
|
4145
|
-
createDrizzleContentRepository(db, ct.db.table_name
|
|
4238
|
+
createDrizzleContentRepository(db, ct.db.table_name, {
|
|
4239
|
+
sortableColumns: sortableColumnsFor(typeName)
|
|
4240
|
+
})
|
|
4146
4241
|
])
|
|
4147
4242
|
);
|
|
4148
4243
|
const taxonomyRepos = Object.fromEntries(
|
|
4149
4244
|
Object.entries(registry.taxonomy_types).map(([typeName, tt]) => [
|
|
4150
4245
|
typeName,
|
|
4151
|
-
createDrizzleContentRepository(db, tt.db.table_name
|
|
4246
|
+
createDrizzleContentRepository(db, tt.db.table_name, {
|
|
4247
|
+
sortableColumns: sortableColumnsFor(typeName)
|
|
4248
|
+
})
|
|
4152
4249
|
])
|
|
4153
4250
|
);
|
|
4154
4251
|
const repos = { ...contentRepos, ...taxonomyRepos };
|
|
@@ -4158,7 +4255,8 @@ function createCmsApp(options) {
|
|
|
4158
4255
|
typeName,
|
|
4159
4256
|
createDrizzleContentRepository(db, ct.db.table_name, {
|
|
4160
4257
|
relations: buildRelationsMap(ct.fields, registry),
|
|
4161
|
-
publishedRelations: true
|
|
4258
|
+
publishedRelations: true,
|
|
4259
|
+
sortableColumns: sortableColumnsFor(typeName)
|
|
4162
4260
|
})
|
|
4163
4261
|
])
|
|
4164
4262
|
);
|
|
@@ -4167,7 +4265,8 @@ function createCmsApp(options) {
|
|
|
4167
4265
|
typeName,
|
|
4168
4266
|
createDrizzleContentRepository(db, tt.db.table_name, {
|
|
4169
4267
|
relations: buildRelationsMap(tt.fields, registry),
|
|
4170
|
-
publishedRelations: true
|
|
4268
|
+
publishedRelations: true,
|
|
4269
|
+
sortableColumns: sortableColumnsFor(typeName)
|
|
4171
4270
|
})
|
|
4172
4271
|
])
|
|
4173
4272
|
);
|
|
@@ -4175,11 +4274,15 @@ function createCmsApp(options) {
|
|
|
4175
4274
|
const graphqlRepos = Object.fromEntries([
|
|
4176
4275
|
...Object.entries(registry.content_types).map(([typeName, ct]) => [
|
|
4177
4276
|
typeName,
|
|
4178
|
-
createDrizzleContentRepository(db, ct.db.table_name
|
|
4277
|
+
createDrizzleContentRepository(db, ct.db.table_name, {
|
|
4278
|
+
sortableColumns: sortableColumnsFor(typeName)
|
|
4279
|
+
})
|
|
4179
4280
|
]),
|
|
4180
4281
|
...Object.entries(registry.taxonomy_types).map(([typeName, tt]) => [
|
|
4181
4282
|
typeName,
|
|
4182
|
-
createDrizzleContentRepository(db, tt.db.table_name
|
|
4283
|
+
createDrizzleContentRepository(db, tt.db.table_name, {
|
|
4284
|
+
sortableColumns: sortableColumnsFor(typeName)
|
|
4285
|
+
})
|
|
4183
4286
|
])
|
|
4184
4287
|
]);
|
|
4185
4288
|
registerAuthRoutes(app, db);
|
|
@@ -4220,17 +4323,7 @@ function createCmsApp(options) {
|
|
|
4220
4323
|
paths: {}
|
|
4221
4324
|
})
|
|
4222
4325
|
);
|
|
4223
|
-
|
|
4224
|
-
...Object.entries(registry.content_types).map(([typeName, ct]) => [
|
|
4225
|
-
typeName,
|
|
4226
|
-
createFieldKeyMap(ct.fields)
|
|
4227
|
-
]),
|
|
4228
|
-
...Object.entries(registry.taxonomy_types).map(([typeName, tt]) => [
|
|
4229
|
-
typeName,
|
|
4230
|
-
createFieldKeyMap(tt.fields)
|
|
4231
|
-
])
|
|
4232
|
-
]);
|
|
4233
|
-
registerPublicContentRoutes(app, registry, publicRepos, fieldKeyMaps, publicPaths, listRateLimit, programmaticResolver);
|
|
4326
|
+
registerPublicContentRoutes(app, registry, publicRepos, projectors, publicPaths, listRateLimit, programmaticResolver);
|
|
4234
4327
|
registerPublicMediaRoutes(app, mediaRepo, publicPaths, listRateLimit);
|
|
4235
4328
|
if (options.graphql?.enabled) {
|
|
4236
4329
|
const gqlOptions = options.graphql;
|
|
@@ -4277,7 +4370,7 @@ function createCmsApp(options) {
|
|
|
4277
4370
|
);
|
|
4278
4371
|
registerSchemaRoute(app, registry, db);
|
|
4279
4372
|
registerUserRoutes(app, db, requirePermission, requireHierarchy);
|
|
4280
|
-
registerAdminContentRoutes(app, registry, repos,
|
|
4373
|
+
registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo, requirePermission, db);
|
|
4281
4374
|
registerAdminMediaRoutes(app, mediaRepo, storage, requirePermission, maxFileSize);
|
|
4282
4375
|
return { prefix, app };
|
|
4283
4376
|
}
|