@bobbykim/manguito-cms-cli 0.4.0 → 0.4.3
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.
|
@@ -49,7 +49,7 @@ var init_esm_shims = __esm({
|
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
// ../api/dist/chunk-
|
|
52
|
+
// ../api/dist/chunk-TK4DNRFF.mjs
|
|
53
53
|
init_esm_shims();
|
|
54
54
|
import crypto from "crypto";
|
|
55
55
|
|
|
@@ -1212,7 +1212,7 @@ Subquery.prototype.getSQL = function() {
|
|
|
1212
1212
|
return new SQL([this]);
|
|
1213
1213
|
};
|
|
1214
1214
|
|
|
1215
|
-
// ../api/dist/chunk-
|
|
1215
|
+
// ../api/dist/chunk-TK4DNRFF.mjs
|
|
1216
1216
|
function quoteIdent(name) {
|
|
1217
1217
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
1218
1218
|
return `"${name}"`;
|
|
@@ -1239,16 +1239,46 @@ async function fetchParagraphMediaIds(db, pType, itemId, fieldName) {
|
|
|
1239
1239
|
}
|
|
1240
1240
|
return ids;
|
|
1241
1241
|
}
|
|
1242
|
-
|
|
1242
|
+
function nestedParagraphFields(pType, registry) {
|
|
1243
|
+
if (!registry) return [];
|
|
1244
|
+
const out = [];
|
|
1245
|
+
for (const f of pType.fields) {
|
|
1246
|
+
if (f.db_column !== null) continue;
|
|
1247
|
+
const comp = f.ui_component;
|
|
1248
|
+
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
1249
|
+
const nType = registry.paragraph_types[comp.ref];
|
|
1250
|
+
if (nType) out.push({ fieldName: f.name, nType });
|
|
1251
|
+
}
|
|
1252
|
+
return out;
|
|
1253
|
+
}
|
|
1254
|
+
async function fetchParagraphRowIds(db, pType, itemId, fieldName) {
|
|
1255
|
+
const tbl = sql.raw(quoteIdent(pType.db.table_name));
|
|
1256
|
+
const result = await db.execute(
|
|
1257
|
+
sql`SELECT id FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`
|
|
1258
|
+
);
|
|
1259
|
+
return result.rows.map((r) => r["id"]).filter((id) => typeof id === "string");
|
|
1260
|
+
}
|
|
1261
|
+
async function persistParagraphField(db, itemId, parentType, fieldName, pType, items, registry) {
|
|
1243
1262
|
const removed = await fetchParagraphMediaIds(db, pType, itemId, fieldName);
|
|
1263
|
+
const nested = nestedParagraphFields(pType, registry);
|
|
1264
|
+
if (nested.length > 0) {
|
|
1265
|
+
const staleIds = await fetchParagraphRowIds(db, pType, itemId, fieldName);
|
|
1266
|
+
for (const rowId of staleIds) {
|
|
1267
|
+
for (const n of nested) {
|
|
1268
|
+
const d = await deleteParagraphField(db, rowId, n.fieldName, n.nType, registry);
|
|
1269
|
+
removed.push(...d.removed);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1244
1273
|
const tbl = sql.raw(quoteIdent(pType.db.table_name));
|
|
1245
1274
|
await db.execute(sql`DELETE FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`);
|
|
1246
1275
|
const mediaFieldNames = paragraphMediaFieldNames(pType);
|
|
1247
1276
|
const added = [];
|
|
1248
1277
|
for (let i = 0; i < items.length; i++) {
|
|
1249
1278
|
const pItem = items[i];
|
|
1279
|
+
const rowId = crypto.randomUUID();
|
|
1250
1280
|
const data = {
|
|
1251
|
-
id:
|
|
1281
|
+
id: rowId,
|
|
1252
1282
|
parent_id: itemId,
|
|
1253
1283
|
parent_type: parentType,
|
|
1254
1284
|
parent_field: fieldName,
|
|
@@ -1267,11 +1297,35 @@ async function persistParagraphField(db, itemId, parentType, fieldName, pType, i
|
|
|
1267
1297
|
const val = pItem[name];
|
|
1268
1298
|
if (typeof val === "string" && val !== "") added.push(val);
|
|
1269
1299
|
}
|
|
1300
|
+
for (const n of nested) {
|
|
1301
|
+
const nestedItems = Array.isArray(pItem[n.fieldName]) ? pItem[n.fieldName] : [];
|
|
1302
|
+
const d = await persistParagraphField(
|
|
1303
|
+
db,
|
|
1304
|
+
rowId,
|
|
1305
|
+
pType.db.table_name,
|
|
1306
|
+
n.fieldName,
|
|
1307
|
+
n.nType,
|
|
1308
|
+
nestedItems,
|
|
1309
|
+
registry
|
|
1310
|
+
);
|
|
1311
|
+
added.push(...d.added);
|
|
1312
|
+
removed.push(...d.removed);
|
|
1313
|
+
}
|
|
1270
1314
|
}
|
|
1271
1315
|
return { added, removed };
|
|
1272
1316
|
}
|
|
1273
|
-
async function deleteParagraphField(db, itemId, fieldName, pType) {
|
|
1317
|
+
async function deleteParagraphField(db, itemId, fieldName, pType, registry) {
|
|
1274
1318
|
const removed = await fetchParagraphMediaIds(db, pType, itemId, fieldName);
|
|
1319
|
+
const nested = nestedParagraphFields(pType, registry);
|
|
1320
|
+
if (nested.length > 0) {
|
|
1321
|
+
const rowIds = await fetchParagraphRowIds(db, pType, itemId, fieldName);
|
|
1322
|
+
for (const rowId of rowIds) {
|
|
1323
|
+
for (const n of nested) {
|
|
1324
|
+
const d = await deleteParagraphField(db, rowId, n.fieldName, n.nType, registry);
|
|
1325
|
+
removed.push(...d.removed);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1275
1329
|
const tbl = sql.raw(quoteIdent(pType.db.table_name));
|
|
1276
1330
|
await db.execute(sql`DELETE FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`);
|
|
1277
1331
|
return { added: [], removed };
|
|
@@ -1334,7 +1388,7 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
1334
1388
|
const parentIds = rows.map((r) => r["id"]);
|
|
1335
1389
|
const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `);
|
|
1336
1390
|
const result = await db.execute(
|
|
1337
|
-
sql`SELECT * FROM ${sql.raw(quoteIdent(rel.table))} WHERE parent_id IN (${inList}) ORDER BY "order" ASC`
|
|
1391
|
+
sql`SELECT * FROM ${sql.raw(quoteIdent(rel.table))} WHERE parent_id IN (${inList}) AND parent_field = ${fieldName} ORDER BY "order" ASC`
|
|
1338
1392
|
);
|
|
1339
1393
|
const byParent = groupBy(result.rows, "parent_id");
|
|
1340
1394
|
for (const row of rows) {
|
|
@@ -1414,7 +1468,7 @@ async function resolveRelationBareIds(db, rows, fieldName, rel) {
|
|
|
1414
1468
|
const parentIds = rows.map((r) => r["id"]);
|
|
1415
1469
|
const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `);
|
|
1416
1470
|
const result = await db.execute(
|
|
1417
|
-
sql`SELECT id, parent_id FROM ${sql.raw(quoteIdent(rel.table))} WHERE parent_id IN (${inList}) ORDER BY "order" ASC`
|
|
1471
|
+
sql`SELECT id, parent_id FROM ${sql.raw(quoteIdent(rel.table))} WHERE parent_id IN (${inList}) AND parent_field = ${fieldName} ORDER BY "order" ASC`
|
|
1418
1472
|
);
|
|
1419
1473
|
const byParent = groupBy(result.rows, "parent_id");
|
|
1420
1474
|
for (const row of rows) {
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
buildRelationsMap,
|
|
11
11
|
init_esm_shims,
|
|
12
12
|
resolveRelationField
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-OAUBUE7N.js";
|
|
14
14
|
|
|
15
15
|
// ../../node_modules/.pnpm/@whatwg-node+fetch@0.10.13/node_modules/@whatwg-node/fetch/dist/shouldSkipPonyfill.js
|
|
16
16
|
var require_shouldSkipPonyfill = __commonJS({
|
|
@@ -10785,10 +10785,13 @@ var require_parser = __commonJS({
|
|
|
10785
10785
|
}
|
|
10786
10786
|
});
|
|
10787
10787
|
|
|
10788
|
-
// ../api/dist/handler-
|
|
10788
|
+
// ../api/dist/handler-CGHZG3J5.mjs
|
|
10789
10789
|
init_esm_shims();
|
|
10790
10790
|
|
|
10791
|
-
// ../api/dist/chunk-
|
|
10791
|
+
// ../api/dist/chunk-GM55HGWA.mjs
|
|
10792
|
+
init_esm_shims();
|
|
10793
|
+
|
|
10794
|
+
// ../../node_modules/.pnpm/graphql-yoga@5.21.2_graphql@16.14.2/node_modules/graphql-yoga/esm/index.js
|
|
10792
10795
|
init_esm_shims();
|
|
10793
10796
|
|
|
10794
10797
|
// ../../node_modules/.pnpm/@graphql-yoga+logger@2.0.1/node_modules/@graphql-yoga/logger/esm/index.js
|
|
@@ -30118,7 +30121,7 @@ function createYoga(options) {
|
|
|
30118
30121
|
});
|
|
30119
30122
|
}
|
|
30120
30123
|
|
|
30121
|
-
// ../api/dist/chunk-
|
|
30124
|
+
// ../api/dist/chunk-GM55HGWA.mjs
|
|
30122
30125
|
var import_dataloader = __toESM(require_dataloader(), 1);
|
|
30123
30126
|
|
|
30124
30127
|
// ../../node_modules/.pnpm/@escape.tech+graphql-armor@3.2.0_@envelop+core@5.5.1_@escape.tech+graphql-armor-types@0.7.0/node_modules/@escape.tech/graphql-armor/dist/graphql-armor.esm.js
|
|
@@ -30876,7 +30879,7 @@ var EnvelopArmor = class {
|
|
|
30876
30879
|
}
|
|
30877
30880
|
};
|
|
30878
30881
|
|
|
30879
|
-
// ../api/dist/chunk-
|
|
30882
|
+
// ../api/dist/chunk-GM55HGWA.mjs
|
|
30880
30883
|
var DateTimeScalar = new GraphQLScalarType({
|
|
30881
30884
|
name: "DateTime",
|
|
30882
30885
|
description: "ISO-8601 date-time string",
|
|
@@ -31071,16 +31074,22 @@ function scalarFieldResolver(schemaName) {
|
|
|
31071
31074
|
function relationFieldResolver(typeName, schemaFieldName) {
|
|
31072
31075
|
return (parent, _args, ctx) => ctx.loaders.load(typeName, schemaFieldName, parent);
|
|
31073
31076
|
}
|
|
31074
|
-
function programmaticFieldResolver(typeName, schemaFieldName) {
|
|
31077
|
+
function programmaticFieldResolver(typeName, schemaFieldName, mediaFieldNames = []) {
|
|
31075
31078
|
return async (parent, _args, ctx) => {
|
|
31076
31079
|
let p = ctx.programmaticMemo.get(parent);
|
|
31077
31080
|
if (!p) {
|
|
31078
|
-
p =
|
|
31081
|
+
p = resolveProgrammaticRow(typeName, parent, ctx, mediaFieldNames);
|
|
31079
31082
|
ctx.programmaticMemo.set(parent, p);
|
|
31080
31083
|
}
|
|
31081
31084
|
return (await p)[schemaFieldName];
|
|
31082
31085
|
};
|
|
31083
31086
|
}
|
|
31087
|
+
async function resolveProgrammaticRow(typeName, parent, ctx, mediaFieldNames) {
|
|
31088
|
+
if (mediaFieldNames.length === 0) return ctx.resolver.resolveItem(typeName, parent);
|
|
31089
|
+
const enriched = { ...parent };
|
|
31090
|
+
await Promise.all(mediaFieldNames.map((name) => ctx.loaders.load(typeName, name, enriched)));
|
|
31091
|
+
return ctx.resolver.resolveItem(typeName, enriched);
|
|
31092
|
+
}
|
|
31084
31093
|
function collectionResolver(typeName, nameMap) {
|
|
31085
31094
|
return async (_root, args, ctx) => {
|
|
31086
31095
|
const page = args.page ?? 1;
|
|
@@ -31206,6 +31215,7 @@ function buildGraphQLSchema(registry) {
|
|
|
31206
31215
|
return GraphQLString;
|
|
31207
31216
|
}
|
|
31208
31217
|
function buildObjectType(machineName, type) {
|
|
31218
|
+
const mediaFieldNames = type.fields.filter((f) => f.field_type === "image" || f.field_type === "video" || f.field_type === "file").map((f) => f.name);
|
|
31209
31219
|
return new GraphQLObjectType({
|
|
31210
31220
|
name: graphqlTypeName(machineName),
|
|
31211
31221
|
fields: () => {
|
|
@@ -31224,7 +31234,7 @@ function buildGraphQLSchema(registry) {
|
|
|
31224
31234
|
const outType = outputTypeForField(field);
|
|
31225
31235
|
let resolve;
|
|
31226
31236
|
if (field.field_type === "programmatic") {
|
|
31227
|
-
resolve = programmaticFieldResolver(machineName, field.name);
|
|
31237
|
+
resolve = programmaticFieldResolver(machineName, field.name, mediaFieldNames);
|
|
31228
31238
|
} else if (field.field_type === "reference" || field.field_type === "paragraph" || field.field_type === "image" || field.field_type === "video" || field.field_type === "file") {
|
|
31229
31239
|
resolve = relationFieldResolver(machineName, field.name);
|
|
31230
31240
|
} else {
|
|
@@ -31375,6 +31385,14 @@ function introspectionPlugin(enabled) {
|
|
|
31375
31385
|
}
|
|
31376
31386
|
};
|
|
31377
31387
|
}
|
|
31388
|
+
function isClientSafeGraphQLError(error) {
|
|
31389
|
+
let current = error;
|
|
31390
|
+
while (current != null && typeof current === "object" && current.name === "GraphQLError") {
|
|
31391
|
+
if (current.originalError == null) return true;
|
|
31392
|
+
current = current.originalError;
|
|
31393
|
+
}
|
|
31394
|
+
return false;
|
|
31395
|
+
}
|
|
31378
31396
|
function createGraphQLHandler(registry, repos, resolver, db, options) {
|
|
31379
31397
|
const schema = buildGraphQLSchema(registry);
|
|
31380
31398
|
const { plugins } = buildArmorPlugin({
|
|
@@ -31386,6 +31404,9 @@ function createGraphQLHandler(registry, repos, resolver, db, options) {
|
|
|
31386
31404
|
graphqlEndpoint: "/graphql",
|
|
31387
31405
|
graphiql: options.graphiql,
|
|
31388
31406
|
landingPage: false,
|
|
31407
|
+
maskedErrors: {
|
|
31408
|
+
maskError: (error, message, isDev2) => isClientSafeGraphQLError(error) ? error : maskError(error, message, isDev2)
|
|
31409
|
+
},
|
|
31389
31410
|
// buildArmorPlugin's return type is `unknown[]` (see security.ts) since Armor's
|
|
31390
31411
|
// plugin shapes aren't part of its public typings; Yoga's `plugins` option only
|
|
31391
31412
|
// accepts its own `Plugin` type, so the cast bridges the two.
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
resolveRelationField,
|
|
37
37
|
sql,
|
|
38
38
|
tracer
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-OAUBUE7N.js";
|
|
40
40
|
|
|
41
41
|
// ../../node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js
|
|
42
42
|
var require_identity = __commonJS({
|
|
@@ -47458,6 +47458,23 @@ function quoteIdent(name) {
|
|
|
47458
47458
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
47459
47459
|
return `"${name}"`;
|
|
47460
47460
|
}
|
|
47461
|
+
async function loadParagraphRows(db, registry2, pType, parentId, fieldName) {
|
|
47462
|
+
const result = await db.execute(
|
|
47463
|
+
sql`SELECT * FROM ${sql.raw(quoteIdent(pType.db.table_name))} WHERE parent_id = ${parentId} AND parent_field = ${fieldName} ORDER BY "order" ASC`
|
|
47464
|
+
);
|
|
47465
|
+
const rows = result.rows;
|
|
47466
|
+
for (const nf of pType.fields) {
|
|
47467
|
+
if (nf.db_column !== null) continue;
|
|
47468
|
+
const comp = nf.ui_component;
|
|
47469
|
+
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
47470
|
+
const nType = registry2.paragraph_types[comp.ref];
|
|
47471
|
+
if (!nType) continue;
|
|
47472
|
+
for (const row of rows) {
|
|
47473
|
+
row[nf.name] = await loadParagraphRows(db, registry2, nType, row["id"], nf.name);
|
|
47474
|
+
}
|
|
47475
|
+
}
|
|
47476
|
+
return rows;
|
|
47477
|
+
}
|
|
47461
47478
|
async function lookupBasePathId(db, pathOrName) {
|
|
47462
47479
|
const r = await db.execute(
|
|
47463
47480
|
sql`SELECT id FROM base_paths WHERE path = ${pathOrName} OR name = ${pathOrName} LIMIT 1`
|
|
@@ -47609,10 +47626,7 @@ function registerAdminContentRoutes(app, registry2, repos, mediaRepo, requirePer
|
|
|
47609
47626
|
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
47610
47627
|
const pType = registry2.paragraph_types[comp.ref];
|
|
47611
47628
|
if (!pType) continue;
|
|
47612
|
-
|
|
47613
|
-
sql`SELECT * FROM ${sql.raw(quoteIdent(pType.db.table_name))} WHERE parent_id = ${id} AND parent_field = ${f.name} ORDER BY "order" ASC`
|
|
47614
|
-
);
|
|
47615
|
-
row[f.name] = r.rows;
|
|
47629
|
+
row[f.name] = await loadParagraphRows(db, registry2, pType, id, f.name);
|
|
47616
47630
|
} else if (f.db_column.junction) {
|
|
47617
47631
|
const j = f.db_column.junction;
|
|
47618
47632
|
const r = await db.execute(
|
|
@@ -47687,82 +47701,85 @@ function registerAdminContentRoutes(app, registry2, repos, mediaRepo, requirePer
|
|
|
47687
47701
|
);
|
|
47688
47702
|
}
|
|
47689
47703
|
}
|
|
47690
|
-
|
|
47691
|
-
|
|
47704
|
+
return writeNewItem(c, body);
|
|
47705
|
+
}
|
|
47706
|
+
);
|
|
47707
|
+
const writeNewItem = async (c, body) => {
|
|
47708
|
+
const fieldErrors = checkRequiredFields(requiredFields, body);
|
|
47709
|
+
if (fieldErrors.length > 0) {
|
|
47710
|
+
return c.json(
|
|
47711
|
+
{
|
|
47712
|
+
ok: false,
|
|
47713
|
+
error: {
|
|
47714
|
+
code: "VALIDATION_ERROR",
|
|
47715
|
+
message: "Required fields are missing",
|
|
47716
|
+
details: fieldErrors
|
|
47717
|
+
}
|
|
47718
|
+
},
|
|
47719
|
+
422
|
|
47720
|
+
);
|
|
47721
|
+
}
|
|
47722
|
+
if (body["published"] === true) {
|
|
47723
|
+
const publishDeny = await requirePermission("content:edit")(c, async () => {
|
|
47724
|
+
});
|
|
47725
|
+
if (publishDeny) return publishDeny;
|
|
47726
|
+
}
|
|
47727
|
+
const paragraphFields = contentType.fields.filter((f) => f.db_column === null);
|
|
47728
|
+
const junctionFields = contentType.fields.filter(
|
|
47729
|
+
(f) => f.db_column !== null && f.db_column.junction !== void 0
|
|
47730
|
+
);
|
|
47731
|
+
const columnFields = contentType.fields.filter(
|
|
47732
|
+
(f) => f.db_column !== null && f.db_column.junction === void 0
|
|
47733
|
+
);
|
|
47734
|
+
const columnData = {
|
|
47735
|
+
published: body["published"] ?? false
|
|
47736
|
+
};
|
|
47737
|
+
if (contentType.only_one) {
|
|
47738
|
+
columnData["slug"] = typeName;
|
|
47739
|
+
} else {
|
|
47740
|
+
columnData["slug"] = body["slug"];
|
|
47741
|
+
}
|
|
47742
|
+
for (const f of columnFields) {
|
|
47743
|
+
const val = body[f.name];
|
|
47744
|
+
columnData[f.db_column.column_name] = val !== void 0 ? val : null;
|
|
47745
|
+
}
|
|
47746
|
+
if (db) {
|
|
47747
|
+
const basePathId = await lookupBasePathId(db, contentType.default_base_path);
|
|
47748
|
+
if (!basePathId) {
|
|
47692
47749
|
return c.json(
|
|
47693
47750
|
{
|
|
47694
47751
|
ok: false,
|
|
47695
47752
|
error: {
|
|
47696
|
-
code: "
|
|
47697
|
-
message:
|
|
47698
|
-
details: fieldErrors
|
|
47753
|
+
code: "BASE_PATH_NOT_FOUND",
|
|
47754
|
+
message: `Base path '${contentType.default_base_path}' not found \u2014 run manguito migrate`
|
|
47699
47755
|
}
|
|
47700
47756
|
},
|
|
47701
|
-
|
|
47757
|
+
500
|
|
47702
47758
|
);
|
|
47703
47759
|
}
|
|
47704
|
-
|
|
47705
|
-
|
|
47706
|
-
|
|
47707
|
-
|
|
47708
|
-
|
|
47709
|
-
|
|
47710
|
-
const
|
|
47711
|
-
|
|
47712
|
-
|
|
47713
|
-
|
|
47714
|
-
(
|
|
47715
|
-
|
|
47716
|
-
|
|
47717
|
-
published: body["published"] ?? false
|
|
47718
|
-
};
|
|
47719
|
-
if (contentType.only_one) {
|
|
47720
|
-
columnData["slug"] = typeName;
|
|
47721
|
-
} else {
|
|
47722
|
-
columnData["slug"] = body["slug"];
|
|
47760
|
+
columnData["base_path_id"] = basePathId;
|
|
47761
|
+
}
|
|
47762
|
+
const item = await repo.create(columnData);
|
|
47763
|
+
const itemId = item["id"];
|
|
47764
|
+
const mediaDeltas = [topLevelMediaDelta(mediaFields, null, body)];
|
|
47765
|
+
if (db) {
|
|
47766
|
+
for (const f of paragraphFields) {
|
|
47767
|
+
const comp = f.ui_component;
|
|
47768
|
+
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
47769
|
+
const pType = registry2.paragraph_types[comp.ref];
|
|
47770
|
+
if (!pType) continue;
|
|
47771
|
+
const items = Array.isArray(body[f.name]) ? body[f.name] : [];
|
|
47772
|
+
mediaDeltas.push(await persistParagraphField(db, itemId, contentType.db.table_name, f.name, pType, items, registry2));
|
|
47723
47773
|
}
|
|
47724
|
-
for (const f of
|
|
47725
|
-
const
|
|
47726
|
-
|
|
47774
|
+
for (const f of junctionFields) {
|
|
47775
|
+
const junction = f.db_column.junction;
|
|
47776
|
+
const relatedIds = Array.isArray(body[f.name]) ? body[f.name].filter((v2) => typeof v2 === "string") : [];
|
|
47777
|
+
await persistJunctionField(db, itemId, junction, relatedIds);
|
|
47727
47778
|
}
|
|
47728
|
-
if (db) {
|
|
47729
|
-
const basePathId = await lookupBasePathId(db, contentType.default_base_path);
|
|
47730
|
-
if (!basePathId) {
|
|
47731
|
-
return c.json(
|
|
47732
|
-
{
|
|
47733
|
-
ok: false,
|
|
47734
|
-
error: {
|
|
47735
|
-
code: "BASE_PATH_NOT_FOUND",
|
|
47736
|
-
message: `Base path '${contentType.default_base_path}' not found \u2014 run manguito migrate`
|
|
47737
|
-
}
|
|
47738
|
-
},
|
|
47739
|
-
500
|
|
47740
|
-
);
|
|
47741
|
-
}
|
|
47742
|
-
columnData["base_path_id"] = basePathId;
|
|
47743
|
-
}
|
|
47744
|
-
const item = await repo.create(columnData);
|
|
47745
|
-
const itemId = item["id"];
|
|
47746
|
-
const mediaDeltas = [topLevelMediaDelta(mediaFields, null, body)];
|
|
47747
|
-
if (db) {
|
|
47748
|
-
for (const f of paragraphFields) {
|
|
47749
|
-
const comp = f.ui_component;
|
|
47750
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
47751
|
-
const pType = registry2.paragraph_types[comp.ref];
|
|
47752
|
-
if (!pType) continue;
|
|
47753
|
-
const items = Array.isArray(body[f.name]) ? body[f.name] : [];
|
|
47754
|
-
mediaDeltas.push(await persistParagraphField(db, itemId, contentType.db.table_name, f.name, pType, items));
|
|
47755
|
-
}
|
|
47756
|
-
for (const f of junctionFields) {
|
|
47757
|
-
const junction = f.db_column.junction;
|
|
47758
|
-
const relatedIds = Array.isArray(body[f.name]) ? body[f.name].filter((v2) => typeof v2 === "string") : [];
|
|
47759
|
-
await persistJunctionField(db, itemId, junction, relatedIds);
|
|
47760
|
-
}
|
|
47761
|
-
}
|
|
47762
|
-
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
47763
|
-
return c.json({ ok: true, data: item }, 201);
|
|
47764
47779
|
}
|
|
47765
|
-
|
|
47780
|
+
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
47781
|
+
return c.json({ ok: true, data: item }, 201);
|
|
47782
|
+
};
|
|
47766
47783
|
app.patch(
|
|
47767
47784
|
`/admin/api/${basePath}/:id`,
|
|
47768
47785
|
requirePermission("content:edit"),
|
|
@@ -47817,71 +47834,92 @@ function registerAdminContentRoutes(app, registry2, repos, mediaRepo, requirePer
|
|
|
47817
47834
|
);
|
|
47818
47835
|
}
|
|
47819
47836
|
}
|
|
47820
|
-
|
|
47821
|
-
|
|
47822
|
-
|
|
47823
|
-
|
|
47824
|
-
|
|
47825
|
-
|
|
47826
|
-
|
|
47827
|
-
|
|
47828
|
-
|
|
47829
|
-
|
|
47830
|
-
|
|
47831
|
-
code: "PUBLISH_VALIDATION_ERROR",
|
|
47832
|
-
message: "Cannot publish \u2014 required fields are missing",
|
|
47833
|
-
details: fieldErrors
|
|
47834
|
-
}
|
|
47835
|
-
},
|
|
47836
|
-
422
|
|
47837
|
-
);
|
|
47838
|
-
}
|
|
47839
|
-
}
|
|
47840
|
-
const patchParagraphFields = contentType.fields.filter((f) => f.db_column === null);
|
|
47841
|
-
const patchJunctionFields = contentType.fields.filter(
|
|
47842
|
-
(f) => f.db_column !== null && f.db_column.junction !== void 0
|
|
47843
|
-
);
|
|
47844
|
-
const patchColumnFields = contentType.fields.filter(
|
|
47845
|
-
(f) => f.db_column !== null && f.db_column.junction === void 0
|
|
47846
|
-
);
|
|
47847
|
-
const patchData = {};
|
|
47848
|
-
if (!contentType.only_one && "slug" in body) patchData["slug"] = body["slug"];
|
|
47849
|
-
if ("published" in body) patchData["published"] = body["published"];
|
|
47850
|
-
for (const f of patchColumnFields) {
|
|
47851
|
-
if (f.name in body) {
|
|
47852
|
-
const val = body[f.name];
|
|
47853
|
-
patchData[f.db_column.column_name] = val !== void 0 ? val : null;
|
|
47854
|
-
}
|
|
47855
|
-
}
|
|
47856
|
-
const updated = await repo.update(id, patchData);
|
|
47857
|
-
if (!updated) {
|
|
47837
|
+
return writeExistingItem(c, id, existing, body);
|
|
47838
|
+
}
|
|
47839
|
+
);
|
|
47840
|
+
const writeExistingItem = async (c, id, existing, body) => {
|
|
47841
|
+
if (body["published"] === true) {
|
|
47842
|
+
const publishDeny = await requirePermission("content:edit")(c, async () => {
|
|
47843
|
+
});
|
|
47844
|
+
if (publishDeny) return publishDeny;
|
|
47845
|
+
const merged = { ...existing, ...body };
|
|
47846
|
+
const fieldErrors = checkRequiredFields(requiredFields, merged);
|
|
47847
|
+
if (fieldErrors.length > 0) {
|
|
47858
47848
|
return c.json(
|
|
47859
|
-
{
|
|
47860
|
-
|
|
47849
|
+
{
|
|
47850
|
+
ok: false,
|
|
47851
|
+
error: {
|
|
47852
|
+
code: "PUBLISH_VALIDATION_ERROR",
|
|
47853
|
+
message: "Cannot publish \u2014 required fields are missing",
|
|
47854
|
+
details: fieldErrors
|
|
47855
|
+
}
|
|
47856
|
+
},
|
|
47857
|
+
422
|
|
47861
47858
|
);
|
|
47862
47859
|
}
|
|
47863
|
-
|
|
47864
|
-
|
|
47865
|
-
|
|
47866
|
-
|
|
47867
|
-
|
|
47868
|
-
|
|
47869
|
-
|
|
47870
|
-
|
|
47871
|
-
|
|
47872
|
-
|
|
47873
|
-
|
|
47874
|
-
|
|
47875
|
-
|
|
47876
|
-
|
|
47877
|
-
|
|
47878
|
-
await persistJunctionField(db, id, junction, relatedIds);
|
|
47879
|
-
}
|
|
47860
|
+
}
|
|
47861
|
+
const patchParagraphFields = contentType.fields.filter((f) => f.db_column === null);
|
|
47862
|
+
const patchJunctionFields = contentType.fields.filter(
|
|
47863
|
+
(f) => f.db_column !== null && f.db_column.junction !== void 0
|
|
47864
|
+
);
|
|
47865
|
+
const patchColumnFields = contentType.fields.filter(
|
|
47866
|
+
(f) => f.db_column !== null && f.db_column.junction === void 0
|
|
47867
|
+
);
|
|
47868
|
+
const patchData = {};
|
|
47869
|
+
if (!contentType.only_one && "slug" in body) patchData["slug"] = body["slug"];
|
|
47870
|
+
if ("published" in body) patchData["published"] = body["published"];
|
|
47871
|
+
for (const f of patchColumnFields) {
|
|
47872
|
+
if (f.name in body) {
|
|
47873
|
+
const val = body[f.name];
|
|
47874
|
+
patchData[f.db_column.column_name] = val !== void 0 ? val : null;
|
|
47880
47875
|
}
|
|
47881
|
-
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
47882
|
-
return c.json({ ok: true, data: updated });
|
|
47883
47876
|
}
|
|
47884
|
-
|
|
47877
|
+
const updated = await repo.update(id, patchData);
|
|
47878
|
+
if (!updated) {
|
|
47879
|
+
return c.json(
|
|
47880
|
+
{ ok: false, error: { code: "NOT_FOUND", message: "Not found" } },
|
|
47881
|
+
404
|
|
47882
|
+
);
|
|
47883
|
+
}
|
|
47884
|
+
const mediaDeltas = [
|
|
47885
|
+
topLevelMediaDelta(mediaFields, existing, body)
|
|
47886
|
+
];
|
|
47887
|
+
if (db) {
|
|
47888
|
+
for (const f of patchParagraphFields) {
|
|
47889
|
+
const comp = f.ui_component;
|
|
47890
|
+
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
47891
|
+
const pType = registry2.paragraph_types[comp.ref];
|
|
47892
|
+
if (!pType) continue;
|
|
47893
|
+
const items = Array.isArray(body[f.name]) ? body[f.name] : [];
|
|
47894
|
+
mediaDeltas.push(await persistParagraphField(db, id, contentType.db.table_name, f.name, pType, items, registry2));
|
|
47895
|
+
}
|
|
47896
|
+
for (const f of patchJunctionFields) {
|
|
47897
|
+
const junction = f.db_column.junction;
|
|
47898
|
+
const relatedIds = Array.isArray(body[f.name]) ? body[f.name].filter((v2) => typeof v2 === "string") : [];
|
|
47899
|
+
await persistJunctionField(db, id, junction, relatedIds);
|
|
47900
|
+
}
|
|
47901
|
+
}
|
|
47902
|
+
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
47903
|
+
return c.json({ ok: true, data: updated });
|
|
47904
|
+
};
|
|
47905
|
+
if (contentType.only_one) {
|
|
47906
|
+
app.put(
|
|
47907
|
+
`/admin/api/${basePath}`,
|
|
47908
|
+
requirePermission("content:edit"),
|
|
47909
|
+
async (c) => {
|
|
47910
|
+
const body = await c.req.json();
|
|
47911
|
+
const existingResult = await repo.findMany({ page: 1, per_page: 1 });
|
|
47912
|
+
const existing = existingResult.data[0];
|
|
47913
|
+
if (!existing) {
|
|
47914
|
+
const createDeny = await requirePermission("content:create")(c, async () => {
|
|
47915
|
+
});
|
|
47916
|
+
if (createDeny) return createDeny;
|
|
47917
|
+
return writeNewItem(c, body);
|
|
47918
|
+
}
|
|
47919
|
+
return writeExistingItem(c, existing["id"], existing, body);
|
|
47920
|
+
}
|
|
47921
|
+
);
|
|
47922
|
+
}
|
|
47885
47923
|
app.delete(
|
|
47886
47924
|
`/admin/api/${basePath}/:id`,
|
|
47887
47925
|
requirePermission("content:delete"),
|
|
@@ -47903,7 +47941,7 @@ function registerAdminContentRoutes(app, registry2, repos, mediaRepo, requirePer
|
|
|
47903
47941
|
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
47904
47942
|
const pType = registry2.paragraph_types[comp.ref];
|
|
47905
47943
|
if (!pType) continue;
|
|
47906
|
-
mediaDeltas.push(await deleteParagraphField(db, id, f.name, pType));
|
|
47944
|
+
mediaDeltas.push(await deleteParagraphField(db, id, f.name, pType, registry2));
|
|
47907
47945
|
}
|
|
47908
47946
|
}
|
|
47909
47947
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
@@ -48018,7 +48056,7 @@ function registerAdminContentRoutes(app, registry2, repos, mediaRepo, requirePer
|
|
|
48018
48056
|
const pType = registry2.paragraph_types[comp.ref];
|
|
48019
48057
|
if (!pType) continue;
|
|
48020
48058
|
const items = Array.isArray(body[f.name]) ? body[f.name] : [];
|
|
48021
|
-
mediaDeltas.push(await persistParagraphField(db, taxItemId, taxonomyType.db.table_name, f.name, pType, items));
|
|
48059
|
+
mediaDeltas.push(await persistParagraphField(db, taxItemId, taxonomyType.db.table_name, f.name, pType, items, registry2));
|
|
48022
48060
|
}
|
|
48023
48061
|
}
|
|
48024
48062
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
@@ -48087,7 +48125,7 @@ function registerAdminContentRoutes(app, registry2, repos, mediaRepo, requirePer
|
|
|
48087
48125
|
const pType = registry2.paragraph_types[comp.ref];
|
|
48088
48126
|
if (!pType) continue;
|
|
48089
48127
|
const items = Array.isArray(body[f.name]) ? body[f.name] : [];
|
|
48090
|
-
mediaDeltas.push(await persistParagraphField(db, id, taxonomyType.db.table_name, f.name, pType, items));
|
|
48128
|
+
mediaDeltas.push(await persistParagraphField(db, id, taxonomyType.db.table_name, f.name, pType, items, registry2));
|
|
48091
48129
|
}
|
|
48092
48130
|
}
|
|
48093
48131
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
@@ -48115,7 +48153,7 @@ function registerAdminContentRoutes(app, registry2, repos, mediaRepo, requirePer
|
|
|
48115
48153
|
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
48116
48154
|
const pType = registry2.paragraph_types[comp.ref];
|
|
48117
48155
|
if (!pType) continue;
|
|
48118
|
-
mediaDeltas.push(await deleteParagraphField(db, id, f.name, pType));
|
|
48156
|
+
mediaDeltas.push(await deleteParagraphField(db, id, f.name, pType, registry2));
|
|
48119
48157
|
}
|
|
48120
48158
|
}
|
|
48121
48159
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
@@ -49064,7 +49102,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
49064
49102
|
}
|
|
49065
49103
|
var API_VERSION = (() => {
|
|
49066
49104
|
try {
|
|
49067
|
-
return "0.3.
|
|
49105
|
+
return "0.3.3";
|
|
49068
49106
|
} catch {
|
|
49069
49107
|
return "0.0.0";
|
|
49070
49108
|
}
|
|
@@ -49529,6 +49567,16 @@ function createCmsApp(options) {
|
|
|
49529
49567
|
])
|
|
49530
49568
|
);
|
|
49531
49569
|
const publicRepos = { ...publicContentRepos, ...publicTaxonomyRepos };
|
|
49570
|
+
const graphqlRepos = Object.fromEntries([
|
|
49571
|
+
...Object.entries(registry2.content_types).map(([typeName, ct2]) => [
|
|
49572
|
+
typeName,
|
|
49573
|
+
createDrizzleContentRepository(db, ct2.db.table_name)
|
|
49574
|
+
]),
|
|
49575
|
+
...Object.entries(registry2.taxonomy_types).map(([typeName, tt2]) => [
|
|
49576
|
+
typeName,
|
|
49577
|
+
createDrizzleContentRepository(db, tt2.db.table_name)
|
|
49578
|
+
])
|
|
49579
|
+
]);
|
|
49532
49580
|
registerAuthRoutes(app, db);
|
|
49533
49581
|
app.use("/admin/api/*", authMiddleware);
|
|
49534
49582
|
app.use("/admin/api/*", mustChangePasswordCheck);
|
|
@@ -49569,8 +49617,8 @@ function createCmsApp(options) {
|
|
|
49569
49617
|
const gqlOptions = options.graphql;
|
|
49570
49618
|
let gqlHandler = null;
|
|
49571
49619
|
let initError = null;
|
|
49572
|
-
const ready = import("./handler-
|
|
49573
|
-
gqlHandler = createGraphQLHandler(registry2,
|
|
49620
|
+
const ready = import("./handler-CGHZG3J5-TCG6K3FE.js").then(({ createGraphQLHandler }) => {
|
|
49621
|
+
gqlHandler = createGraphQLHandler(registry2, graphqlRepos, programmaticResolver, db, gqlOptions);
|
|
49574
49622
|
}).catch((err) => {
|
|
49575
49623
|
initError = err;
|
|
49576
49624
|
const message = err instanceof Error ? err.message : String(err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobbykim/manguito-cms-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Bobby Kim <bobby.sihun.kim@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/bobbykim89/manguito-cms",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"tsup": "^8.5.1",
|
|
30
30
|
"typescript": "^6.0.3",
|
|
31
31
|
"vite": "^8.1.3",
|
|
32
|
-
"@bobbykim/manguito-cms-admin": "0.
|
|
32
|
+
"@bobbykim/manguito-cms-admin": "0.4.0",
|
|
33
33
|
"@bobbykim/manguito-cms-core": "0.3.0",
|
|
34
34
|
"@bobbykim/manguito-cms-db": "0.1.3",
|
|
35
|
-
"@bobbykim/manguito-cms-api": "0.3.
|
|
35
|
+
"@bobbykim/manguito-cms-api": "0.3.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^25.9.5",
|