opacacms 0.1.20 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +638 -732
- package/dist/admin/auth-client.d.ts +39 -39
- package/dist/admin/index.d.ts +1 -0
- package/dist/admin/index.js +2397 -1405
- package/dist/admin/react.d.ts +1 -1
- package/dist/admin/react.js +8 -0
- package/dist/admin/router.d.ts +1 -0
- package/dist/admin/stores/ui.d.ts +10 -0
- package/dist/admin/ui/admin-layout.d.ts +4 -4
- package/dist/admin/ui/components/DataDetailView.d.ts +1 -1
- package/dist/admin/ui/components/DetailSheet.d.ts +19 -0
- package/dist/admin/ui/components/PluginSettingsForm.d.ts +11 -0
- package/dist/admin/ui/components/fields/BooleanField.d.ts +2 -1
- package/dist/admin/ui/components/fields/DateField.d.ts +1 -1
- package/dist/admin/ui/components/fields/FieldLabel.d.ts +11 -0
- package/dist/admin/ui/components/fields/FileField.d.ts +1 -1
- package/dist/admin/ui/components/fields/NumberField.d.ts +1 -1
- package/dist/admin/ui/components/fields/RadioField.d.ts +1 -1
- package/dist/admin/ui/components/fields/RelationshipField.d.ts +3 -1
- package/dist/admin/ui/components/fields/SelectField.d.ts +1 -1
- package/dist/admin/ui/components/fields/TextAreaField.d.ts +1 -1
- package/dist/admin/ui/components/fields/TextField.d.ts +1 -1
- package/dist/admin/ui/components/fields/VirtualField.d.ts +1 -0
- package/dist/admin/ui/components/fields/index.d.ts +16 -16
- package/dist/admin/ui/components/fields/richtext-editor/index.d.ts +1 -1
- package/dist/admin/ui/components/media/AssetManagerModal.d.ts +1 -1
- package/dist/admin/ui/components/toast.d.ts +1 -1
- package/dist/admin/ui/components/ui/accordion.d.ts +1 -1
- package/dist/admin/ui/components/ui/button.d.ts +1 -1
- package/dist/admin/ui/components/ui/collapsible.d.ts +1 -1
- package/dist/admin/ui/components/ui/dialog.d.ts +1 -1
- package/dist/admin/ui/components/ui/group.d.ts +1 -1
- package/dist/admin/ui/components/ui/index.d.ts +17 -17
- package/dist/admin/ui/components/ui/input.d.ts +1 -1
- package/dist/admin/ui/components/ui/label.d.ts +1 -1
- package/dist/admin/ui/components/ui/radio-group.d.ts +1 -1
- package/dist/admin/ui/components/ui/relationship.d.ts +4 -4
- package/dist/admin/ui/components/ui/select.d.ts +1 -1
- package/dist/admin/ui/components/ui/sheet.d.ts +1 -1
- package/dist/admin/ui/components/ui/tabs.d.ts +1 -1
- package/dist/admin/ui/components/versions-sheet.d.ts +11 -0
- package/dist/admin/ui/views/media-registry-view.d.ts +1 -1
- package/dist/admin/ui/views/settings-view.d.ts +2 -2
- package/dist/admin/vue.d.ts +17 -0
- package/dist/admin/vue.js +8 -0
- package/dist/admin/webcomponent.js +2 -2
- package/dist/admin.css +1 -1
- package/dist/auth/index.d.ts +101 -41
- package/dist/{chunk-qkn1ykrj.js → chunk-0bq155dy.js} +94 -31
- package/dist/{chunk-2z8wxx9g.js → chunk-0gtxnxmd.js} +98 -25
- package/dist/{chunk-v521d72w.js → chunk-3rdhbedb.js} +1 -1
- package/dist/chunk-51z3x7kq.js +20 -0
- package/dist/{chunk-7fyepksb.js → chunk-526a3gqx.js} +1 -1
- package/dist/{chunk-wmvjvn7b.js → chunk-6qq3ne6b.js} +39 -1
- package/dist/{chunk-0am1m47g.js → chunk-6v1fw7q7.js} +5 -5
- package/dist/{chunk-zvwb67nd.js → chunk-7y1nbmw6.js} +36 -5
- package/dist/chunk-8scgdznr.js +44 -0
- package/dist/{chunk-erh6x75p.js → chunk-b3kr8w41.js} +58 -7
- package/dist/chunk-bexcv7xe.js +36 -0
- package/dist/{chunk-16vgcf3k.js → chunk-byq8g0rd.js} +1 -1
- package/dist/{chunk-wq314kkx.js → chunk-d1asgtke.js} +94 -31
- package/dist/{chunk-xtwc125q.js → chunk-dykn5hr6.js} +8 -8
- package/dist/{chunk-pxh5encs.js → chunk-esrg9qj0.js} +102 -44
- package/dist/chunk-fj19qccp.js +78 -0
- package/dist/{chunk-x2ejaftz.js → chunk-gmee4mdc.js} +98 -27
- package/dist/{chunk-xa7rjsn2.js → chunk-j53pz21t.js} +2 -2
- package/dist/{chunk-9y3m1xkx.js → chunk-kc4jfnv7.js} +480 -85
- package/dist/chunk-mkn49zmy.js +102 -0
- package/dist/{chunk-n1xraw7j.js → chunk-qb6ztvw9.js} +1 -1
- package/dist/{chunk-2kyhqvhc.js → chunk-qxt9vge8.js} +1 -1
- package/dist/chunk-r39em4yj.js +29 -0
- package/dist/chunk-rqyjjqgy.js +91 -0
- package/dist/chunk-rsf0tpy1.js +8 -0
- package/dist/chunk-swtcpvhf.js +2442 -0
- package/dist/chunk-t0zg026p.js +71 -0
- package/dist/chunk-twpvxfce.js +64 -0
- package/dist/{chunk-ybbbqj63.js → chunk-v9z61v3g.js} +15 -0
- package/dist/{chunk-jwjk85ze.js → chunk-ywm4t2gm.js} +6 -2
- package/dist/cli/commands/plugin-build.d.ts +1 -0
- package/dist/cli/commands/plugin-init.d.ts +1 -0
- package/dist/cli/commands/plugin-sync.d.ts +1 -0
- package/dist/cli/index.js +24 -6
- package/dist/config-utils.d.ts +1 -1
- package/dist/config.d.ts +21 -4
- package/dist/db/better-sqlite.d.ts +1 -1
- package/dist/db/better-sqlite.js +5 -5
- package/dist/db/bun-sqlite.d.ts +1 -1
- package/dist/db/bun-sqlite.js +5 -5
- package/dist/db/d1.d.ts +1 -1
- package/dist/db/d1.js +5 -5
- package/dist/db/index.js +9 -9
- package/dist/db/postgres.d.ts +1 -1
- package/dist/db/postgres.js +5 -5
- package/dist/db/sqlite.d.ts +1 -1
- package/dist/db/sqlite.js +5 -5
- package/dist/index.js +4 -3
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/ui-bridge.d.ts +12 -0
- package/dist/plugins/utils.d.ts +5 -0
- package/dist/runtimes/bun.js +13 -7
- package/dist/runtimes/cloudflare-workers.js +5 -5
- package/dist/runtimes/next.js +5 -5
- package/dist/runtimes/node.js +13 -7
- package/dist/schema/collection.d.ts +9 -30
- package/dist/schema/fields/base.d.ts +3 -2
- package/dist/schema/fields/index.d.ts +12 -0
- package/dist/schema/fields/validation.test.d.ts +1 -0
- package/dist/schema/global.d.ts +10 -15
- package/dist/schema/index.js +22 -14
- package/dist/server/admin-router.d.ts +2 -2
- package/dist/server/admin.d.ts +2 -1
- package/dist/server/collection-router.d.ts +1 -1
- package/dist/server/handlers.d.ts +10 -0
- package/dist/server/middlewares/admin.d.ts +2 -2
- package/dist/server/middlewares/auth.d.ts +1 -1
- package/dist/server/middlewares/context.d.ts +2 -0
- package/dist/server/middlewares/rate-limit.d.ts +1 -1
- package/dist/server/openapi.d.ts +2 -0
- package/dist/server/plugins-loader.d.ts +6 -0
- package/dist/server/router.d.ts +3 -3
- package/dist/server/routers/admin.d.ts +2 -2
- package/dist/server/routers/auth.d.ts +1 -1
- package/dist/server/routers/collections.d.ts +1 -1
- package/dist/server/routers/plugins.d.ts +18 -0
- package/dist/server/setup-middlewares.d.ts +2 -2
- package/dist/server/system-router.d.ts +1 -1
- package/dist/server.js +11 -7
- package/dist/storage/adapters/local.d.ts +1 -1
- package/dist/storage/adapters/s3.d.ts +1 -1
- package/dist/types.d.ts +227 -12
- package/dist/utils/logger.d.ts +13 -35
- package/dist/validation.d.ts +60 -8
- package/dist/validator.d.ts +1 -1
- package/package.json +21 -7
- package/dist/admin/ui/components/DataDetailSheet.d.ts +0 -13
- package/dist/admin/ui/components/ui/relationship-detail-sheet.d.ts +0 -9
- package/dist/chunk-62ev8gnc.js +0 -41
- package/dist/chunk-j4d50hrx.js +0 -20
- package/dist/chunk-nb7ctdg8.js +0 -311
|
@@ -3,18 +3,18 @@ import {
|
|
|
3
3
|
flattenPayload,
|
|
4
4
|
pushSchema,
|
|
5
5
|
unflattenRow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-dykn5hr6.js";
|
|
7
7
|
import {
|
|
8
8
|
BaseDatabaseAdapter
|
|
9
9
|
} from "./chunk-s8mqwnm1.js";
|
|
10
|
-
import {
|
|
11
|
-
logger
|
|
12
|
-
} from "./chunk-62ev8gnc.js";
|
|
13
10
|
import {
|
|
14
11
|
flattenFields,
|
|
15
12
|
getRelationalFields,
|
|
16
13
|
toSnakeCase
|
|
17
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-qxt9vge8.js";
|
|
15
|
+
import {
|
|
16
|
+
logger
|
|
17
|
+
} from "./chunk-t0zg026p.js";
|
|
18
18
|
|
|
19
19
|
// src/db/d1.ts
|
|
20
20
|
import fs from "node:fs/promises";
|
|
@@ -127,19 +127,11 @@ class D1Adapter extends BaseDatabaseAdapter {
|
|
|
127
127
|
const dbFields = flattenFields(colDef?.fields || []);
|
|
128
128
|
const validCols = new Set(dbFields.map((f) => toSnakeCase(f.name)));
|
|
129
129
|
validCols.add("id");
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const now = new Date().toISOString();
|
|
136
|
-
if (!flatData[createdAtField])
|
|
137
|
-
flatData[createdAtField] = now;
|
|
138
|
-
if (!flatData[updatedAtField])
|
|
139
|
-
flatData[updatedAtField] = now;
|
|
140
|
-
validCols.add(createdAtField);
|
|
141
|
-
validCols.add(updatedAtField);
|
|
142
|
-
}
|
|
130
|
+
validCols.add(toSnakeCase("createdAt"));
|
|
131
|
+
validCols.add(toSnakeCase("updatedAt"));
|
|
132
|
+
const now = new Date().toISOString();
|
|
133
|
+
flatData[toSnakeCase("createdAt")] = now;
|
|
134
|
+
flatData[toSnakeCase("updatedAt")] = now;
|
|
143
135
|
const filteredData = {};
|
|
144
136
|
for (const col of Object.keys(flatData)) {
|
|
145
137
|
if (validCols.has(col)) {
|
|
@@ -272,7 +264,87 @@ class D1Adapter extends BaseDatabaseAdapter {
|
|
|
272
264
|
qb = qb.orderBy("created_at", "desc");
|
|
273
265
|
}
|
|
274
266
|
const rows = await qb.execute();
|
|
275
|
-
|
|
267
|
+
if (rows.length === 0) {
|
|
268
|
+
const totalPages2 = Math.ceil(total / limit);
|
|
269
|
+
return {
|
|
270
|
+
docs: [],
|
|
271
|
+
totalDocs: total,
|
|
272
|
+
limit,
|
|
273
|
+
totalPages: totalPages2,
|
|
274
|
+
page,
|
|
275
|
+
pagingCounter: offset + 1,
|
|
276
|
+
hasNextPage: page * limit < total,
|
|
277
|
+
hasPrevPage: page > 1,
|
|
278
|
+
prevPage: page > 1 ? page - 1 : null,
|
|
279
|
+
nextPage: page < totalPages2 ? page + 1 : null
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const rowIds = rows.map((r) => r.id);
|
|
283
|
+
const docs = rows.map((r) => unflattenRow(r));
|
|
284
|
+
const colDef = this._collections.find((c) => c.slug === collection);
|
|
285
|
+
if (colDef) {
|
|
286
|
+
const relationalFields = getRelationalFields(colDef.fields);
|
|
287
|
+
for (const field of relationalFields) {
|
|
288
|
+
if (!field.name)
|
|
289
|
+
continue;
|
|
290
|
+
const snakeName = toSnakeCase(field.name);
|
|
291
|
+
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
292
|
+
const joinTableName = `${toSnakeCase(collection)}_${snakeName}_relations`.toLowerCase();
|
|
293
|
+
try {
|
|
294
|
+
const allRelations = await this._db.selectFrom(joinTableName).selectAll().where("source_id", "in", rowIds).orderBy("order", "asc").execute();
|
|
295
|
+
const relationsBySource = allRelations.reduce((acc, r) => {
|
|
296
|
+
if (!acc[r.source_id])
|
|
297
|
+
acc[r.source_id] = [];
|
|
298
|
+
acc[r.source_id].push(r.target_id);
|
|
299
|
+
return acc;
|
|
300
|
+
}, {});
|
|
301
|
+
const parts = field.name.split("__");
|
|
302
|
+
for (let docIdx = 0;docIdx < docs.length; docIdx++) {
|
|
303
|
+
const rowId = rowIds[docIdx];
|
|
304
|
+
let current = docs[docIdx];
|
|
305
|
+
for (let i = 0;i < parts.length - 1; i++) {
|
|
306
|
+
if (!current[parts[i]])
|
|
307
|
+
current[parts[i]] = {};
|
|
308
|
+
current = current[parts[i]];
|
|
309
|
+
}
|
|
310
|
+
current[parts[parts.length - 1]] = relationsBySource[rowId] || [];
|
|
311
|
+
}
|
|
312
|
+
} catch (e) {}
|
|
313
|
+
} else if (field.type === "blocks" && field.blocks) {
|
|
314
|
+
const blocksBySource = {};
|
|
315
|
+
for (const b of field.blocks) {
|
|
316
|
+
const blockTableName = `${collection}_${snakeName}_${toSnakeCase(b.slug)}`.toLowerCase();
|
|
317
|
+
try {
|
|
318
|
+
const allBlocks = await this._db.selectFrom(blockTableName).selectAll().where("_parent_id", "in", rowIds).execute();
|
|
319
|
+
for (const blk of allBlocks) {
|
|
320
|
+
const uf = unflattenRow(blk);
|
|
321
|
+
uf.blockType = blk.block_type;
|
|
322
|
+
if (!blocksBySource[blk._parent_id])
|
|
323
|
+
blocksBySource[blk._parent_id] = [];
|
|
324
|
+
blocksBySource[blk._parent_id].push(uf);
|
|
325
|
+
}
|
|
326
|
+
} catch (e) {}
|
|
327
|
+
}
|
|
328
|
+
const parts = field.name.split("__");
|
|
329
|
+
for (let docIdx = 0;docIdx < docs.length; docIdx++) {
|
|
330
|
+
const rowId = rowIds[docIdx];
|
|
331
|
+
const blockData = blocksBySource[rowId] || [];
|
|
332
|
+
blockData.sort((a, b) => a._order - b._order);
|
|
333
|
+
blockData.forEach((b) => {
|
|
334
|
+
delete b._order;
|
|
335
|
+
delete b._parentId;
|
|
336
|
+
});
|
|
337
|
+
let current = docs[docIdx];
|
|
338
|
+
for (let i = 0;i < parts.length - 1; i++) {
|
|
339
|
+
if (!current[parts[i]])
|
|
340
|
+
current[parts[i]] = {};
|
|
341
|
+
current = current[parts[i]];
|
|
342
|
+
}
|
|
343
|
+
current[parts[parts.length - 1]] = blockData;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
276
348
|
const totalPages = Math.ceil(total / limit);
|
|
277
349
|
return {
|
|
278
350
|
docs: docs.filter(Boolean),
|
|
@@ -515,21 +587,12 @@ class D1Adapter extends BaseDatabaseAdapter {
|
|
|
515
587
|
const existing = await this.findGlobal(slug);
|
|
516
588
|
const flatData = flattenPayload(data);
|
|
517
589
|
const globalDef = this._globals.find((g) => g.slug === slug);
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
const config = typeof globalDef?.timestamps === "object" ? globalDef.timestamps : {};
|
|
521
|
-
const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
|
|
522
|
-
flatData[updatedField] = new Date().toISOString();
|
|
523
|
-
}
|
|
590
|
+
const now = new Date().toISOString();
|
|
591
|
+
flatData[toSnakeCase("updatedAt")] = now;
|
|
524
592
|
if (!existing) {
|
|
525
593
|
if (!flatData.id)
|
|
526
594
|
flatData.id = "global";
|
|
527
|
-
|
|
528
|
-
const config = typeof globalDef?.timestamps === "object" ? globalDef.timestamps : {};
|
|
529
|
-
const createdField = toSnakeCase(config.createdAt || "createdAt");
|
|
530
|
-
if (!flatData[createdField])
|
|
531
|
-
flatData[createdField] = flatData[toSnakeCase(config.updatedAt || "updatedAt")];
|
|
532
|
-
}
|
|
595
|
+
flatData[toSnakeCase("createdAt")] = now;
|
|
533
596
|
await this._db.insertInto(tableName).values(flatData).execute();
|
|
534
597
|
} else {
|
|
535
598
|
await this._db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
logger
|
|
3
|
-
} from "./chunk-62ev8gnc.js";
|
|
4
1
|
import {
|
|
5
2
|
flattenFields,
|
|
6
3
|
getRelationalFields,
|
|
7
4
|
mapFieldToPostgresType,
|
|
8
5
|
mapFieldToSQLiteType,
|
|
9
6
|
toSnakeCase
|
|
10
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-qxt9vge8.js";
|
|
11
8
|
import {
|
|
12
9
|
getSystemCollections,
|
|
13
10
|
init_system_schema
|
|
14
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-v9z61v3g.js";
|
|
12
|
+
import {
|
|
13
|
+
logger
|
|
14
|
+
} from "./chunk-t0zg026p.js";
|
|
15
15
|
|
|
16
16
|
// src/db/kysely/data-mapper.ts
|
|
17
17
|
function toCamelCase(str) {
|
|
18
18
|
if (str.startsWith("_"))
|
|
19
19
|
return str;
|
|
20
|
-
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
20
|
+
return str.replace(/_+([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
21
21
|
}
|
|
22
22
|
function flattenPayload(payload, prefix = "", excludeKeys = []) {
|
|
23
23
|
if (typeof payload !== "object" || payload === null || Array.isArray(payload))
|
|
@@ -39,7 +39,7 @@ function flattenPayload(payload, prefix = "", excludeKeys = []) {
|
|
|
39
39
|
if (value === undefined)
|
|
40
40
|
continue;
|
|
41
41
|
const colName = `${prefix}${toSnakeCase(key)}`;
|
|
42
|
-
if (typeof value === "object" && value !== null) {
|
|
42
|
+
if (typeof value === "object" && value !== null && !(value instanceof Date)) {
|
|
43
43
|
result[colName] = JSON.stringify(value);
|
|
44
44
|
} else {
|
|
45
45
|
result[colName] = value;
|
|
@@ -240,7 +240,7 @@ async function pushSchema(db, dialect, collections, globals = [], options = {})
|
|
|
240
240
|
assertSafeIdentifier(tableName);
|
|
241
241
|
expectedTableNames.add(tableName);
|
|
242
242
|
const flattenedFields = flattenFields(collection.fields);
|
|
243
|
-
const hasTimestamps =
|
|
243
|
+
const hasTimestamps = true;
|
|
244
244
|
const versions = collection.versions;
|
|
245
245
|
if (!existingTableSet.has(tableName)) {
|
|
246
246
|
logger.info(` -> Creating table: ${logger.format("green", `"${tableName}"`)}`);
|
|
@@ -3,16 +3,17 @@ import {
|
|
|
3
3
|
flattenPayload,
|
|
4
4
|
pushSchema,
|
|
5
5
|
unflattenRow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-dykn5hr6.js";
|
|
7
7
|
import {
|
|
8
8
|
BaseDatabaseAdapter
|
|
9
9
|
} from "./chunk-s8mqwnm1.js";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
} from "./chunk-62ev8gnc.js";
|
|
13
|
-
import {
|
|
11
|
+
getRelationalFields,
|
|
14
12
|
toSnakeCase
|
|
15
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-qxt9vge8.js";
|
|
14
|
+
import {
|
|
15
|
+
logger
|
|
16
|
+
} from "./chunk-t0zg026p.js";
|
|
16
17
|
import {
|
|
17
18
|
__require
|
|
18
19
|
} from "./chunk-8sqjbsgt.js";
|
|
@@ -131,16 +132,13 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
131
132
|
}
|
|
132
133
|
if (!flatData.id)
|
|
133
134
|
flatData.id = crypto.randomUUID();
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (!flatData[updatedField])
|
|
142
|
-
flatData[updatedField] = new Date().toISOString();
|
|
143
|
-
}
|
|
135
|
+
const now = new Date().toISOString();
|
|
136
|
+
const createdKey = toSnakeCase("createdAt");
|
|
137
|
+
const updatedKey = toSnakeCase("updatedAt");
|
|
138
|
+
if (!flatData[createdKey])
|
|
139
|
+
flatData[createdKey] = now;
|
|
140
|
+
if (!flatData[updatedKey])
|
|
141
|
+
flatData[updatedKey] = now;
|
|
144
142
|
await tx.insertInto(tableName).values(flatData).execute();
|
|
145
143
|
for (const [key, values] of Object.entries(hasManyData)) {
|
|
146
144
|
const joinTableName = `${tableName}_${toSnakeCase(key)}_relations`.toLowerCase();
|
|
@@ -187,8 +185,8 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
187
185
|
const unflattened = unflattenRow(row);
|
|
188
186
|
const colDef = this._collections.find((c) => c.slug === collection);
|
|
189
187
|
if (colDef) {
|
|
190
|
-
const { getRelationalFields, toSnakeCase: toSnakeCase2 } = await import("./chunk-
|
|
191
|
-
const relationalFields =
|
|
188
|
+
const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await import("./chunk-526a3gqx.js");
|
|
189
|
+
const relationalFields = getRelationalFields2(colDef.fields);
|
|
192
190
|
for (const field of relationalFields) {
|
|
193
191
|
if (!field.name)
|
|
194
192
|
continue;
|
|
@@ -258,7 +256,87 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
258
256
|
qb = qb.orderBy("created_at", "desc");
|
|
259
257
|
}
|
|
260
258
|
const rows = await qb.execute();
|
|
261
|
-
|
|
259
|
+
if (rows.length === 0) {
|
|
260
|
+
const totalPages2 = Math.ceil(total / limit);
|
|
261
|
+
return {
|
|
262
|
+
docs: [],
|
|
263
|
+
totalDocs: total,
|
|
264
|
+
limit,
|
|
265
|
+
totalPages: totalPages2,
|
|
266
|
+
page,
|
|
267
|
+
pagingCounter: offset + 1,
|
|
268
|
+
hasNextPage: page * limit < total,
|
|
269
|
+
hasPrevPage: page > 1,
|
|
270
|
+
prevPage: page > 1 ? page - 1 : null,
|
|
271
|
+
nextPage: page < totalPages2 ? page + 1 : null
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
const rowIds = rows.map((r) => r.id);
|
|
275
|
+
const docs = rows.map((r) => unflattenRow(r));
|
|
276
|
+
const colDef = this._collections.find((c) => c.slug === collection);
|
|
277
|
+
if (colDef) {
|
|
278
|
+
const relationalFields = getRelationalFields(colDef.fields);
|
|
279
|
+
for (const field of relationalFields) {
|
|
280
|
+
if (!field.name)
|
|
281
|
+
continue;
|
|
282
|
+
const snakeName = toSnakeCase(field.name);
|
|
283
|
+
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
284
|
+
const joinTableName = `${toSnakeCase(collection)}_${snakeName}_relations`.toLowerCase();
|
|
285
|
+
try {
|
|
286
|
+
const allRelations = await this._db.selectFrom(joinTableName).selectAll().where("source_id", "in", rowIds).orderBy("order", "asc").execute();
|
|
287
|
+
const relationsBySource = allRelations.reduce((acc, r) => {
|
|
288
|
+
if (!acc[r.source_id])
|
|
289
|
+
acc[r.source_id] = [];
|
|
290
|
+
acc[r.source_id].push(r.target_id);
|
|
291
|
+
return acc;
|
|
292
|
+
}, {});
|
|
293
|
+
const parts = field.name.split("__");
|
|
294
|
+
for (let docIdx = 0;docIdx < docs.length; docIdx++) {
|
|
295
|
+
const rowId = rowIds[docIdx];
|
|
296
|
+
let current = docs[docIdx];
|
|
297
|
+
for (let i = 0;i < parts.length - 1; i++) {
|
|
298
|
+
if (!current[parts[i]])
|
|
299
|
+
current[parts[i]] = {};
|
|
300
|
+
current = current[parts[i]];
|
|
301
|
+
}
|
|
302
|
+
current[parts[parts.length - 1]] = relationsBySource[rowId] || [];
|
|
303
|
+
}
|
|
304
|
+
} catch (e) {}
|
|
305
|
+
} else if (field.type === "blocks" && field.blocks) {
|
|
306
|
+
const blocksBySource = {};
|
|
307
|
+
for (const b of field.blocks) {
|
|
308
|
+
const blockTableName = `${collection}_${snakeName}_${toSnakeCase(b.slug)}`.toLowerCase();
|
|
309
|
+
try {
|
|
310
|
+
const allBlocks = await this._db.selectFrom(blockTableName).selectAll().where("_parent_id", "in", rowIds).execute();
|
|
311
|
+
for (const blk of allBlocks) {
|
|
312
|
+
const uf = unflattenRow(blk);
|
|
313
|
+
uf.blockType = blk.block_type;
|
|
314
|
+
if (!blocksBySource[blk._parent_id])
|
|
315
|
+
blocksBySource[blk._parent_id] = [];
|
|
316
|
+
blocksBySource[blk._parent_id].push(uf);
|
|
317
|
+
}
|
|
318
|
+
} catch (e) {}
|
|
319
|
+
}
|
|
320
|
+
const parts = field.name.split("__");
|
|
321
|
+
for (let docIdx = 0;docIdx < docs.length; docIdx++) {
|
|
322
|
+
const rowId = rowIds[docIdx];
|
|
323
|
+
const blockData = blocksBySource[rowId] || [];
|
|
324
|
+
blockData.sort((a, b) => a._order - b._order);
|
|
325
|
+
blockData.forEach((b) => {
|
|
326
|
+
delete b._order;
|
|
327
|
+
delete b._parentId;
|
|
328
|
+
});
|
|
329
|
+
let current = docs[docIdx];
|
|
330
|
+
for (let i = 0;i < parts.length - 1; i++) {
|
|
331
|
+
if (!current[parts[i]])
|
|
332
|
+
current[parts[i]] = {};
|
|
333
|
+
current = current[parts[i]];
|
|
334
|
+
}
|
|
335
|
+
current[parts[parts.length - 1]] = blockData;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
262
340
|
const totalPages = Math.ceil(total / limit);
|
|
263
341
|
return {
|
|
264
342
|
docs: docs.filter(Boolean),
|
|
@@ -300,12 +378,7 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
300
378
|
delete flatData[key];
|
|
301
379
|
}
|
|
302
380
|
}
|
|
303
|
-
|
|
304
|
-
if (ts) {
|
|
305
|
-
const config = typeof colDef?.timestamps === "object" ? colDef.timestamps : {};
|
|
306
|
-
const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
|
|
307
|
-
flatData[updatedField] = new Date().toISOString();
|
|
308
|
-
}
|
|
381
|
+
flatData.updatedAt = new Date().toISOString();
|
|
309
382
|
const tableName = toSnakeCase(collection);
|
|
310
383
|
if (Object.keys(flatData).length > 0) {
|
|
311
384
|
await tx.updateTable(tableName).set(flatData).where("id", "=", current.id).execute();
|
|
@@ -365,13 +438,7 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
365
438
|
delete flatData[key];
|
|
366
439
|
}
|
|
367
440
|
}
|
|
368
|
-
|
|
369
|
-
const ts = colDef?.timestamps !== false;
|
|
370
|
-
if (ts) {
|
|
371
|
-
const config = typeof colDef?.timestamps === "object" ? colDef.timestamps : {};
|
|
372
|
-
const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
|
|
373
|
-
flatData[updatedField] = new Date().toISOString();
|
|
374
|
-
}
|
|
441
|
+
flatData.updatedAt = new Date().toISOString();
|
|
375
442
|
if (Object.keys(flatData).length > 0) {
|
|
376
443
|
const result = await qb.set(flatData).executeTakeFirst();
|
|
377
444
|
return Number(result.numUpdatedRows || 0);
|
|
@@ -465,21 +532,12 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
465
532
|
const globalDef = this._globals.find((g) => g.slug === slug);
|
|
466
533
|
const jsonFields = globalDef?.fields.filter((f) => f.name && (["richtext", "json", "file"].includes(f.type) || f.localized)).map((f) => f.name) || [];
|
|
467
534
|
const flatData = flattenPayload(data, "", jsonFields);
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
const config = typeof globalDef?.timestamps === "object" ? globalDef.timestamps : {};
|
|
471
|
-
const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
|
|
472
|
-
flatData[updatedField] = new Date().toISOString();
|
|
473
|
-
}
|
|
535
|
+
const now = new Date().toISOString();
|
|
536
|
+
flatData[toSnakeCase("updatedAt")] = now;
|
|
474
537
|
if (!existing) {
|
|
475
538
|
if (!flatData.id)
|
|
476
539
|
flatData.id = "global";
|
|
477
|
-
|
|
478
|
-
const config = typeof globalDef?.timestamps === "object" ? globalDef.timestamps : {};
|
|
479
|
-
const createdField = toSnakeCase(config.createdAt || "createdAt");
|
|
480
|
-
if (!flatData[createdField])
|
|
481
|
-
flatData[createdField] = new Date().toISOString();
|
|
482
|
-
}
|
|
540
|
+
flatData[toSnakeCase("createdAt")] = now;
|
|
483
541
|
await this._db.insertInto(tableName).values(flatData).execute();
|
|
484
542
|
} else {
|
|
485
543
|
await this._db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
|
|
@@ -510,7 +568,7 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
510
568
|
}
|
|
511
569
|
}
|
|
512
570
|
async migrate(collections, globals = []) {
|
|
513
|
-
const { getSystemCollections } = await import("./chunk-
|
|
571
|
+
const { getSystemCollections } = await import("./chunk-3rdhbedb.js");
|
|
514
572
|
this._collections = [...getSystemCollections(), ...collections];
|
|
515
573
|
this._globals = globals;
|
|
516
574
|
if (this.push && this._db) {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pluginSyncCommand
|
|
3
|
+
} from "./chunk-twpvxfce.js";
|
|
4
|
+
import {
|
|
5
|
+
logger
|
|
6
|
+
} from "./chunk-t0zg026p.js";
|
|
7
|
+
import {
|
|
8
|
+
__require
|
|
9
|
+
} from "./chunk-8sqjbsgt.js";
|
|
10
|
+
|
|
11
|
+
// src/cli/commands/plugin-build.ts
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
async function pluginBuildCommand(projectRoot) {
|
|
15
|
+
const srcDir = path.resolve(projectRoot, "src");
|
|
16
|
+
const distDir = path.resolve(projectRoot, "dist");
|
|
17
|
+
if (!fs.existsSync(srcDir)) {
|
|
18
|
+
logger.error(`No 'src' directory found in ${projectRoot}.`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
logger.info("Syncing plugin UI...");
|
|
22
|
+
await pluginSyncCommand(projectRoot, false);
|
|
23
|
+
const entryFiles = ["index.tsx", "index.ts", "plugin.tsx", "plugin.ts"];
|
|
24
|
+
let mainEntry = "";
|
|
25
|
+
for (const file of entryFiles) {
|
|
26
|
+
const fullPath = path.join(srcDir, file);
|
|
27
|
+
if (fs.existsSync(fullPath)) {
|
|
28
|
+
mainEntry = fullPath;
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (!mainEntry) {
|
|
33
|
+
logger.error(`Could not find a main entry file in ${srcDir}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
logger.info(`Building plugin entry: ${path.basename(mainEntry)}...`);
|
|
37
|
+
if (!fs.existsSync(distDir)) {
|
|
38
|
+
fs.mkdirSync(distDir, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
const buildResult = await Bun.build({
|
|
41
|
+
entrypoints: [mainEntry],
|
|
42
|
+
outdir: distDir,
|
|
43
|
+
target: "node",
|
|
44
|
+
format: "esm",
|
|
45
|
+
external: [
|
|
46
|
+
"opacacms",
|
|
47
|
+
"opacacms/types",
|
|
48
|
+
"opacacms/plugins",
|
|
49
|
+
"opacacms/schema",
|
|
50
|
+
"hono",
|
|
51
|
+
"zod",
|
|
52
|
+
"react",
|
|
53
|
+
"react-dom"
|
|
54
|
+
]
|
|
55
|
+
});
|
|
56
|
+
if (!buildResult.success) {
|
|
57
|
+
logger.error("Plugin build failed.");
|
|
58
|
+
for (const msg of buildResult.logs) {
|
|
59
|
+
console.error(msg);
|
|
60
|
+
}
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
logger.info("Generating type declarations...");
|
|
65
|
+
const { execSync } = await import("node:child_process");
|
|
66
|
+
execSync("bunx tsc --emitDeclarationOnly --outDir dist", {
|
|
67
|
+
cwd: projectRoot,
|
|
68
|
+
stdio: "ignore"
|
|
69
|
+
});
|
|
70
|
+
logger.success("Types generated.");
|
|
71
|
+
} catch (err) {
|
|
72
|
+
logger.warn("Failed to generate types. Make sure typescript is installed.");
|
|
73
|
+
}
|
|
74
|
+
logger.success(`Plugin built successfully in ${distDir}!`);
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
pluginBuildCommand
|
|
78
|
+
};
|
|
@@ -3,17 +3,18 @@ import {
|
|
|
3
3
|
flattenPayload,
|
|
4
4
|
pushSchema,
|
|
5
5
|
unflattenRow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-dykn5hr6.js";
|
|
7
7
|
import {
|
|
8
8
|
BaseDatabaseAdapter
|
|
9
9
|
} from "./chunk-s8mqwnm1.js";
|
|
10
|
-
import {
|
|
11
|
-
logger
|
|
12
|
-
} from "./chunk-62ev8gnc.js";
|
|
13
10
|
import {
|
|
14
11
|
flattenFields,
|
|
12
|
+
getRelationalFields,
|
|
15
13
|
toSnakeCase
|
|
16
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-qxt9vge8.js";
|
|
15
|
+
import {
|
|
16
|
+
logger
|
|
17
|
+
} from "./chunk-t0zg026p.js";
|
|
17
18
|
import {
|
|
18
19
|
__require
|
|
19
20
|
} from "./chunk-8sqjbsgt.js";
|
|
@@ -67,7 +68,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
67
68
|
if (!colDef)
|
|
68
69
|
return data;
|
|
69
70
|
const result = { ...data };
|
|
70
|
-
const { flattenFields: flattenFields2 } = await import("./chunk-
|
|
71
|
+
const { flattenFields: flattenFields2 } = await import("./chunk-526a3gqx.js");
|
|
71
72
|
const allFields = flattenFields2(colDef.fields);
|
|
72
73
|
for (const field of allFields) {
|
|
73
74
|
const colName = toSnakeCase(field.name);
|
|
@@ -139,12 +140,11 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
139
140
|
const dbFields = flattenFields(colDef?.fields || []);
|
|
140
141
|
const validCols = new Set(dbFields.map((f) => toSnakeCase(f.name)));
|
|
141
142
|
validCols.add("id");
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
143
|
+
validCols.add(toSnakeCase("createdAt"));
|
|
144
|
+
validCols.add(toSnakeCase("updatedAt"));
|
|
145
|
+
const now = new Date().toISOString();
|
|
146
|
+
flatData[toSnakeCase("createdAt")] = now;
|
|
147
|
+
flatData[toSnakeCase("updatedAt")] = now;
|
|
148
148
|
const filteredData = {};
|
|
149
149
|
for (const col of Object.keys(flatData)) {
|
|
150
150
|
if (validCols.has(col)) {
|
|
@@ -205,8 +205,8 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
205
205
|
const unflattened = unflattenRow(row);
|
|
206
206
|
const colDef = this._collections.find((c) => c.slug === collection);
|
|
207
207
|
if (colDef) {
|
|
208
|
-
const { getRelationalFields, toSnakeCase: toSnakeCase2 } = await import("./chunk-
|
|
209
|
-
const relationalFields =
|
|
208
|
+
const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await import("./chunk-526a3gqx.js");
|
|
209
|
+
const relationalFields = getRelationalFields2(colDef.fields);
|
|
210
210
|
for (const field of relationalFields) {
|
|
211
211
|
if (!field.name)
|
|
212
212
|
continue;
|
|
@@ -259,7 +259,87 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
259
259
|
qb = qb.orderBy("created_at", "desc");
|
|
260
260
|
}
|
|
261
261
|
const rows = await qb.execute();
|
|
262
|
-
|
|
262
|
+
if (rows.length === 0) {
|
|
263
|
+
const totalPages2 = Math.ceil(total / limit);
|
|
264
|
+
return {
|
|
265
|
+
docs: [],
|
|
266
|
+
totalDocs: total,
|
|
267
|
+
limit,
|
|
268
|
+
totalPages: totalPages2,
|
|
269
|
+
page,
|
|
270
|
+
pagingCounter: offset + 1,
|
|
271
|
+
hasNextPage: page * limit < total,
|
|
272
|
+
hasPrevPage: page > 1,
|
|
273
|
+
prevPage: page > 1 ? page - 1 : null,
|
|
274
|
+
nextPage: page < totalPages2 ? page + 1 : null
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const rowIds = rows.map((r) => r.id);
|
|
278
|
+
const docs = rows.map((r) => unflattenRow(r));
|
|
279
|
+
const colDef = this._collections.find((c) => c.slug === collection);
|
|
280
|
+
if (colDef) {
|
|
281
|
+
const relationalFields = getRelationalFields(colDef.fields);
|
|
282
|
+
for (const field of relationalFields) {
|
|
283
|
+
if (!field.name)
|
|
284
|
+
continue;
|
|
285
|
+
const snakeName = toSnakeCase(field.name);
|
|
286
|
+
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
287
|
+
const joinTableName = `${toSnakeCase(collection)}_${snakeName}_relations`.toLowerCase();
|
|
288
|
+
try {
|
|
289
|
+
const allRelations = await this._db.selectFrom(joinTableName).selectAll().where("source_id", "in", rowIds).orderBy("order", "asc").execute();
|
|
290
|
+
const relationsBySource = allRelations.reduce((acc, r) => {
|
|
291
|
+
if (!acc[r.source_id])
|
|
292
|
+
acc[r.source_id] = [];
|
|
293
|
+
acc[r.source_id].push(r.target_id);
|
|
294
|
+
return acc;
|
|
295
|
+
}, {});
|
|
296
|
+
const parts = field.name.split("__");
|
|
297
|
+
for (let docIdx = 0;docIdx < docs.length; docIdx++) {
|
|
298
|
+
const rowId = rowIds[docIdx];
|
|
299
|
+
let current = docs[docIdx];
|
|
300
|
+
for (let i = 0;i < parts.length - 1; i++) {
|
|
301
|
+
if (!current[parts[i]])
|
|
302
|
+
current[parts[i]] = {};
|
|
303
|
+
current = current[parts[i]];
|
|
304
|
+
}
|
|
305
|
+
current[parts[parts.length - 1]] = relationsBySource[rowId] || [];
|
|
306
|
+
}
|
|
307
|
+
} catch (e) {}
|
|
308
|
+
} else if (field.type === "blocks" && field.blocks) {
|
|
309
|
+
const blocksBySource = {};
|
|
310
|
+
for (const b of field.blocks) {
|
|
311
|
+
const blockTableName = `${collection}_${snakeName}_${toSnakeCase(b.slug)}`.toLowerCase();
|
|
312
|
+
try {
|
|
313
|
+
const allBlocks = await this._db.selectFrom(blockTableName).selectAll().where("_parent_id", "in", rowIds).execute();
|
|
314
|
+
for (const blk of allBlocks) {
|
|
315
|
+
const uf = unflattenRow(blk);
|
|
316
|
+
uf.blockType = blk.block_type;
|
|
317
|
+
if (!blocksBySource[blk._parent_id])
|
|
318
|
+
blocksBySource[blk._parent_id] = [];
|
|
319
|
+
blocksBySource[blk._parent_id].push(uf);
|
|
320
|
+
}
|
|
321
|
+
} catch (e) {}
|
|
322
|
+
}
|
|
323
|
+
const parts = field.name.split("__");
|
|
324
|
+
for (let docIdx = 0;docIdx < docs.length; docIdx++) {
|
|
325
|
+
const rowId = rowIds[docIdx];
|
|
326
|
+
const blockData = blocksBySource[rowId] || [];
|
|
327
|
+
blockData.sort((a, b) => a._order - b._order);
|
|
328
|
+
blockData.forEach((b) => {
|
|
329
|
+
delete b._order;
|
|
330
|
+
delete b._parentId;
|
|
331
|
+
});
|
|
332
|
+
let current = docs[docIdx];
|
|
333
|
+
for (let i = 0;i < parts.length - 1; i++) {
|
|
334
|
+
if (!current[parts[i]])
|
|
335
|
+
current[parts[i]] = {};
|
|
336
|
+
current = current[parts[i]];
|
|
337
|
+
}
|
|
338
|
+
current[parts[parts.length - 1]] = blockData;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
263
343
|
const totalPages = Math.ceil(total / limit);
|
|
264
344
|
return {
|
|
265
345
|
docs: docs.filter(Boolean),
|
|
@@ -464,21 +544,12 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
464
544
|
const existing = await this.findGlobal(slug);
|
|
465
545
|
const flatData = flattenPayload(data);
|
|
466
546
|
const globalDef = this._globals.find((g) => g.slug === slug);
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
const config = typeof globalDef?.timestamps === "object" ? globalDef.timestamps : {};
|
|
470
|
-
const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
|
|
471
|
-
flatData[updatedField] = new Date().toISOString();
|
|
472
|
-
}
|
|
547
|
+
const now = new Date().toISOString();
|
|
548
|
+
flatData[toSnakeCase("updatedAt")] = now;
|
|
473
549
|
if (!existing) {
|
|
474
550
|
if (!flatData.id)
|
|
475
551
|
flatData.id = "global";
|
|
476
|
-
|
|
477
|
-
const config = typeof globalDef?.timestamps === "object" ? globalDef.timestamps : {};
|
|
478
|
-
const createdField = toSnakeCase(config.createdAt || "createdAt");
|
|
479
|
-
if (!flatData[createdField])
|
|
480
|
-
flatData[createdField] = flatData[toSnakeCase(config.updatedAt || "updatedAt")];
|
|
481
|
-
}
|
|
552
|
+
flatData[toSnakeCase("createdAt")] = now;
|
|
482
553
|
await this._db.insertInto(tableName).values(flatData).execute();
|
|
483
554
|
} else {
|
|
484
555
|
await this._db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
|