opacacms 0.1.21 → 0.2.1
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 +792 -50
- package/dist/admin/auth-client.d.ts +39 -39
- package/dist/admin/index.js +2360 -1392
- 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.js +8 -0
- package/dist/admin/webcomponent.js +20 -2
- package/dist/admin.css +1 -1
- package/dist/auth/index.d.ts +101 -41
- package/dist/{chunk-fqastxq9.js → chunk-06ks4ggh.js} +133 -44
- package/dist/{chunk-xrfhhz85.js → chunk-2es275xs.js} +480 -85
- 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-0sdceeys.js → chunk-6d1vdfwa.js} +121 -31
- package/dist/{chunk-wmvjvn7b.js → chunk-6qq3ne6b.js} +39 -1
- package/dist/{chunk-0am1m47g.js → chunk-6v1fw7q7.js} +5 -5
- package/dist/{chunk-t9v845m2.js → chunk-7y1nbmw6.js} +34 -3
- package/dist/chunk-8scgdznr.js +44 -0
- package/dist/{chunk-mycmsjd9.js → chunk-b3kr8w41.js} +57 -6
- package/dist/chunk-bexcv7xe.js +36 -0
- package/dist/{chunk-ekxkvqjm.js → chunk-bygjkgrx.js} +124 -34
- package/dist/{chunk-16vgcf3k.js → chunk-byq8g0rd.js} +1 -1
- package/dist/{chunk-cpw2y3pn.js → chunk-dykn5hr6.js} +7 -7
- package/dist/chunk-fj19qccp.js +78 -0
- package/dist/{chunk-n1xraw7j.js → chunk-g1jb60xd.js} +1 -1
- package/dist/{chunk-xa7rjsn2.js → chunk-j53pz21t.js} +2 -2
- package/dist/{chunk-nb7ctdg8.js → chunk-jdfw4v3r.js} +1 -1
- package/dist/chunk-mkn49zmy.js +102 -0
- package/dist/{chunk-59sg3pw9.js → chunk-n133qpsm.js} +128 -34
- 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-t0zg026p.js +71 -0
- package/dist/{chunk-61kwqve4.js → chunk-tfnaf41w.js} +118 -37
- 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/adapter.d.ts +2 -2
- package/dist/db/better-sqlite.d.ts +2 -1
- package/dist/db/better-sqlite.js +5 -5
- package/dist/db/bun-sqlite.d.ts +2 -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 +3 -3
- package/dist/db/postgres.js +5 -5
- package/dist/db/sqlite.d.ts +2 -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 -26
- 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 -7
- package/dist/schema/index.js +22 -6
- 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/storage/index.js +34 -25
- package/dist/types.d.ts +224 -17
- package/dist/utils/logger.d.ts +13 -35
- package/dist/validation.d.ts +40 -0
- package/dist/validator.d.ts +1 -1
- package/package.json +41 -27
- 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
|
@@ -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";
|
|
@@ -21,15 +22,12 @@ import {
|
|
|
21
22
|
// src/db/better-sqlite.ts
|
|
22
23
|
import fs from "node:fs/promises";
|
|
23
24
|
import path from "node:path";
|
|
24
|
-
import { createRequire } from "node:module";
|
|
25
25
|
import { CompiledQuery, FileMigrationProvider, Kysely, Migrator, SqliteDialect } from "kysely";
|
|
26
|
-
var require2 = createRequire(import.meta.url);
|
|
27
|
-
var Database = require2("better-sqlite3");
|
|
28
|
-
|
|
29
26
|
class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
27
|
+
path;
|
|
30
28
|
name = "better-sqlite3";
|
|
31
29
|
_rawDb;
|
|
32
|
-
_db;
|
|
30
|
+
_db = null;
|
|
33
31
|
_collections = [];
|
|
34
32
|
_globals = [];
|
|
35
33
|
push;
|
|
@@ -39,27 +37,39 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
39
37
|
return this._rawDb;
|
|
40
38
|
}
|
|
41
39
|
get db() {
|
|
40
|
+
if (!this._db)
|
|
41
|
+
throw new Error("Database not connected. Call connect() first.");
|
|
42
42
|
return this._db;
|
|
43
43
|
}
|
|
44
44
|
constructor(path2, options) {
|
|
45
45
|
super();
|
|
46
|
-
this.
|
|
46
|
+
this.path = path2;
|
|
47
|
+
this.push = options?.push ?? true;
|
|
48
|
+
this.pushDestructive = options?.pushDestructive ?? false;
|
|
49
|
+
this.migrationDir = options?.migrationDir ?? "./migrations";
|
|
50
|
+
}
|
|
51
|
+
async connect() {
|
|
52
|
+
if (this._db)
|
|
53
|
+
return;
|
|
54
|
+
const { createRequire } = await import("node:module");
|
|
55
|
+
const require2 = createRequire(import.meta.url);
|
|
56
|
+
const Database = require2("better-sqlite3");
|
|
57
|
+
this._rawDb = new Database(this.path);
|
|
47
58
|
this._db = new Kysely({
|
|
48
59
|
dialect: new SqliteDialect({
|
|
49
60
|
database: this._rawDb
|
|
50
61
|
})
|
|
51
62
|
});
|
|
52
|
-
this.push = options?.push ?? true;
|
|
53
|
-
this.pushDestructive = options?.pushDestructive ?? false;
|
|
54
|
-
this.migrationDir = options?.migrationDir ?? "./migrations";
|
|
55
63
|
}
|
|
56
|
-
async connect() {}
|
|
57
64
|
async disconnect() {
|
|
58
|
-
|
|
65
|
+
if (this._db)
|
|
66
|
+
await this.db.destroy();
|
|
59
67
|
}
|
|
60
68
|
async unsafe(query, params) {
|
|
69
|
+
if (!this._db)
|
|
70
|
+
await this.connect();
|
|
61
71
|
const compiled = CompiledQuery.raw(query, params || []);
|
|
62
|
-
const result = await this.
|
|
72
|
+
const result = await this.db.executeQuery(compiled);
|
|
63
73
|
return result.rows;
|
|
64
74
|
}
|
|
65
75
|
async coerceData(collection, data) {
|
|
@@ -67,7 +77,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
67
77
|
if (!colDef)
|
|
68
78
|
return data;
|
|
69
79
|
const result = { ...data };
|
|
70
|
-
const { flattenFields: flattenFields2 } = await import("./chunk-
|
|
80
|
+
const { flattenFields: flattenFields2 } = await import("./chunk-526a3gqx.js");
|
|
71
81
|
const allFields = flattenFields2(colDef.fields);
|
|
72
82
|
for (const field of allFields) {
|
|
73
83
|
const colName = toSnakeCase(field.name);
|
|
@@ -103,7 +113,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
103
113
|
}
|
|
104
114
|
async count(collection, query) {
|
|
105
115
|
const tableName = toSnakeCase(collection);
|
|
106
|
-
let qb = this.
|
|
116
|
+
let qb = this.db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count"));
|
|
107
117
|
if (query && Object.keys(query).length > 0) {
|
|
108
118
|
qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
109
119
|
}
|
|
@@ -112,7 +122,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
112
122
|
}
|
|
113
123
|
async create(collection, data) {
|
|
114
124
|
const tableName = toSnakeCase(collection);
|
|
115
|
-
return this.
|
|
125
|
+
return this.db.transaction().execute(async (tx) => {
|
|
116
126
|
const colDef = this._collections.find((c) => c.slug === collection);
|
|
117
127
|
const jsonFields = colDef?.fields.filter((f) => ["richtext", "json", "file"].includes(f.type)).map((f) => f.name) || [];
|
|
118
128
|
const flatData = flattenPayload(data, "", jsonFields);
|
|
@@ -193,7 +203,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
193
203
|
}
|
|
194
204
|
async findOne(collection, query, tx) {
|
|
195
205
|
const tableName = toSnakeCase(collection);
|
|
196
|
-
const executor = tx || this.
|
|
206
|
+
const executor = tx || this.db;
|
|
197
207
|
let qb = executor.selectFrom(tableName).selectAll();
|
|
198
208
|
if (query && Object.keys(query).length > 0) {
|
|
199
209
|
qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
@@ -204,8 +214,8 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
204
214
|
const unflattened = unflattenRow(row);
|
|
205
215
|
const colDef = this._collections.find((c) => c.slug === collection);
|
|
206
216
|
if (colDef) {
|
|
207
|
-
const { getRelationalFields, toSnakeCase: toSnakeCase2 } = await import("./chunk-
|
|
208
|
-
const relationalFields =
|
|
217
|
+
const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await import("./chunk-526a3gqx.js");
|
|
218
|
+
const relationalFields = getRelationalFields2(colDef.fields);
|
|
209
219
|
for (const field of relationalFields) {
|
|
210
220
|
if (!field.name)
|
|
211
221
|
continue;
|
|
@@ -246,7 +256,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
246
256
|
const offset = (page - 1) * limit;
|
|
247
257
|
const total = await this.count(collection, query);
|
|
248
258
|
const tableName = toSnakeCase(collection);
|
|
249
|
-
let qb = this.
|
|
259
|
+
let qb = this.db.selectFrom(tableName).selectAll().limit(limit).offset(offset);
|
|
250
260
|
if (query && Object.keys(query).length > 0) {
|
|
251
261
|
qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
252
262
|
}
|
|
@@ -258,7 +268,87 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
258
268
|
qb = qb.orderBy("created_at", "desc");
|
|
259
269
|
}
|
|
260
270
|
const rows = await qb.execute();
|
|
261
|
-
|
|
271
|
+
if (rows.length === 0) {
|
|
272
|
+
const totalPages2 = Math.ceil(total / limit);
|
|
273
|
+
return {
|
|
274
|
+
docs: [],
|
|
275
|
+
totalDocs: total,
|
|
276
|
+
limit,
|
|
277
|
+
totalPages: totalPages2,
|
|
278
|
+
page,
|
|
279
|
+
pagingCounter: offset + 1,
|
|
280
|
+
hasNextPage: page * limit < total,
|
|
281
|
+
hasPrevPage: page > 1,
|
|
282
|
+
prevPage: page > 1 ? page - 1 : null,
|
|
283
|
+
nextPage: page < totalPages2 ? page + 1 : null
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
const rowIds = rows.map((r) => r.id);
|
|
287
|
+
const docs = rows.map((r) => unflattenRow(r));
|
|
288
|
+
const colDef = this._collections.find((c) => c.slug === collection);
|
|
289
|
+
if (colDef) {
|
|
290
|
+
const relationalFields = getRelationalFields(colDef.fields);
|
|
291
|
+
for (const field of relationalFields) {
|
|
292
|
+
if (!field.name)
|
|
293
|
+
continue;
|
|
294
|
+
const snakeName = toSnakeCase(field.name);
|
|
295
|
+
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
296
|
+
const joinTableName = `${toSnakeCase(collection)}_${snakeName}_relations`.toLowerCase();
|
|
297
|
+
try {
|
|
298
|
+
const allRelations = await this.db.selectFrom(joinTableName).selectAll().where("source_id", "in", rowIds).orderBy("order", "asc").execute();
|
|
299
|
+
const relationsBySource = allRelations.reduce((acc, r) => {
|
|
300
|
+
if (!acc[r.source_id])
|
|
301
|
+
acc[r.source_id] = [];
|
|
302
|
+
acc[r.source_id].push(r.target_id);
|
|
303
|
+
return acc;
|
|
304
|
+
}, {});
|
|
305
|
+
const parts = field.name.split("__");
|
|
306
|
+
for (let docIdx = 0;docIdx < docs.length; docIdx++) {
|
|
307
|
+
const rowId = rowIds[docIdx];
|
|
308
|
+
let current = docs[docIdx];
|
|
309
|
+
for (let i = 0;i < parts.length - 1; i++) {
|
|
310
|
+
if (!current[parts[i]])
|
|
311
|
+
current[parts[i]] = {};
|
|
312
|
+
current = current[parts[i]];
|
|
313
|
+
}
|
|
314
|
+
current[parts[parts.length - 1]] = relationsBySource[rowId] || [];
|
|
315
|
+
}
|
|
316
|
+
} catch (e) {}
|
|
317
|
+
} else if (field.type === "blocks" && field.blocks) {
|
|
318
|
+
const blocksBySource = {};
|
|
319
|
+
for (const b of field.blocks) {
|
|
320
|
+
const blockTableName = `${collection}_${snakeName}_${toSnakeCase(b.slug)}`.toLowerCase();
|
|
321
|
+
try {
|
|
322
|
+
const allBlocks = await this.db.selectFrom(blockTableName).selectAll().where("_parent_id", "in", rowIds).execute();
|
|
323
|
+
for (const blk of allBlocks) {
|
|
324
|
+
const uf = unflattenRow(blk);
|
|
325
|
+
uf.blockType = blk.block_type;
|
|
326
|
+
if (!blocksBySource[blk._parent_id])
|
|
327
|
+
blocksBySource[blk._parent_id] = [];
|
|
328
|
+
blocksBySource[blk._parent_id].push(uf);
|
|
329
|
+
}
|
|
330
|
+
} catch (e) {}
|
|
331
|
+
}
|
|
332
|
+
const parts = field.name.split("__");
|
|
333
|
+
for (let docIdx = 0;docIdx < docs.length; docIdx++) {
|
|
334
|
+
const rowId = rowIds[docIdx];
|
|
335
|
+
const blockData = blocksBySource[rowId] || [];
|
|
336
|
+
blockData.sort((a, b) => a._order - b._order);
|
|
337
|
+
blockData.forEach((b) => {
|
|
338
|
+
delete b._order;
|
|
339
|
+
delete b._parentId;
|
|
340
|
+
});
|
|
341
|
+
let current = docs[docIdx];
|
|
342
|
+
for (let i = 0;i < parts.length - 1; i++) {
|
|
343
|
+
if (!current[parts[i]])
|
|
344
|
+
current[parts[i]] = {};
|
|
345
|
+
current = current[parts[i]];
|
|
346
|
+
}
|
|
347
|
+
current[parts[parts.length - 1]] = blockData;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
262
352
|
const totalPages = Math.ceil(total / limit);
|
|
263
353
|
return {
|
|
264
354
|
docs: docs.filter(Boolean),
|
|
@@ -275,7 +365,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
275
365
|
}
|
|
276
366
|
async update(collection, query, data) {
|
|
277
367
|
const tableName = toSnakeCase(collection);
|
|
278
|
-
return this.
|
|
368
|
+
return this.db.transaction().execute(async (tx) => {
|
|
279
369
|
let normalizedQuery = query;
|
|
280
370
|
if (typeof query !== "object" || query === null) {
|
|
281
371
|
normalizedQuery = { id: query };
|
|
@@ -366,7 +456,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
366
456
|
const current = await this.findOne(collection, normalizedQuery);
|
|
367
457
|
if (!current)
|
|
368
458
|
return false;
|
|
369
|
-
await this.
|
|
459
|
+
await this.db.transaction().execute(async (tx) => {
|
|
370
460
|
const colDef = this._collections.find((c) => c.slug === collection);
|
|
371
461
|
if (colDef) {
|
|
372
462
|
for (const field of colDef.fields) {
|
|
@@ -392,7 +482,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
392
482
|
}
|
|
393
483
|
async updateMany(collection, query, data) {
|
|
394
484
|
const tableName = toSnakeCase(collection);
|
|
395
|
-
return this.
|
|
485
|
+
return this.db.transaction().execute(async (tx) => {
|
|
396
486
|
let qb = tx.updateTable(tableName);
|
|
397
487
|
if (query && Object.keys(query).length > 0) {
|
|
398
488
|
qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
@@ -421,7 +511,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
421
511
|
}
|
|
422
512
|
async deleteMany(collection, query) {
|
|
423
513
|
const tableName = toSnakeCase(collection);
|
|
424
|
-
return this.
|
|
514
|
+
return this.db.transaction().execute(async (tx) => {
|
|
425
515
|
let selectQb = tx.selectFrom(tableName).select("id");
|
|
426
516
|
if (query && Object.keys(query).length > 0) {
|
|
427
517
|
selectQb = selectQb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
|
|
@@ -455,7 +545,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
455
545
|
}
|
|
456
546
|
async findGlobal(slug) {
|
|
457
547
|
const tableName = toSnakeCase(slug);
|
|
458
|
-
const row = await this.
|
|
548
|
+
const row = await this.db.selectFrom(tableName).selectAll().limit(1).executeTakeFirst();
|
|
459
549
|
return row ? unflattenRow(row) : null;
|
|
460
550
|
}
|
|
461
551
|
async updateGlobal(slug, data) {
|
|
@@ -469,15 +559,15 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
469
559
|
if (!flatData.id)
|
|
470
560
|
flatData.id = "global";
|
|
471
561
|
flatData[toSnakeCase("createdAt")] = now;
|
|
472
|
-
await this.
|
|
562
|
+
await this.db.insertInto(tableName).values(flatData).execute();
|
|
473
563
|
} else {
|
|
474
|
-
await this.
|
|
564
|
+
await this.db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
|
|
475
565
|
}
|
|
476
566
|
return this.findGlobal(slug);
|
|
477
567
|
}
|
|
478
568
|
async runMigrations() {
|
|
479
569
|
const migrator = new Migrator({
|
|
480
|
-
db: this.
|
|
570
|
+
db: this.db,
|
|
481
571
|
provider: new FileMigrationProvider({
|
|
482
572
|
fs,
|
|
483
573
|
path,
|
|
@@ -500,7 +590,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
|
|
|
500
590
|
this._collections = collections;
|
|
501
591
|
this._globals = globals;
|
|
502
592
|
if (this.push) {
|
|
503
|
-
await pushSchema(this.
|
|
593
|
+
await pushSchema(this.db, "sqlite", collections, globals, {
|
|
504
594
|
pushDestructive: this.pushDestructive
|
|
505
595
|
});
|
|
506
596
|
}
|
|
@@ -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;
|
|
@@ -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
|
+
};
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// src/cli/commands/seed-command.ts
|
|
6
6
|
async function seedCommand(opaca, count, reset, type) {
|
|
7
|
-
const { autoSeed } = await import("./chunk-
|
|
7
|
+
const { autoSeed } = await import("./chunk-jdfw4v3r.js");
|
|
8
8
|
try {
|
|
9
9
|
await autoSeed(opaca, count, reset, type);
|
|
10
10
|
} catch (error) {
|
|
@@ -203,7 +203,7 @@ function sortCollections(collections) {
|
|
|
203
203
|
async function autoSeed(config, countPerCollection = 10, reset = false, type = "all") {
|
|
204
204
|
const { collections, db, globals, storages, serverURL } = config;
|
|
205
205
|
console.log(`\uD83C\uDF31 Starting automatic seed (${countPerCollection} records per collection)...`);
|
|
206
|
-
const { getSystemCollections } = await import("./chunk-
|
|
206
|
+
const { getSystemCollections } = await import("./chunk-3rdhbedb.js");
|
|
207
207
|
const systemCollections = getSystemCollections().filter((c) => c.slug === "_opaca_assets");
|
|
208
208
|
const allCollections = [...systemCollections, ...collections];
|
|
209
209
|
let collectionsToSeed = sortCollections(allCollections);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-t0zg026p.js";
|
|
4
|
+
import"./chunk-8sqjbsgt.js";
|
|
5
|
+
|
|
6
|
+
// src/cli/commands/plugin-init.ts
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
async function pluginInitCommand(pluginName) {
|
|
10
|
+
if (!pluginName) {
|
|
11
|
+
logger.error("Please provide a plugin name. Usage: opacacms plugin:init <plugin_name>");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const targetDir = path.resolve(process.cwd(), pluginName);
|
|
15
|
+
if (fs.existsSync(targetDir)) {
|
|
16
|
+
logger.error(`Directory ${targetDir} already exists.`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
logger.info(`Initializing new plugin in ${targetDir}...`);
|
|
20
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
21
|
+
fs.mkdirSync(path.join(targetDir, "src"), { recursive: true });
|
|
22
|
+
const packageJson = {
|
|
23
|
+
name: pluginName,
|
|
24
|
+
version: "1.0.0",
|
|
25
|
+
description: "An OpacaCMS plugin",
|
|
26
|
+
main: "dist/index.js",
|
|
27
|
+
types: "dist/index.d.ts",
|
|
28
|
+
scripts: {
|
|
29
|
+
build: "opacacms plugin:build",
|
|
30
|
+
dev: "bun run build --watch"
|
|
31
|
+
},
|
|
32
|
+
peerDependencies: {
|
|
33
|
+
opacacms: "workspace:*"
|
|
34
|
+
},
|
|
35
|
+
devDependencies: {
|
|
36
|
+
typescript: "^5.0.0",
|
|
37
|
+
"@types/node": "^20.0.0"
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
fs.writeFileSync(path.join(targetDir, "package.json"), JSON.stringify(packageJson, null, 2), "utf-8");
|
|
41
|
+
const tsconfig = {
|
|
42
|
+
compilerOptions: {
|
|
43
|
+
target: "ESNext",
|
|
44
|
+
module: "ESNext",
|
|
45
|
+
moduleResolution: "bundler",
|
|
46
|
+
strict: true,
|
|
47
|
+
esModuleInterop: true,
|
|
48
|
+
skipLibCheck: true,
|
|
49
|
+
forceConsistentCasingInFileNames: true,
|
|
50
|
+
outDir: "./dist",
|
|
51
|
+
declaration: true,
|
|
52
|
+
jsx: "react"
|
|
53
|
+
},
|
|
54
|
+
include: ["src/**/*"]
|
|
55
|
+
};
|
|
56
|
+
fs.writeFileSync(path.join(targetDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2), "utf-8");
|
|
57
|
+
const indexTsx = `import type { OpacaPlugin, OpacaPluginContext } from 'opacacms/types';
|
|
58
|
+
import { definePlugin } from 'opacacms/plugins';
|
|
59
|
+
|
|
60
|
+
export const myPlugin = (): OpacaPlugin => definePlugin({
|
|
61
|
+
name: '${pluginName}',
|
|
62
|
+
label: 'My Custom Plugin',
|
|
63
|
+
description: 'A great new plugin for OpacaCMS.',
|
|
64
|
+
version: '1.0.0',
|
|
65
|
+
icon: 'Box',
|
|
66
|
+
|
|
67
|
+
onInit: (ctx: OpacaPluginContext) => {
|
|
68
|
+
ctx.logger.info('[${pluginName}] Plugin initialized!');
|
|
69
|
+
return ctx.config;
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
adminUI: {
|
|
73
|
+
component: 'my-plugin-dashboard',
|
|
74
|
+
// The CLI will automatically compile dashboard.tsx into this 'source' field during build.
|
|
75
|
+
source: '',
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
`;
|
|
79
|
+
fs.writeFileSync(path.join(targetDir, "src", "index.tsx"), indexTsx, "utf-8");
|
|
80
|
+
const dashboardTsx = `class MyPluginDashboard extends HTMLElement {
|
|
81
|
+
connectedCallback() {
|
|
82
|
+
this.innerHTML = \`
|
|
83
|
+
<div style="padding: 2rem; font-family: sans-serif;">
|
|
84
|
+
<h2>Welcome to ${pluginName} Dashboard!</h2>
|
|
85
|
+
<p>This UI component is built as a self-contained Web Component.</p>
|
|
86
|
+
</div>
|
|
87
|
+
\`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
customElements.define('my-plugin-dashboard', MyPluginDashboard);
|
|
92
|
+
`;
|
|
93
|
+
fs.writeFileSync(path.join(targetDir, "src", "dashboard.tsx"), dashboardTsx, "utf-8");
|
|
94
|
+
logger.success(`Plugin ${pluginName} initialized successfully!`);
|
|
95
|
+
logger.info(`Next steps:
|
|
96
|
+
cd ${pluginName}
|
|
97
|
+
bun install
|
|
98
|
+
bun run build`);
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
pluginInitCommand
|
|
102
|
+
};
|