opacacms 0.1.21 → 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 +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 +2 -2
- package/dist/admin.css +1 -1
- package/dist/auth/index.d.ts +101 -41
- package/dist/{chunk-0sdceeys.js → chunk-0bq155dy.js} +86 -6
- package/dist/{chunk-59sg3pw9.js → chunk-0gtxnxmd.js} +90 -7
- 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-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-16vgcf3k.js → chunk-byq8g0rd.js} +1 -1
- package/dist/{chunk-fqastxq9.js → chunk-d1asgtke.js} +86 -6
- package/dist/{chunk-cpw2y3pn.js → chunk-dykn5hr6.js} +7 -7
- package/dist/{chunk-61kwqve4.js → chunk-esrg9qj0.js} +90 -9
- package/dist/chunk-fj19qccp.js +78 -0
- package/dist/{chunk-ekxkvqjm.js → chunk-gmee4mdc.js} +90 -9
- package/dist/{chunk-xa7rjsn2.js → chunk-j53pz21t.js} +2 -2
- package/dist/{chunk-xrfhhz85.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 -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/types.d.ts +222 -15
- 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 +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,7 +3,7 @@ import {
|
|
|
3
3
|
mapFieldToPostgresType,
|
|
4
4
|
mapFieldToSQLiteType,
|
|
5
5
|
toSnakeCase
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-qxt9vge8.js";
|
|
7
7
|
import"./chunk-8sqjbsgt.js";
|
|
8
8
|
|
|
9
9
|
// src/db/kysely/migration-generator.ts
|
|
@@ -60,6 +60,26 @@ function generateMigrationCode(collections, globals = [], dialect) {
|
|
|
60
60
|
`;
|
|
61
61
|
downCode = ` await db.schema.dropTable('${slug}').execute();
|
|
62
62
|
` + downCode;
|
|
63
|
+
if ("versions" in collection && collection.versions) {
|
|
64
|
+
const versionsTable = `${toSnakeCase(slug)}_versions`;
|
|
65
|
+
upCode += ` await db.schema.createTable('${versionsTable}')
|
|
66
|
+
`;
|
|
67
|
+
upCode += ` .addColumn('id', 'text', (col) => col.primaryKey())
|
|
68
|
+
`;
|
|
69
|
+
upCode += ` .addColumn('_parent_id', 'text', (col) => col.notNull())
|
|
70
|
+
`;
|
|
71
|
+
upCode += ` .addColumn('_version_data', '${dialect === "postgres" ? "jsonb" : "text"}', (col) => col.notNull())
|
|
72
|
+
`;
|
|
73
|
+
upCode += ` .addColumn('_status', 'text')
|
|
74
|
+
`;
|
|
75
|
+
upCode += ` .addColumn('created_at', '${tsType}', (col) => col.defaultTo(sql\`CURRENT_TIMESTAMP\`))
|
|
76
|
+
`;
|
|
77
|
+
upCode += ` .execute();
|
|
78
|
+
|
|
79
|
+
`;
|
|
80
|
+
downCode = ` await db.schema.dropTable('${versionsTable}').execute();
|
|
81
|
+
` + downCode;
|
|
82
|
+
}
|
|
63
83
|
for (const field of flattenedFields) {
|
|
64
84
|
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
65
85
|
const colName = toSnakeCase(field.name);
|
|
@@ -188,6 +208,24 @@ function generateSQLCode(collections, globals = []) {
|
|
|
188
208
|
);
|
|
189
209
|
|
|
190
210
|
`;
|
|
211
|
+
if ("versions" in collection && collection.versions) {
|
|
212
|
+
const versionsTable = `${toSnakeCase(slug)}_versions`;
|
|
213
|
+
sql += `CREATE TABLE IF NOT EXISTS "${versionsTable}" (
|
|
214
|
+
`;
|
|
215
|
+
sql += ` "id" TEXT PRIMARY KEY,
|
|
216
|
+
`;
|
|
217
|
+
sql += ` "_parent_id" TEXT NOT NULL,
|
|
218
|
+
`;
|
|
219
|
+
sql += ` "_version_data" TEXT NOT NULL,
|
|
220
|
+
`;
|
|
221
|
+
sql += ` "_status" TEXT,
|
|
222
|
+
`;
|
|
223
|
+
sql += ` "created_at" TEXT DEFAULT CURRENT_TIMESTAMP
|
|
224
|
+
`;
|
|
225
|
+
sql += `);
|
|
226
|
+
|
|
227
|
+
`;
|
|
228
|
+
}
|
|
191
229
|
for (const field of flattenedFields) {
|
|
192
230
|
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
193
231
|
const colName = toSnakeCase(field.name);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
logger
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-t0zg026p.js";
|
|
4
4
|
import {
|
|
5
5
|
__require
|
|
6
6
|
} from "./chunk-8sqjbsgt.js";
|
|
@@ -17,8 +17,8 @@ async function migrateCreateCommand(opaca, migrationName = "migration", outDir)
|
|
|
17
17
|
fs.mkdirSync(fullMigrationDir, { recursive: true });
|
|
18
18
|
}
|
|
19
19
|
const timestamp = new Date().toISOString().replace(/[-:T]/g, "").split(".")[0];
|
|
20
|
-
const { generateMigrationCode, generateSQLCode } = await import("./chunk-
|
|
21
|
-
const { getSystemCollections } = await import("./chunk-
|
|
20
|
+
const { generateMigrationCode, generateSQLCode } = await import("./chunk-6qq3ne6b.js");
|
|
21
|
+
const { getSystemCollections } = await import("./chunk-3rdhbedb.js");
|
|
22
22
|
let dialect = "sqlite";
|
|
23
23
|
if (db.name === "postgres")
|
|
24
24
|
dialect = "postgres";
|
|
@@ -104,9 +104,9 @@ async function migrateD1Command(migrationDir, isRemote, binding = "DB") {
|
|
|
104
104
|
isRemote ? "--remote" : "--local",
|
|
105
105
|
"--yes"
|
|
106
106
|
];
|
|
107
|
-
const result = spawnSync("
|
|
107
|
+
const result = spawnSync("bun", ["x", ...wranglerArgs], {
|
|
108
108
|
stdio: "inherit",
|
|
109
|
-
shell:
|
|
109
|
+
shell: false,
|
|
110
110
|
cwd: process.cwd()
|
|
111
111
|
});
|
|
112
112
|
if (result.status !== 0) {
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-t0zg026p.js";
|
|
4
|
+
|
|
1
5
|
// src/validation.ts
|
|
2
6
|
import { z } from "zod";
|
|
3
7
|
var FieldTypeSchema = z.enum([
|
|
@@ -20,7 +24,8 @@ var FieldTypeSchema = z.enum([
|
|
|
20
24
|
"tabs",
|
|
21
25
|
"join",
|
|
22
26
|
"array",
|
|
23
|
-
"virtual"
|
|
27
|
+
"virtual",
|
|
28
|
+
"ui"
|
|
24
29
|
]);
|
|
25
30
|
var BaseFieldSchema = z.object({
|
|
26
31
|
name: z.string(),
|
|
@@ -41,7 +46,8 @@ var BaseFieldSchema = z.object({
|
|
|
41
46
|
components: z.object({
|
|
42
47
|
Field: z.string().optional(),
|
|
43
48
|
Cell: z.string().optional()
|
|
44
|
-
}).optional()
|
|
49
|
+
}).optional(),
|
|
50
|
+
customProps: z.record(z.string(), z.any()).optional()
|
|
45
51
|
}).optional()
|
|
46
52
|
});
|
|
47
53
|
var FieldSchema = z.lazy(() => z.discriminatedUnion("type", [
|
|
@@ -154,6 +160,9 @@ var FieldSchema = z.lazy(() => z.discriminatedUnion("type", [
|
|
|
154
160
|
type: z.literal("virtual"),
|
|
155
161
|
resolve: z.function(),
|
|
156
162
|
returnType: z.string().optional()
|
|
163
|
+
}),
|
|
164
|
+
BaseFieldSchema.extend({
|
|
165
|
+
type: z.literal("ui")
|
|
157
166
|
})
|
|
158
167
|
]));
|
|
159
168
|
var AccessConfigSchema = z.object({
|
|
@@ -266,6 +275,11 @@ var OpacaAuthConfigSchema = z.object({
|
|
|
266
275
|
enabled: z.boolean(),
|
|
267
276
|
issuer: z.string()
|
|
268
277
|
}).optional()
|
|
278
|
+
}).optional(),
|
|
279
|
+
logger: z.object({
|
|
280
|
+
disabled: z.boolean().optional(),
|
|
281
|
+
disableColors: z.boolean().optional(),
|
|
282
|
+
level: z.enum(["debug", "info", "warn", "error"]).optional()
|
|
269
283
|
}).optional()
|
|
270
284
|
});
|
|
271
285
|
var ApiConfigSchema = z.object({
|
|
@@ -277,9 +291,19 @@ var ApiConfigSchema = z.object({
|
|
|
277
291
|
store: z.any().optional(),
|
|
278
292
|
provider: z.function().optional(),
|
|
279
293
|
keyGenerator: z.function().optional()
|
|
294
|
+
}).optional(),
|
|
295
|
+
openAPI: z.object({
|
|
296
|
+
enabled: z.boolean().optional().default(false),
|
|
297
|
+
path: z.string().optional().default("/reference"),
|
|
298
|
+
theme: z.string().optional(),
|
|
299
|
+
layout: z.string().optional(),
|
|
300
|
+
hideModels: z.boolean().optional(),
|
|
301
|
+
hideDownloadButton: z.boolean().optional(),
|
|
302
|
+
customCss: z.string().optional()
|
|
280
303
|
}).optional()
|
|
281
304
|
});
|
|
282
305
|
var OpacaConfigSchema = z.object({
|
|
306
|
+
plugins: z.array(z.any()).optional(),
|
|
283
307
|
collections: z.array(CollectionSchema),
|
|
284
308
|
globals: z.array(GlobalSchema).optional(),
|
|
285
309
|
db: z.any(),
|
|
@@ -321,7 +345,14 @@ function recursivelyBuild(obj) {
|
|
|
321
345
|
return built;
|
|
322
346
|
}
|
|
323
347
|
function defineConfig(config) {
|
|
324
|
-
|
|
348
|
+
let builtConfig = recursivelyBuild(config);
|
|
349
|
+
if (builtConfig.plugins && Array.isArray(builtConfig.plugins)) {
|
|
350
|
+
for (const plugin of builtConfig.plugins) {
|
|
351
|
+
if (plugin.onInit) {
|
|
352
|
+
builtConfig = plugin.onInit({ config: builtConfig, logger }) || builtConfig;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
325
356
|
const result = OpacaConfigSchema.safeParse(builtConfig);
|
|
326
357
|
if (!result.success) {
|
|
327
358
|
throw new Error(`Invalid OpacaCMS Configuration: ${result.error.message}`);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/admin/stores/ui.ts
|
|
2
|
+
import { persistentAtom } from "@nanostores/persistent";
|
|
3
|
+
import { atom } from "nanostores";
|
|
4
|
+
var $toasts = atom([]);
|
|
5
|
+
var $isSidebarCollapsed = persistentAtom("opaca-sidebar-collapsed", false, {
|
|
6
|
+
encode: JSON.stringify,
|
|
7
|
+
decode: JSON.parse
|
|
8
|
+
});
|
|
9
|
+
function toggleSidebar() {
|
|
10
|
+
$isSidebarCollapsed.set(!$isSidebarCollapsed.get());
|
|
11
|
+
}
|
|
12
|
+
function notify(message, type = "success") {
|
|
13
|
+
const id = Math.random().toString(36).substring(2, 9);
|
|
14
|
+
$toasts.set([...$toasts.get(), { id, message, type }]);
|
|
15
|
+
}
|
|
16
|
+
function clearToast(id) {
|
|
17
|
+
$toasts.set($toasts.get().filter((t) => t.id !== id));
|
|
18
|
+
}
|
|
19
|
+
var $customSidebarItems = atom([]);
|
|
20
|
+
function registerSidebarItem(item) {
|
|
21
|
+
const current = $customSidebarItems.get();
|
|
22
|
+
if (!current.find((i) => i.path === item.path)) {
|
|
23
|
+
if (item.render && !item.component) {
|
|
24
|
+
item.component = `opaca-plugin-render-${Math.random().toString(36).substring(2, 9)}`;
|
|
25
|
+
if (typeof customElements !== "undefined" && !customElements.get(item.component)) {
|
|
26
|
+
const componentName = item.component;
|
|
27
|
+
const renderFn = item.render;
|
|
28
|
+
|
|
29
|
+
class OpacaGenericPlugin extends HTMLElement {
|
|
30
|
+
connectedCallback() {
|
|
31
|
+
const serverUrl = this.getAttribute("server-url") || "";
|
|
32
|
+
const config = JSON.parse(this.getAttribute("config") || "{}");
|
|
33
|
+
const user = JSON.parse(this.getAttribute("user") || "{}");
|
|
34
|
+
this.innerHTML = renderFn(serverUrl, config, user);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
customElements.define(componentName, OpacaGenericPlugin);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
$customSidebarItems.set([...current, item]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { $toasts, $isSidebarCollapsed, toggleSidebar, notify, clearToast, $customSidebarItems, registerSidebarItem };
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSystemCollections,
|
|
3
3
|
init_system_schema
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-v9z61v3g.js";
|
|
5
|
+
import {
|
|
6
|
+
OpacaLogger
|
|
7
|
+
} from "./chunk-t0zg026p.js";
|
|
5
8
|
|
|
6
9
|
// src/auth/index.ts
|
|
7
10
|
import { apiKey } from "@better-auth/api-key";
|
|
8
11
|
import { betterAuth } from "better-auth";
|
|
9
|
-
import { admin } from "better-auth/plugins";
|
|
12
|
+
import { admin, openAPI } from "better-auth/plugins";
|
|
10
13
|
import { CamelCasePlugin } from "kysely";
|
|
11
14
|
|
|
12
15
|
// src/auth/premissions.ts
|
|
@@ -81,6 +84,10 @@ async function createAuth(config) {
|
|
|
81
84
|
ac,
|
|
82
85
|
roles
|
|
83
86
|
}),
|
|
87
|
+
openAPI({
|
|
88
|
+
disableDefaultReference: true,
|
|
89
|
+
path: "/open-api"
|
|
90
|
+
}),
|
|
84
91
|
...userAuth.features?.apiKeys?.enabled ? [
|
|
85
92
|
apiKey({
|
|
86
93
|
defaultPrefix: "opaca_",
|
|
@@ -158,19 +165,50 @@ async function createAuth(config) {
|
|
|
158
165
|
}
|
|
159
166
|
}
|
|
160
167
|
},
|
|
161
|
-
plugins
|
|
168
|
+
plugins,
|
|
169
|
+
logger: {
|
|
170
|
+
disabled: config.logger?.disabled,
|
|
171
|
+
disableColors: config.logger?.disableColors,
|
|
172
|
+
level: config.logger?.level,
|
|
173
|
+
log: (level, message, ...args) => {
|
|
174
|
+
const rebrand = (msg) => {
|
|
175
|
+
if (typeof msg !== "string")
|
|
176
|
+
return String(msg);
|
|
177
|
+
return msg.replace(/\[better-auth\]\s*/g, "").replace(/BETTER_AUTH_SECRET/g, "OPACA_SECRET").replace(/`npx auth secret`/g, "`openssl rand -base64 32`").replace(/npx auth secret/g, "openssl rand -base64 32").replace(/^Warning:\s*/i, "");
|
|
178
|
+
};
|
|
179
|
+
const branded = rebrand(message);
|
|
180
|
+
const brandedArgs = args.map((a) => typeof a === "string" ? rebrand(a) : a);
|
|
181
|
+
const authLogger = new OpacaLogger(config.logger);
|
|
182
|
+
switch (level) {
|
|
183
|
+
case "debug":
|
|
184
|
+
authLogger.debug(branded, ...brandedArgs);
|
|
185
|
+
break;
|
|
186
|
+
case "info":
|
|
187
|
+
authLogger.info(branded, ...brandedArgs);
|
|
188
|
+
break;
|
|
189
|
+
case "warn":
|
|
190
|
+
authLogger.warn(branded, ...brandedArgs);
|
|
191
|
+
break;
|
|
192
|
+
case "error":
|
|
193
|
+
authLogger.error(branded, ...brandedArgs);
|
|
194
|
+
break;
|
|
195
|
+
default:
|
|
196
|
+
authLogger.info(branded, ...brandedArgs);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
162
200
|
});
|
|
163
201
|
}
|
|
164
202
|
|
|
165
203
|
// src/config-utils.ts
|
|
166
204
|
init_system_schema();
|
|
167
|
-
function sanitizeConfig(config) {
|
|
205
|
+
function sanitizeConfig(config, settings = {}) {
|
|
168
206
|
const collections = [...config.collections];
|
|
169
207
|
const supportsAuth = ["sqlite", "postgres", "d1", "bun-sqlite", "better-sqlite3"].includes(config.db.name);
|
|
170
208
|
const systemCollections = getSystemCollections();
|
|
171
209
|
for (const systemCol of systemCollections) {
|
|
172
210
|
const isAsset = systemCol.slug === "_opaca_assets";
|
|
173
|
-
const isAuth = ["_users", "_sessions", "_accounts", "_verifications", "_api_keys"].includes(systemCol.slug);
|
|
211
|
+
const isAuth = ["_users", "_sessions", "_accounts", "_verifications", "_api_keys", "_opaca_plugin_settings"].includes(systemCol.slug);
|
|
174
212
|
if (isAsset && config.storages || isAuth && supportsAuth) {
|
|
175
213
|
if (!collections.find((col) => col.slug === systemCol.slug)) {
|
|
176
214
|
collections.push({
|
|
@@ -235,7 +273,20 @@ function sanitizeConfig(config) {
|
|
|
235
273
|
acc[key] = {};
|
|
236
274
|
return acc;
|
|
237
275
|
}, {}) : {},
|
|
238
|
-
i18n: config.i18n
|
|
276
|
+
i18n: config.i18n,
|
|
277
|
+
plugins: config.plugins?.map((p) => ({
|
|
278
|
+
name: p.name,
|
|
279
|
+
label: p.label,
|
|
280
|
+
description: p.description,
|
|
281
|
+
version: p.version,
|
|
282
|
+
author: p.author,
|
|
283
|
+
homepage: p.homepage,
|
|
284
|
+
icon: p.icon,
|
|
285
|
+
adminAssets: p.adminAssets ? p.adminAssets() : undefined,
|
|
286
|
+
adminUI: p.adminUI,
|
|
287
|
+
settings: settings[p.name] || {},
|
|
288
|
+
configSchema: p.configSchema ? p.configSchema.map(sanitizeField) : undefined
|
|
289
|
+
}))
|
|
239
290
|
};
|
|
240
291
|
}
|
|
241
292
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineCustomField
|
|
3
|
+
} from "./chunk-rqyjjqgy.js";
|
|
4
|
+
|
|
5
|
+
// src/admin/react.tsx
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { createRoot } from "react-dom/client";
|
|
8
|
+
function defineReactField(tagName, Component) {
|
|
9
|
+
defineCustomField(tagName, {
|
|
10
|
+
mount: (container, props) => {
|
|
11
|
+
const el = container;
|
|
12
|
+
const root = createRoot(el);
|
|
13
|
+
el._opacaReactRoot = root;
|
|
14
|
+
root.render(React.createElement(Component, props));
|
|
15
|
+
},
|
|
16
|
+
update: (container, props) => {
|
|
17
|
+
const el = container;
|
|
18
|
+
const root = el._opacaReactRoot;
|
|
19
|
+
if (root) {
|
|
20
|
+
root.render(React.createElement(Component, props));
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
unmount: (container) => {
|
|
24
|
+
const el = container;
|
|
25
|
+
const root = el._opacaReactRoot;
|
|
26
|
+
if (root) {
|
|
27
|
+
setTimeout(() => {
|
|
28
|
+
root.unmount();
|
|
29
|
+
delete el._opacaReactRoot;
|
|
30
|
+
}, 0);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { defineReactField };
|
|
@@ -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";
|
|
@@ -264,7 +264,87 @@ class D1Adapter extends BaseDatabaseAdapter {
|
|
|
264
264
|
qb = qb.orderBy("created_at", "desc");
|
|
265
265
|
}
|
|
266
266
|
const rows = await qb.execute();
|
|
267
|
-
|
|
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
|
+
}
|
|
268
348
|
const totalPages = Math.ceil(total / limit);
|
|
269
349
|
return {
|
|
270
350
|
docs: docs.filter(Boolean),
|
|
@@ -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;
|
|
@@ -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";
|
|
@@ -184,8 +185,8 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
184
185
|
const unflattened = unflattenRow(row);
|
|
185
186
|
const colDef = this._collections.find((c) => c.slug === collection);
|
|
186
187
|
if (colDef) {
|
|
187
|
-
const { getRelationalFields, toSnakeCase: toSnakeCase2 } = await import("./chunk-
|
|
188
|
-
const relationalFields =
|
|
188
|
+
const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await import("./chunk-526a3gqx.js");
|
|
189
|
+
const relationalFields = getRelationalFields2(colDef.fields);
|
|
189
190
|
for (const field of relationalFields) {
|
|
190
191
|
if (!field.name)
|
|
191
192
|
continue;
|
|
@@ -255,7 +256,87 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
255
256
|
qb = qb.orderBy("created_at", "desc");
|
|
256
257
|
}
|
|
257
258
|
const rows = await qb.execute();
|
|
258
|
-
|
|
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
|
+
}
|
|
259
340
|
const totalPages = Math.ceil(total / limit);
|
|
260
341
|
return {
|
|
261
342
|
docs: docs.filter(Boolean),
|
|
@@ -487,7 +568,7 @@ class PostgresAdapter extends BaseDatabaseAdapter {
|
|
|
487
568
|
}
|
|
488
569
|
}
|
|
489
570
|
async migrate(collections, globals = []) {
|
|
490
|
-
const { getSystemCollections } = await import("./chunk-
|
|
571
|
+
const { getSystemCollections } = await import("./chunk-3rdhbedb.js");
|
|
491
572
|
this._collections = [...getSystemCollections(), ...collections];
|
|
492
573
|
this._globals = globals;
|
|
493
574
|
if (this.push && this._db) {
|