@sonicjs-cms/core 2.8.3 → 2.10.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/dist/{app-DnQ26Lho.d.cts → app-Ozl9agJG.d.cts} +1 -1
- package/dist/{app-DnQ26Lho.d.ts → app-Ozl9agJG.d.ts} +1 -1
- package/dist/{chunk-YFJJU26H.js → chunk-27AOVQTR.js} +10 -2
- package/dist/chunk-27AOVQTR.js.map +1 -0
- package/dist/{chunk-Y3VMEGY2.js → chunk-4TTMQQC7.js} +4 -4
- package/dist/{chunk-Y3VMEGY2.js.map → chunk-4TTMQQC7.js.map} +1 -1
- package/dist/{chunk-VNLR35GO.cjs → chunk-64APW3DW.cjs} +339 -2
- package/dist/chunk-64APW3DW.cjs.map +1 -0
- package/dist/{chunk-GTFMI24U.js → chunk-6O3RJV3C.js} +2 -2
- package/dist/{chunk-GTFMI24U.js.map → chunk-6O3RJV3C.js.map} +1 -1
- package/dist/{chunk-G44QUVNM.js → chunk-7JMMLHPQ.js} +337 -4
- package/dist/chunk-7JMMLHPQ.js.map +1 -0
- package/dist/chunk-CJYFSKH7.js +54 -54
- package/dist/chunk-CJYFSKH7.js.map +1 -1
- package/dist/{chunk-JDIM5AG7.cjs → chunk-EKPLKUZT.cjs} +11 -5
- package/dist/chunk-EKPLKUZT.cjs.map +1 -0
- package/dist/{chunk-MPT5PA6U.cjs → chunk-IIBRG5S5.cjs} +10 -2
- package/dist/chunk-IIBRG5S5.cjs.map +1 -0
- package/dist/{chunk-K4Q4SFJJ.cjs → chunk-IT2TC4ZD.cjs} +7 -7
- package/dist/{chunk-K4Q4SFJJ.cjs.map → chunk-IT2TC4ZD.cjs.map} +1 -1
- package/dist/{chunk-5XAI2XUF.js → chunk-IZWNIUJI.js} +11 -5
- package/dist/chunk-IZWNIUJI.js.map +1 -0
- package/dist/{chunk-CH5UHZVM.js → chunk-JTNUM7JE.js} +1218 -442
- package/dist/chunk-JTNUM7JE.js.map +1 -0
- package/dist/chunk-MNFY6DWY.cjs +54 -54
- package/dist/chunk-MNFY6DWY.cjs.map +1 -1
- package/dist/{chunk-R4WR3VTN.cjs → chunk-RCA6R6VE.cjs} +1329 -553
- package/dist/chunk-RCA6R6VE.cjs.map +1 -0
- package/dist/{chunk-HXHVU5GM.cjs → chunk-ZMVWMJ3S.cjs} +2 -2
- package/dist/{chunk-HXHVU5GM.cjs.map → chunk-ZMVWMJ3S.cjs.map} +1 -1
- package/dist/{collection-config-i8EaAF7z.d.cts → collection-config-B4PG-AaF.d.cts} +4 -2
- package/dist/{collection-config-i8EaAF7z.d.ts → collection-config-B4PG-AaF.d.ts} +4 -2
- package/dist/{filter-bar.template-Daw8ZDoq.d.cts → filter-bar.template-DlVYMk-T.d.cts} +1 -1
- package/dist/{filter-bar.template-Daw8ZDoq.d.ts → filter-bar.template-DlVYMk-T.d.ts} +1 -1
- package/dist/index.cjs +142 -141
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +29 -29
- package/dist/middleware.d.cts +1 -1
- package/dist/middleware.d.ts +1 -1
- package/dist/middleware.js +3 -3
- package/dist/migrations-N2C2VPJU.js +4 -0
- package/dist/{migrations-KHWFJ2HN.js.map → migrations-N2C2VPJU.js.map} +1 -1
- package/dist/migrations-ONIAY6GK.cjs +13 -0
- package/dist/{migrations-7X4RPH7O.cjs.map → migrations-ONIAY6GK.cjs.map} +1 -1
- package/dist/{plugin-zvZpaiP5.d.cts → plugin-0Xogrln-.d.cts} +1 -1
- package/dist/{plugin-zvZpaiP5.d.ts → plugin-0Xogrln-.d.ts} +1 -1
- package/dist/{plugin-bootstrap-CJozpgmI.d.cts → plugin-bootstrap-WmpvYM5w.d.ts} +2 -2
- package/dist/{plugin-bootstrap-DU5VmuHZ.d.ts → plugin-bootstrap-fpG98Otb.d.cts} +2 -2
- package/dist/{plugin-manager-Baa6xXqB.d.ts → plugin-manager-Clf2gXwj.d.ts} +2 -2
- package/dist/{plugin-manager-vBal9Zip.d.cts → plugin-manager-GcIeb226.d.cts} +2 -2
- package/dist/plugins.d.cts +2 -2
- package/dist/plugins.d.ts +2 -2
- package/dist/routes.cjs +29 -29
- package/dist/routes.d.cts +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/routes.js +6 -6
- package/dist/services.cjs +44 -28
- package/dist/services.d.cts +29 -4
- package/dist/services.d.ts +29 -4
- package/dist/services.js +3 -3
- package/dist/{telemetry-UiD1i9GS.d.cts → telemetry-B9vIV4wh.d.cts} +1 -1
- package/dist/{telemetry-UiD1i9GS.d.ts → telemetry-B9vIV4wh.d.ts} +1 -1
- package/dist/templates.d.cts +1 -1
- package/dist/templates.d.ts +1 -1
- package/dist/types.d.cts +3 -3
- package/dist/types.d.ts +3 -3
- package/dist/utils.cjs +11 -11
- package/dist/utils.d.cts +3 -3
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +1 -1
- package/dist/{version-C_CXrN_T.d.cts → version-ChpccWQ1.d.cts} +1 -1
- package/dist/{version-C_CXrN_T.d.ts → version-ChpccWQ1.d.ts} +1 -1
- package/package.json +9 -3
- package/dist/chunk-5XAI2XUF.js.map +0 -1
- package/dist/chunk-CH5UHZVM.js.map +0 -1
- package/dist/chunk-G44QUVNM.js.map +0 -1
- package/dist/chunk-JDIM5AG7.cjs.map +0 -1
- package/dist/chunk-MPT5PA6U.cjs.map +0 -1
- package/dist/chunk-R4WR3VTN.cjs.map +0 -1
- package/dist/chunk-VNLR35GO.cjs.map +0 -1
- package/dist/chunk-YFJJU26H.js.map +0 -1
- package/dist/migrations-7X4RPH7O.cjs +0 -13
- package/dist/migrations-KHWFJ2HN.js +0 -4
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
3
|
+
var chunk64APW3DW_cjs = require('./chunk-64APW3DW.cjs');
|
|
4
|
+
var chunkIT2TC4ZD_cjs = require('./chunk-IT2TC4ZD.cjs');
|
|
5
|
+
var chunkIIBRG5S5_cjs = require('./chunk-IIBRG5S5.cjs');
|
|
6
|
+
var chunkZMVWMJ3S_cjs = require('./chunk-ZMVWMJ3S.cjs');
|
|
7
7
|
var chunkLTKV7AE5_cjs = require('./chunk-LTKV7AE5.cjs');
|
|
8
8
|
var chunk6FHNRRJ3_cjs = require('./chunk-6FHNRRJ3.cjs');
|
|
9
|
-
var
|
|
9
|
+
var chunkEKPLKUZT_cjs = require('./chunk-EKPLKUZT.cjs');
|
|
10
10
|
var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
|
|
11
11
|
var chunkMNWKYY5E_cjs = require('./chunk-MNWKYY5E.cjs');
|
|
12
12
|
var hono = require('hono');
|
|
@@ -121,7 +121,7 @@ apiContentCrudRoutes.get("/:id", async (c) => {
|
|
|
121
121
|
}, 500);
|
|
122
122
|
}
|
|
123
123
|
});
|
|
124
|
-
apiContentCrudRoutes.post("/",
|
|
124
|
+
apiContentCrudRoutes.post("/", chunkIT2TC4ZD_cjs.requireAuth(), chunkIT2TC4ZD_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
|
|
125
125
|
try {
|
|
126
126
|
const db = c.env.DB;
|
|
127
127
|
const user = c.get("user");
|
|
@@ -162,7 +162,7 @@ apiContentCrudRoutes.post("/", chunkK4Q4SFJJ_cjs.requireAuth(), async (c) => {
|
|
|
162
162
|
now,
|
|
163
163
|
now
|
|
164
164
|
).run();
|
|
165
|
-
const cache =
|
|
165
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
|
|
166
166
|
await cache.invalidate(`content:list:${collectionId}:*`);
|
|
167
167
|
await cache.invalidate("content-filtered:*");
|
|
168
168
|
const getStmt = db.prepare("SELECT * FROM content WHERE id = ?");
|
|
@@ -187,7 +187,7 @@ apiContentCrudRoutes.post("/", chunkK4Q4SFJJ_cjs.requireAuth(), async (c) => {
|
|
|
187
187
|
}, 500);
|
|
188
188
|
}
|
|
189
189
|
});
|
|
190
|
-
apiContentCrudRoutes.put("/:id",
|
|
190
|
+
apiContentCrudRoutes.put("/:id", chunkIT2TC4ZD_cjs.requireAuth(), chunkIT2TC4ZD_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
|
|
191
191
|
try {
|
|
192
192
|
const id = c.req.param("id");
|
|
193
193
|
const db = c.env.DB;
|
|
@@ -225,7 +225,7 @@ apiContentCrudRoutes.put("/:id", chunkK4Q4SFJJ_cjs.requireAuth(), async (c) => {
|
|
|
225
225
|
WHERE id = ?
|
|
226
226
|
`);
|
|
227
227
|
await updateStmt.bind(...params).run();
|
|
228
|
-
const cache =
|
|
228
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
|
|
229
229
|
await cache.delete(cache.generateKey("content", id));
|
|
230
230
|
await cache.invalidate(`content:list:${existing.collection_id}:*`);
|
|
231
231
|
await cache.invalidate("content-filtered:*");
|
|
@@ -251,7 +251,7 @@ apiContentCrudRoutes.put("/:id", chunkK4Q4SFJJ_cjs.requireAuth(), async (c) => {
|
|
|
251
251
|
}, 500);
|
|
252
252
|
}
|
|
253
253
|
});
|
|
254
|
-
apiContentCrudRoutes.delete("/:id",
|
|
254
|
+
apiContentCrudRoutes.delete("/:id", chunkIT2TC4ZD_cjs.requireAuth(), chunkIT2TC4ZD_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
|
|
255
255
|
try {
|
|
256
256
|
const id = c.req.param("id");
|
|
257
257
|
const db = c.env.DB;
|
|
@@ -262,7 +262,7 @@ apiContentCrudRoutes.delete("/:id", chunkK4Q4SFJJ_cjs.requireAuth(), async (c) =
|
|
|
262
262
|
}
|
|
263
263
|
const deleteStmt = db.prepare("DELETE FROM content WHERE id = ?");
|
|
264
264
|
await deleteStmt.bind(id).run();
|
|
265
|
-
const cache =
|
|
265
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
|
|
266
266
|
await cache.delete(cache.generateKey("content", id));
|
|
267
267
|
await cache.invalidate(`content:list:${existing.collection_id}:*`);
|
|
268
268
|
await cache.invalidate("content-filtered:*");
|
|
@@ -287,7 +287,7 @@ apiRoutes.use("*", async (c, next) => {
|
|
|
287
287
|
c.header("X-Response-Time", `${totalTime}ms`);
|
|
288
288
|
});
|
|
289
289
|
apiRoutes.use("*", async (c, next) => {
|
|
290
|
-
const cacheEnabled = await
|
|
290
|
+
const cacheEnabled = await chunkIT2TC4ZD_cjs.isPluginActive(c.env.DB, "core-cache");
|
|
291
291
|
c.set("cacheEnabled", cacheEnabled);
|
|
292
292
|
await next();
|
|
293
293
|
});
|
|
@@ -724,7 +724,7 @@ apiRoutes.get("/collections", async (c) => {
|
|
|
724
724
|
try {
|
|
725
725
|
const db = c.env.DB;
|
|
726
726
|
const cacheEnabled = c.get("cacheEnabled");
|
|
727
|
-
const cache =
|
|
727
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
|
|
728
728
|
const cacheKey = cache.generateKey("collections", "all");
|
|
729
729
|
if (cacheEnabled) {
|
|
730
730
|
const cacheResult = await cache.getWithSource(cacheKey);
|
|
@@ -778,7 +778,7 @@ apiRoutes.get("/collections", async (c) => {
|
|
|
778
778
|
return c.json({ error: "Failed to fetch collections" }, 500);
|
|
779
779
|
}
|
|
780
780
|
});
|
|
781
|
-
apiRoutes.get("/content",
|
|
781
|
+
apiRoutes.get("/content", chunkIT2TC4ZD_cjs.optionalAuth(), async (c) => {
|
|
782
782
|
const executionStart = Date.now();
|
|
783
783
|
try {
|
|
784
784
|
const db = c.env.DB;
|
|
@@ -801,13 +801,13 @@ apiRoutes.get("/content", chunkK4Q4SFJJ_cjs.optionalAuth(), async (c) => {
|
|
|
801
801
|
});
|
|
802
802
|
}
|
|
803
803
|
}
|
|
804
|
-
const filter =
|
|
804
|
+
const filter = chunkEKPLKUZT_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
|
|
805
805
|
const normalizedFilter = normalizePublicContentFilter(filter, c.get("user")?.role);
|
|
806
806
|
if (!normalizedFilter.limit) {
|
|
807
807
|
normalizedFilter.limit = 50;
|
|
808
808
|
}
|
|
809
809
|
normalizedFilter.limit = Math.min(normalizedFilter.limit, 1e3);
|
|
810
|
-
const builder3 = new
|
|
810
|
+
const builder3 = new chunkEKPLKUZT_cjs.QueryFilterBuilder();
|
|
811
811
|
const queryResult = builder3.build("content", normalizedFilter);
|
|
812
812
|
if (queryResult.errors.length > 0) {
|
|
813
813
|
return c.json({
|
|
@@ -816,7 +816,7 @@ apiRoutes.get("/content", chunkK4Q4SFJJ_cjs.optionalAuth(), async (c) => {
|
|
|
816
816
|
}, 400);
|
|
817
817
|
}
|
|
818
818
|
const cacheEnabled = c.get("cacheEnabled");
|
|
819
|
-
const cache =
|
|
819
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
|
|
820
820
|
const cacheKey = cache.generateKey("content-filtered", JSON.stringify({ filter: normalizedFilter, query: queryResult.sql }));
|
|
821
821
|
if (cacheEnabled) {
|
|
822
822
|
const cacheResult = await cache.getWithSource(cacheKey);
|
|
@@ -879,7 +879,7 @@ apiRoutes.get("/content", chunkK4Q4SFJJ_cjs.optionalAuth(), async (c) => {
|
|
|
879
879
|
}, 500);
|
|
880
880
|
}
|
|
881
881
|
});
|
|
882
|
-
apiRoutes.get("/collections/:collection/content",
|
|
882
|
+
apiRoutes.get("/collections/:collection/content", chunkIT2TC4ZD_cjs.optionalAuth(), async (c) => {
|
|
883
883
|
const executionStart = Date.now();
|
|
884
884
|
try {
|
|
885
885
|
const collection = c.req.param("collection");
|
|
@@ -890,7 +890,7 @@ apiRoutes.get("/collections/:collection/content", chunkK4Q4SFJJ_cjs.optionalAuth
|
|
|
890
890
|
if (!collectionResult) {
|
|
891
891
|
return c.json({ error: "Collection not found" }, 404);
|
|
892
892
|
}
|
|
893
|
-
const filter =
|
|
893
|
+
const filter = chunkEKPLKUZT_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
|
|
894
894
|
const normalizedFilter = normalizePublicContentFilter(filter, c.get("user")?.role);
|
|
895
895
|
if (!normalizedFilter.where) {
|
|
896
896
|
normalizedFilter.where = { and: [] };
|
|
@@ -907,7 +907,7 @@ apiRoutes.get("/collections/:collection/content", chunkK4Q4SFJJ_cjs.optionalAuth
|
|
|
907
907
|
normalizedFilter.limit = 50;
|
|
908
908
|
}
|
|
909
909
|
normalizedFilter.limit = Math.min(normalizedFilter.limit, 1e3);
|
|
910
|
-
const builder3 = new
|
|
910
|
+
const builder3 = new chunkEKPLKUZT_cjs.QueryFilterBuilder();
|
|
911
911
|
const queryResult = builder3.build("content", normalizedFilter);
|
|
912
912
|
if (queryResult.errors.length > 0) {
|
|
913
913
|
return c.json({
|
|
@@ -916,7 +916,7 @@ apiRoutes.get("/collections/:collection/content", chunkK4Q4SFJJ_cjs.optionalAuth
|
|
|
916
916
|
}, 400);
|
|
917
917
|
}
|
|
918
918
|
const cacheEnabled = c.get("cacheEnabled");
|
|
919
|
-
const cache =
|
|
919
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
|
|
920
920
|
const cacheKey = cache.generateKey("collection-content-filtered", `${collection}:${JSON.stringify({ filter: normalizedFilter, query: queryResult.sql })}`);
|
|
921
921
|
if (cacheEnabled) {
|
|
922
922
|
const cacheResult = await cache.getWithSource(cacheKey);
|
|
@@ -1028,7 +1028,7 @@ var fileValidationSchema = zod.z.object({
|
|
|
1028
1028
|
// 50MB max
|
|
1029
1029
|
});
|
|
1030
1030
|
var apiMediaRoutes = new hono.Hono();
|
|
1031
|
-
apiMediaRoutes.use("*",
|
|
1031
|
+
apiMediaRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
1032
1032
|
apiMediaRoutes.post("/upload", async (c) => {
|
|
1033
1033
|
try {
|
|
1034
1034
|
const user = c.get("user");
|
|
@@ -1772,8 +1772,8 @@ apiSystemRoutes.get("/env", (c) => {
|
|
|
1772
1772
|
});
|
|
1773
1773
|
var api_system_default = apiSystemRoutes;
|
|
1774
1774
|
var adminApiRoutes = new hono.Hono();
|
|
1775
|
-
adminApiRoutes.use("*",
|
|
1776
|
-
adminApiRoutes.use("*",
|
|
1775
|
+
adminApiRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
1776
|
+
adminApiRoutes.use("*", chunkIT2TC4ZD_cjs.requireRole(["admin", "editor"]));
|
|
1777
1777
|
adminApiRoutes.get("/stats", async (c) => {
|
|
1778
1778
|
try {
|
|
1779
1779
|
const db = c.env.DB;
|
|
@@ -2283,7 +2283,7 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
|
|
|
2283
2283
|
});
|
|
2284
2284
|
adminApiRoutes.get("/migrations/status", async (c) => {
|
|
2285
2285
|
try {
|
|
2286
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
2286
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-ONIAY6GK.cjs');
|
|
2287
2287
|
const db = c.env.DB;
|
|
2288
2288
|
const migrationService = new MigrationService2(db);
|
|
2289
2289
|
const status = await migrationService.getMigrationStatus();
|
|
@@ -2308,7 +2308,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
2308
2308
|
error: "Unauthorized. Admin access required."
|
|
2309
2309
|
}, 403);
|
|
2310
2310
|
}
|
|
2311
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
2311
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-ONIAY6GK.cjs');
|
|
2312
2312
|
const db = c.env.DB;
|
|
2313
2313
|
const migrationService = new MigrationService2(db);
|
|
2314
2314
|
const result = await migrationService.runPendingMigrations();
|
|
@@ -2327,7 +2327,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
2327
2327
|
});
|
|
2328
2328
|
adminApiRoutes.get("/migrations/validate", async (c) => {
|
|
2329
2329
|
try {
|
|
2330
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
2330
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-ONIAY6GK.cjs');
|
|
2331
2331
|
const db = c.env.DB;
|
|
2332
2332
|
const migrationService = new MigrationService2(db);
|
|
2333
2333
|
const validation = await migrationService.validateSchema();
|
|
@@ -2738,7 +2738,7 @@ var JWT_SECRET_FALLBACK = "your-super-secret-jwt-key-change-in-production";
|
|
|
2738
2738
|
async function setCsrfCookie(c) {
|
|
2739
2739
|
const secret = c.env?.JWT_SECRET || JWT_SECRET_FALLBACK;
|
|
2740
2740
|
const isDev = c.env?.ENVIRONMENT === "development" || !c.env?.ENVIRONMENT;
|
|
2741
|
-
const csrfToken = await
|
|
2741
|
+
const csrfToken = await chunkIT2TC4ZD_cjs.generateCsrfToken(secret);
|
|
2742
2742
|
cookie.setCookie(c, "csrf_token", csrfToken, {
|
|
2743
2743
|
httpOnly: false,
|
|
2744
2744
|
secure: !isDev,
|
|
@@ -2795,7 +2795,7 @@ var loginSchema = zod.z.object({
|
|
|
2795
2795
|
});
|
|
2796
2796
|
authRoutes.post(
|
|
2797
2797
|
"/register",
|
|
2798
|
-
|
|
2798
|
+
chunkIT2TC4ZD_cjs.rateLimit({ max: 3, windowMs: 60 * 1e3, keyPrefix: "register" }),
|
|
2799
2799
|
async (c) => {
|
|
2800
2800
|
try {
|
|
2801
2801
|
const db = c.env.DB;
|
|
@@ -2832,7 +2832,7 @@ authRoutes.post(
|
|
|
2832
2832
|
if (existingUser) {
|
|
2833
2833
|
return c.json({ error: "User with this email or username already exists" }, 400);
|
|
2834
2834
|
}
|
|
2835
|
-
const passwordHash = await
|
|
2835
|
+
const passwordHash = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword(password);
|
|
2836
2836
|
const userId = crypto.randomUUID();
|
|
2837
2837
|
const now = /* @__PURE__ */ new Date();
|
|
2838
2838
|
await db.prepare(`
|
|
@@ -2852,7 +2852,7 @@ authRoutes.post(
|
|
|
2852
2852
|
now.getTime(),
|
|
2853
2853
|
now.getTime()
|
|
2854
2854
|
).run();
|
|
2855
|
-
const token = await
|
|
2855
|
+
const token = await chunkIT2TC4ZD_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer", c.env.JWT_SECRET);
|
|
2856
2856
|
cookie.setCookie(c, "auth_token", token, {
|
|
2857
2857
|
httpOnly: true,
|
|
2858
2858
|
secure: true,
|
|
@@ -2886,7 +2886,7 @@ authRoutes.post(
|
|
|
2886
2886
|
);
|
|
2887
2887
|
authRoutes.post(
|
|
2888
2888
|
"/login",
|
|
2889
|
-
|
|
2889
|
+
chunkIT2TC4ZD_cjs.rateLimit({ max: 5, windowMs: 60 * 1e3, keyPrefix: "login" }),
|
|
2890
2890
|
async (c) => {
|
|
2891
2891
|
try {
|
|
2892
2892
|
const body = await c.req.json();
|
|
@@ -2897,7 +2897,7 @@ authRoutes.post(
|
|
|
2897
2897
|
const { email, password } = validation.data;
|
|
2898
2898
|
const db = c.env.DB;
|
|
2899
2899
|
const normalizedEmail = email.toLowerCase();
|
|
2900
|
-
const cache =
|
|
2900
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.user);
|
|
2901
2901
|
let user = await cache.get(cache.generateKey("user", `email:${normalizedEmail}`));
|
|
2902
2902
|
if (!user) {
|
|
2903
2903
|
user = await db.prepare("SELECT * FROM users WHERE email = ? AND is_active = 1").bind(normalizedEmail).first();
|
|
@@ -2909,19 +2909,19 @@ authRoutes.post(
|
|
|
2909
2909
|
if (!user) {
|
|
2910
2910
|
return c.json({ error: "Invalid email or password" }, 401);
|
|
2911
2911
|
}
|
|
2912
|
-
const isValidPassword = await
|
|
2912
|
+
const isValidPassword = await chunkIT2TC4ZD_cjs.AuthManager.verifyPassword(password, user.password_hash);
|
|
2913
2913
|
if (!isValidPassword) {
|
|
2914
2914
|
return c.json({ error: "Invalid email or password" }, 401);
|
|
2915
2915
|
}
|
|
2916
|
-
if (
|
|
2916
|
+
if (chunkIT2TC4ZD_cjs.AuthManager.isLegacyHash(user.password_hash)) {
|
|
2917
2917
|
try {
|
|
2918
|
-
const newHash = await
|
|
2918
|
+
const newHash = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword(password);
|
|
2919
2919
|
await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(newHash, Date.now(), user.id).run();
|
|
2920
2920
|
} catch (rehashError) {
|
|
2921
2921
|
console.error("Password rehash failed (non-fatal):", rehashError);
|
|
2922
2922
|
}
|
|
2923
2923
|
}
|
|
2924
|
-
const token = await
|
|
2924
|
+
const token = await chunkIT2TC4ZD_cjs.AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET);
|
|
2925
2925
|
cookie.setCookie(c, "auth_token", token, {
|
|
2926
2926
|
httpOnly: true,
|
|
2927
2927
|
secure: true,
|
|
@@ -2974,7 +2974,7 @@ authRoutes.get("/logout", (c) => {
|
|
|
2974
2974
|
clearCsrfCookie(c);
|
|
2975
2975
|
return c.redirect("/auth/login?message=You have been logged out successfully");
|
|
2976
2976
|
});
|
|
2977
|
-
authRoutes.get("/me",
|
|
2977
|
+
authRoutes.get("/me", chunkIT2TC4ZD_cjs.requireAuth(), async (c) => {
|
|
2978
2978
|
try {
|
|
2979
2979
|
const user = c.get("user");
|
|
2980
2980
|
if (!user) {
|
|
@@ -2991,13 +2991,13 @@ authRoutes.get("/me", chunkK4Q4SFJJ_cjs.requireAuth(), async (c) => {
|
|
|
2991
2991
|
return c.json({ error: "Failed to get user" }, 500);
|
|
2992
2992
|
}
|
|
2993
2993
|
});
|
|
2994
|
-
authRoutes.post("/refresh",
|
|
2994
|
+
authRoutes.post("/refresh", chunkIT2TC4ZD_cjs.requireAuth(), async (c) => {
|
|
2995
2995
|
try {
|
|
2996
2996
|
const user = c.get("user");
|
|
2997
2997
|
if (!user) {
|
|
2998
2998
|
return c.json({ error: "Not authenticated" }, 401);
|
|
2999
2999
|
}
|
|
3000
|
-
const token = await
|
|
3000
|
+
const token = await chunkIT2TC4ZD_cjs.AuthManager.generateToken(user.userId, user.email, user.role, c.env.JWT_SECRET);
|
|
3001
3001
|
cookie.setCookie(c, "auth_token", token, {
|
|
3002
3002
|
httpOnly: true,
|
|
3003
3003
|
secure: true,
|
|
@@ -3014,7 +3014,7 @@ authRoutes.post("/refresh", chunkK4Q4SFJJ_cjs.requireAuth(), async (c) => {
|
|
|
3014
3014
|
});
|
|
3015
3015
|
authRoutes.post(
|
|
3016
3016
|
"/register/form",
|
|
3017
|
-
|
|
3017
|
+
chunkIT2TC4ZD_cjs.rateLimit({ max: 3, windowMs: 60 * 1e3, keyPrefix: "register" }),
|
|
3018
3018
|
async (c) => {
|
|
3019
3019
|
try {
|
|
3020
3020
|
const db = c.env.DB;
|
|
@@ -3061,7 +3061,7 @@ authRoutes.post(
|
|
|
3061
3061
|
</div>
|
|
3062
3062
|
`);
|
|
3063
3063
|
}
|
|
3064
|
-
const passwordHash = await
|
|
3064
|
+
const passwordHash = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword(password);
|
|
3065
3065
|
const role = isFirstUser ? "admin" : "viewer";
|
|
3066
3066
|
const userId = crypto.randomUUID();
|
|
3067
3067
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -3081,7 +3081,7 @@ authRoutes.post(
|
|
|
3081
3081
|
now.getTime(),
|
|
3082
3082
|
now.getTime()
|
|
3083
3083
|
).run();
|
|
3084
|
-
const token = await
|
|
3084
|
+
const token = await chunkIT2TC4ZD_cjs.AuthManager.generateToken(userId, normalizedEmail, role, c.env.JWT_SECRET);
|
|
3085
3085
|
cookie.setCookie(c, "auth_token", token, {
|
|
3086
3086
|
httpOnly: true,
|
|
3087
3087
|
secure: false,
|
|
@@ -3114,7 +3114,7 @@ authRoutes.post(
|
|
|
3114
3114
|
);
|
|
3115
3115
|
authRoutes.post(
|
|
3116
3116
|
"/login/form",
|
|
3117
|
-
|
|
3117
|
+
chunkIT2TC4ZD_cjs.rateLimit({ max: 5, windowMs: 60 * 1e3, keyPrefix: "login" }),
|
|
3118
3118
|
async (c) => {
|
|
3119
3119
|
try {
|
|
3120
3120
|
const formData = await c.req.formData();
|
|
@@ -3138,7 +3138,7 @@ authRoutes.post(
|
|
|
3138
3138
|
</div>
|
|
3139
3139
|
`);
|
|
3140
3140
|
}
|
|
3141
|
-
const isValidPassword = await
|
|
3141
|
+
const isValidPassword = await chunkIT2TC4ZD_cjs.AuthManager.verifyPassword(password, user.password_hash);
|
|
3142
3142
|
if (!isValidPassword) {
|
|
3143
3143
|
return c.html(html.html`
|
|
3144
3144
|
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
|
@@ -3146,15 +3146,15 @@ authRoutes.post(
|
|
|
3146
3146
|
</div>
|
|
3147
3147
|
`);
|
|
3148
3148
|
}
|
|
3149
|
-
if (
|
|
3149
|
+
if (chunkIT2TC4ZD_cjs.AuthManager.isLegacyHash(user.password_hash)) {
|
|
3150
3150
|
try {
|
|
3151
|
-
const newHash = await
|
|
3151
|
+
const newHash = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword(password);
|
|
3152
3152
|
await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(newHash, Date.now(), user.id).run();
|
|
3153
3153
|
} catch (rehashError) {
|
|
3154
3154
|
console.error("Password rehash failed (non-fatal):", rehashError);
|
|
3155
3155
|
}
|
|
3156
3156
|
}
|
|
3157
|
-
const token = await
|
|
3157
|
+
const token = await chunkIT2TC4ZD_cjs.AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET);
|
|
3158
3158
|
cookie.setCookie(c, "auth_token", token, {
|
|
3159
3159
|
httpOnly: true,
|
|
3160
3160
|
secure: false,
|
|
@@ -3196,7 +3196,7 @@ authRoutes.post(
|
|
|
3196
3196
|
);
|
|
3197
3197
|
authRoutes.post(
|
|
3198
3198
|
"/seed-admin",
|
|
3199
|
-
|
|
3199
|
+
chunkIT2TC4ZD_cjs.rateLimit({ max: 2, windowMs: 60 * 1e3, keyPrefix: "seed-admin" }),
|
|
3200
3200
|
async (c) => {
|
|
3201
3201
|
try {
|
|
3202
3202
|
const db = c.env.DB;
|
|
@@ -3218,7 +3218,7 @@ authRoutes.post(
|
|
|
3218
3218
|
`).run();
|
|
3219
3219
|
const existingAdmin = await db.prepare("SELECT id FROM users WHERE email = ? OR username = ?").bind("admin@sonicjs.com", "admin").first();
|
|
3220
3220
|
if (existingAdmin) {
|
|
3221
|
-
const passwordHash2 = await
|
|
3221
|
+
const passwordHash2 = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword("sonicjs!");
|
|
3222
3222
|
await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(passwordHash2, Date.now(), existingAdmin.id).run();
|
|
3223
3223
|
return c.json({
|
|
3224
3224
|
message: "Admin user already exists (password updated)",
|
|
@@ -3230,7 +3230,7 @@ authRoutes.post(
|
|
|
3230
3230
|
}
|
|
3231
3231
|
});
|
|
3232
3232
|
}
|
|
3233
|
-
const passwordHash = await
|
|
3233
|
+
const passwordHash = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword("sonicjs!");
|
|
3234
3234
|
const userId = "admin-user-id";
|
|
3235
3235
|
const now = Date.now();
|
|
3236
3236
|
const adminEmail = "admin@sonicjs.com".toLowerCase();
|
|
@@ -3451,7 +3451,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
3451
3451
|
if (existingUsername) {
|
|
3452
3452
|
return c.json({ error: "Username is already taken" }, 400);
|
|
3453
3453
|
}
|
|
3454
|
-
const passwordHash = await
|
|
3454
|
+
const passwordHash = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword(password);
|
|
3455
3455
|
const updateStmt = db.prepare(`
|
|
3456
3456
|
UPDATE users SET
|
|
3457
3457
|
username = ?,
|
|
@@ -3470,7 +3470,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
3470
3470
|
Date.now(),
|
|
3471
3471
|
invitedUser.id
|
|
3472
3472
|
).run();
|
|
3473
|
-
const authToken = await
|
|
3473
|
+
const authToken = await chunkIT2TC4ZD_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role, c.env.JWT_SECRET);
|
|
3474
3474
|
cookie.setCookie(c, "auth_token", authToken, {
|
|
3475
3475
|
httpOnly: true,
|
|
3476
3476
|
secure: true,
|
|
@@ -3487,7 +3487,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
3487
3487
|
});
|
|
3488
3488
|
authRoutes.post(
|
|
3489
3489
|
"/request-password-reset",
|
|
3490
|
-
|
|
3490
|
+
chunkIT2TC4ZD_cjs.rateLimit({ max: 3, windowMs: 15 * 60 * 1e3, keyPrefix: "password-reset" }),
|
|
3491
3491
|
async (c) => {
|
|
3492
3492
|
try {
|
|
3493
3493
|
const formData = await c.req.formData();
|
|
@@ -3705,7 +3705,7 @@ authRoutes.post("/reset-password", async (c) => {
|
|
|
3705
3705
|
if (Date.now() > user.password_reset_expires) {
|
|
3706
3706
|
return c.json({ error: "Reset token has expired" }, 400);
|
|
3707
3707
|
}
|
|
3708
|
-
const newPasswordHash = await
|
|
3708
|
+
const newPasswordHash = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword(password);
|
|
3709
3709
|
try {
|
|
3710
3710
|
const historyStmt = db.prepare(`
|
|
3711
3711
|
INSERT INTO password_history (id, user_id, password_hash, created_at)
|
|
@@ -4286,7 +4286,7 @@ function getMDXEditorInitScript(config) {
|
|
|
4286
4286
|
const toolbar = config?.toolbar || "full";
|
|
4287
4287
|
const placeholder = config?.placeholder || "Start writing your content...";
|
|
4288
4288
|
return `
|
|
4289
|
-
// Initialize EasyMDE (Markdown Editor) for
|
|
4289
|
+
// Initialize EasyMDE (Markdown Editor) only for markdown-marked fields
|
|
4290
4290
|
function initializeMDXEditor() {
|
|
4291
4291
|
if (typeof EasyMDE === 'undefined') {
|
|
4292
4292
|
console.warn('EasyMDE not loaded yet, retrying...');
|
|
@@ -4295,7 +4295,7 @@ function getMDXEditorInitScript(config) {
|
|
|
4295
4295
|
}
|
|
4296
4296
|
|
|
4297
4297
|
// Find all textareas that need EasyMDE
|
|
4298
|
-
document.querySelectorAll('.richtext-container textarea').forEach((textarea) => {
|
|
4298
|
+
document.querySelectorAll('.richtext-container[data-editor-provider="easymde"] textarea').forEach((textarea) => {
|
|
4299
4299
|
// Skip if already initialized
|
|
4300
4300
|
if (textarea.dataset.mdxeditorInitialized === 'true') {
|
|
4301
4301
|
return;
|
|
@@ -4394,11 +4394,11 @@ function getTinyMCEInitScript(config) {
|
|
|
4394
4394
|
const contentCss = skin.includes("dark") ? "dark" : "default";
|
|
4395
4395
|
const defaultHeight = config?.defaultHeight || 300;
|
|
4396
4396
|
return `
|
|
4397
|
-
// Initialize TinyMCE for
|
|
4397
|
+
// Initialize TinyMCE only for TinyMCE-backed richtext fields
|
|
4398
4398
|
function initializeTinyMCE() {
|
|
4399
4399
|
if (typeof tinymce !== 'undefined') {
|
|
4400
4400
|
// Find all textareas that need TinyMCE
|
|
4401
|
-
document.querySelectorAll('.richtext-container textarea').forEach((textarea) => {
|
|
4401
|
+
document.querySelectorAll('.richtext-container[data-editor-provider="tinymce"] textarea').forEach((textarea) => {
|
|
4402
4402
|
// Skip if already initialized
|
|
4403
4403
|
if (tinymce.get(textarea.id)) {
|
|
4404
4404
|
return;
|
|
@@ -4777,6 +4777,39 @@ function getReadFieldValueScript() {
|
|
|
4777
4777
|
window.__sonicReadFieldValueInit = true;
|
|
4778
4778
|
|
|
4779
4779
|
window.sonicReadFieldValue = function(fieldWrapper) {
|
|
4780
|
+
const getDirectChild = (parent, selector) => {
|
|
4781
|
+
if (!(parent instanceof Element)) return null;
|
|
4782
|
+
return Array.from(parent.children).find(
|
|
4783
|
+
(child) => child instanceof Element && child.matches(selector),
|
|
4784
|
+
) || null;
|
|
4785
|
+
};
|
|
4786
|
+
const getDirectStructuredSubfields = (host) =>
|
|
4787
|
+
Array.from(host.children).filter(
|
|
4788
|
+
(child) => child instanceof Element && child.classList.contains('structured-subfield'),
|
|
4789
|
+
);
|
|
4790
|
+
const getStructuredObjectFieldsHost = (container) => {
|
|
4791
|
+
const directFieldsHost = getDirectChild(container, '[data-structured-object-fields]');
|
|
4792
|
+
if (directFieldsHost) return directFieldsHost;
|
|
4793
|
+
const groupContent = getDirectChild(container, '.field-group-content');
|
|
4794
|
+
const nestedFieldsHost = groupContent
|
|
4795
|
+
? getDirectChild(groupContent, '[data-structured-object-fields]')
|
|
4796
|
+
: null;
|
|
4797
|
+
if (nestedFieldsHost) return nestedFieldsHost;
|
|
4798
|
+
return getDirectChild(container, '[data-array-item-fields]') || container;
|
|
4799
|
+
};
|
|
4800
|
+
const getDirectStructuredObject = (fieldWrapper) => {
|
|
4801
|
+
const directObject = getDirectChild(fieldWrapper, '[data-structured-object]');
|
|
4802
|
+
if (directObject) return directObject;
|
|
4803
|
+
const formGroup = getDirectChild(fieldWrapper, '.form-group');
|
|
4804
|
+
return formGroup ? getDirectChild(formGroup, '[data-structured-object]') : null;
|
|
4805
|
+
};
|
|
4806
|
+
const getDirectStructuredArray = (fieldWrapper) => {
|
|
4807
|
+
const directArray = getDirectChild(fieldWrapper, '[data-structured-array]');
|
|
4808
|
+
if (directArray) return directArray;
|
|
4809
|
+
const formGroup = getDirectChild(fieldWrapper, '.form-group');
|
|
4810
|
+
return formGroup ? getDirectChild(formGroup, '[data-structured-array]') : null;
|
|
4811
|
+
};
|
|
4812
|
+
|
|
4780
4813
|
const fieldType = fieldWrapper.dataset.fieldType;
|
|
4781
4814
|
const select = fieldWrapper.querySelector('select');
|
|
4782
4815
|
const textarea = fieldWrapper.querySelector('textarea');
|
|
@@ -4786,7 +4819,47 @@ function getReadFieldValueScript() {
|
|
|
4786
4819
|
const nonHiddenInput = inputs.find((input) => input.type !== 'hidden' && input.type !== 'checkbox');
|
|
4787
4820
|
const hiddenInput = inputs.find((input) => input.type === 'hidden');
|
|
4788
4821
|
|
|
4822
|
+
const readStructuredFieldsHost = (host) => {
|
|
4823
|
+
const fields = getDirectStructuredSubfields(host);
|
|
4824
|
+
if (fields.length === 1 && fields[0].dataset.structuredField === '__value') {
|
|
4825
|
+
return window.sonicReadFieldValue(fields[0]);
|
|
4826
|
+
}
|
|
4827
|
+
return fields.reduce((acc, subfield) => {
|
|
4828
|
+
const fieldName = subfield.dataset.structuredField;
|
|
4829
|
+
if (!fieldName || fieldName === '__value') return acc;
|
|
4830
|
+
acc[fieldName] = window.sonicReadFieldValue(subfield);
|
|
4831
|
+
return acc;
|
|
4832
|
+
}, {});
|
|
4833
|
+
};
|
|
4834
|
+
|
|
4835
|
+
const readStructuredObject = () => {
|
|
4836
|
+
const objectContainer = getDirectStructuredObject(fieldWrapper);
|
|
4837
|
+
if (!objectContainer) return null;
|
|
4838
|
+
const host = getStructuredObjectFieldsHost(objectContainer);
|
|
4839
|
+
return readStructuredFieldsHost(host);
|
|
4840
|
+
};
|
|
4841
|
+
|
|
4842
|
+
const readStructuredArray = () => {
|
|
4843
|
+
const arrayContainer = getDirectStructuredArray(fieldWrapper);
|
|
4844
|
+
if (!arrayContainer) return null;
|
|
4845
|
+
const list = arrayContainer.querySelector('[data-structured-array-list]');
|
|
4846
|
+
if (!list) return [];
|
|
4847
|
+
const items = Array.from(list.querySelectorAll(':scope > .structured-array-item'));
|
|
4848
|
+
return items.map((item) => {
|
|
4849
|
+
const host =
|
|
4850
|
+
item.querySelector(':scope > [data-array-item-fields]') ||
|
|
4851
|
+
item.querySelector('[data-array-item-fields]') ||
|
|
4852
|
+
item;
|
|
4853
|
+
return readStructuredFieldsHost(host);
|
|
4854
|
+
});
|
|
4855
|
+
};
|
|
4856
|
+
|
|
4789
4857
|
if (fieldType === 'object' || fieldType === 'array') {
|
|
4858
|
+
const liveValue = fieldType === 'array' ? readStructuredArray() : readStructuredObject();
|
|
4859
|
+
if (liveValue !== null) {
|
|
4860
|
+
return liveValue;
|
|
4861
|
+
}
|
|
4862
|
+
|
|
4790
4863
|
if (!hiddenInput) {
|
|
4791
4864
|
return fieldType === 'array' ? [] : {};
|
|
4792
4865
|
}
|
|
@@ -4835,6 +4908,33 @@ function getReadFieldValueScript() {
|
|
|
4835
4908
|
</script>
|
|
4836
4909
|
`;
|
|
4837
4910
|
}
|
|
4911
|
+
var STRUCTURED_INDEX_TOKEN = "__INDEX__";
|
|
4912
|
+
var BLOCK_INDEX_TOKEN = "__BLOCK_INDEX__";
|
|
4913
|
+
function sanitizeStructuredGroupId(fieldName) {
|
|
4914
|
+
return `object-${fieldName}`.split(BLOCK_INDEX_TOKEN).map(
|
|
4915
|
+
(blockSegment) => blockSegment.split(STRUCTURED_INDEX_TOKEN).map(
|
|
4916
|
+
(segment) => segment.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "")
|
|
4917
|
+
).join(STRUCTURED_INDEX_TOKEN)
|
|
4918
|
+
).join(BLOCK_INDEX_TOKEN);
|
|
4919
|
+
}
|
|
4920
|
+
function isMarkdownEditorFieldType(fieldType) {
|
|
4921
|
+
return fieldType === "markdown" || fieldType === "mdxeditor" || fieldType === "easymde";
|
|
4922
|
+
}
|
|
4923
|
+
function getEditorMetadata(fieldType) {
|
|
4924
|
+
if (fieldType === "richtext" || fieldType === "tinymce") {
|
|
4925
|
+
return {
|
|
4926
|
+
family: "richtext",
|
|
4927
|
+
provider: "tinymce"
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4930
|
+
if (isMarkdownEditorFieldType(fieldType)) {
|
|
4931
|
+
return {
|
|
4932
|
+
family: "markdown",
|
|
4933
|
+
provider: "easymde"
|
|
4934
|
+
};
|
|
4935
|
+
}
|
|
4936
|
+
return null;
|
|
4937
|
+
}
|
|
4838
4938
|
function renderDynamicField(field, options = {}) {
|
|
4839
4939
|
const { value = "", errors = [], disabled = false, className = "", pluginStatuses = {}, collectionId = "", contentId = "" } = options;
|
|
4840
4940
|
const opts = field.field_options || {};
|
|
@@ -4848,10 +4948,10 @@ function renderDynamicField(field, options = {}) {
|
|
|
4848
4948
|
if (field.field_type === "quill" && !pluginStatuses.quillEnabled) {
|
|
4849
4949
|
fallbackToTextarea = true;
|
|
4850
4950
|
fallbackWarning = "\u26A0\uFE0F Quill Editor plugin is inactive. Using textarea fallback.";
|
|
4851
|
-
} else if ((field.field_type
|
|
4951
|
+
} else if (isMarkdownEditorFieldType(field.field_type) && !pluginStatuses.mdxeditorEnabled) {
|
|
4852
4952
|
fallbackToTextarea = true;
|
|
4853
|
-
fallbackWarning = "\u26A0\uFE0F
|
|
4854
|
-
} else if (field.field_type === "tinymce" && !pluginStatuses.tinymceEnabled) {
|
|
4953
|
+
fallbackWarning = "\u26A0\uFE0F Markdown editor plugin is inactive. Using textarea fallback.";
|
|
4954
|
+
} else if ((field.field_type === "richtext" || field.field_type === "tinymce") && !pluginStatuses.tinymceEnabled) {
|
|
4855
4955
|
fallbackToTextarea = true;
|
|
4856
4956
|
fallbackWarning = "\u26A0\uFE0F TinyMCE plugin is inactive. Using textarea fallback.";
|
|
4857
4957
|
}
|
|
@@ -4973,8 +5073,10 @@ function renderDynamicField(field, options = {}) {
|
|
|
4973
5073
|
`;
|
|
4974
5074
|
break;
|
|
4975
5075
|
case "richtext":
|
|
5076
|
+
case "tinymce": {
|
|
5077
|
+
const editorMetadata = getEditorMetadata(field.field_type);
|
|
4976
5078
|
fieldHTML = `
|
|
4977
|
-
<div class="richtext-container" data-height="${opts.height || 300}" data-toolbar="${opts.toolbar || "full"}">
|
|
5079
|
+
<div class="richtext-container" data-height="${opts.height || 300}" data-toolbar="${opts.toolbar || "full"}" data-editor-family="${editorMetadata?.family || ""}" data-editor-provider="${editorMetadata?.provider || ""}">
|
|
4978
5080
|
<textarea
|
|
4979
5081
|
id="${fieldId}"
|
|
4980
5082
|
name="${fieldName}"
|
|
@@ -4985,6 +5087,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
4985
5087
|
</div>
|
|
4986
5088
|
`;
|
|
4987
5089
|
break;
|
|
5090
|
+
}
|
|
4988
5091
|
case "quill":
|
|
4989
5092
|
fieldHTML = `
|
|
4990
5093
|
<div class="quill-editor-container" data-field-id="${fieldId}">
|
|
@@ -5008,12 +5111,12 @@ function renderDynamicField(field, options = {}) {
|
|
|
5008
5111
|
</div>
|
|
5009
5112
|
`;
|
|
5010
5113
|
break;
|
|
5011
|
-
case "mdxeditor":
|
|
5012
|
-
case "tinymce":
|
|
5013
|
-
case "easymde":
|
|
5014
5114
|
case "markdown":
|
|
5115
|
+
case "mdxeditor":
|
|
5116
|
+
case "easymde": {
|
|
5117
|
+
const editorMetadata = getEditorMetadata(field.field_type);
|
|
5015
5118
|
fieldHTML = `
|
|
5016
|
-
<div class="richtext-container" data-height="${opts.height || 300}" data-toolbar="${opts.toolbar || "full"}">
|
|
5119
|
+
<div class="richtext-container" data-height="${opts.height || 300}" data-toolbar="${opts.toolbar || "full"}" data-editor-family="${editorMetadata?.family || ""}" data-editor-provider="${editorMetadata?.provider || ""}">
|
|
5017
5120
|
<textarea
|
|
5018
5121
|
id="${fieldId}"
|
|
5019
5122
|
name="${fieldName}"
|
|
@@ -5024,6 +5127,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
5024
5127
|
</div>
|
|
5025
5128
|
`;
|
|
5026
5129
|
break;
|
|
5130
|
+
}
|
|
5027
5131
|
case "number":
|
|
5028
5132
|
fieldHTML = `
|
|
5029
5133
|
<input
|
|
@@ -5393,12 +5497,14 @@ function renderDynamicField(field, options = {}) {
|
|
|
5393
5497
|
|
|
5394
5498
|
${isMultiple ? `
|
|
5395
5499
|
<div class="media-preview-grid grid grid-cols-4 gap-2 mb-2 ${mediaValues.length === 0 ? "hidden" : ""}" id="${fieldId}-preview">
|
|
5396
|
-
${mediaValues.map(
|
|
5500
|
+
${mediaValues.map(
|
|
5501
|
+
(url, idx) => `
|
|
5397
5502
|
<div class="relative media-preview-item" data-url="${url}">
|
|
5398
5503
|
${renderMediaPreview(url, `Media ${idx + 1}`, "w-full h-24 object-cover rounded-lg border border-white/20")}
|
|
5399
5504
|
<button
|
|
5400
5505
|
type="button"
|
|
5401
5506
|
onclick="removeMediaFromMultiple('${fieldId}', '${url}')"
|
|
5507
|
+
data-media-remove="true"
|
|
5402
5508
|
class="absolute top-1 right-1 bg-red-600 text-white rounded-full p-1 hover:bg-red-700"
|
|
5403
5509
|
${disabled ? "disabled" : ""}
|
|
5404
5510
|
>
|
|
@@ -5407,7 +5513,8 @@ function renderDynamicField(field, options = {}) {
|
|
|
5407
5513
|
</svg>
|
|
5408
5514
|
</button>
|
|
5409
5515
|
</div>
|
|
5410
|
-
`
|
|
5516
|
+
`
|
|
5517
|
+
).join("")}
|
|
5411
5518
|
</div>
|
|
5412
5519
|
` : `
|
|
5413
5520
|
<div class="media-preview ${singleValue ? "" : "hidden"}" id="${fieldId}-preview">
|
|
@@ -5431,6 +5538,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
5431
5538
|
<button
|
|
5432
5539
|
type="button"
|
|
5433
5540
|
onclick="clearMediaField('${fieldId}')"
|
|
5541
|
+
data-media-remove="true"
|
|
5434
5542
|
class="inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-xl hover:bg-red-700 transition-all"
|
|
5435
5543
|
${disabled ? "disabled" : ""}
|
|
5436
5544
|
>
|
|
@@ -5464,7 +5572,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
5464
5572
|
}
|
|
5465
5573
|
const showLabel = field.field_type !== "boolean";
|
|
5466
5574
|
return `
|
|
5467
|
-
<div class="form-group">
|
|
5575
|
+
<div class="form-group" data-has-errors="${errors.length > 0 ? "true" : "false"}">
|
|
5468
5576
|
${showLabel ? `
|
|
5469
5577
|
<label for="${fieldId}" class="block text-sm/6 font-medium text-zinc-950 dark:text-white mb-2">
|
|
5470
5578
|
${escapeHtml3(field.field_label)}
|
|
@@ -5473,7 +5581,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
5473
5581
|
` : ""}
|
|
5474
5582
|
${fieldHTML}
|
|
5475
5583
|
${errors.length > 0 ? `
|
|
5476
|
-
<div class="mt-2 text-sm text-pink-600 dark:text-pink-400">
|
|
5584
|
+
<div class="mt-2 text-sm text-pink-600 dark:text-pink-400" data-validation-error-message>
|
|
5477
5585
|
${errors.map((error) => `<div>${escapeHtml3(error)}</div>`).join("")}
|
|
5478
5586
|
</div>
|
|
5479
5587
|
` : ""}
|
|
@@ -5488,8 +5596,8 @@ function renderDynamicField(field, options = {}) {
|
|
|
5488
5596
|
function renderFieldGroup(title, fields, collapsible = false) {
|
|
5489
5597
|
const groupId = title.toLowerCase().replace(/\s+/g, "-");
|
|
5490
5598
|
return `
|
|
5491
|
-
<div class="field-group rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 mb-6">
|
|
5492
|
-
<div class="field-group-header border-b border-zinc-950/5 dark:border-white/10 px-6 py-4 ${collapsible ? "cursor-pointer" : ""}" ${collapsible ? `onclick="toggleFieldGroup(
|
|
5599
|
+
<div class="field-group rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 mb-6" data-group-id="${escapeHtml3(groupId)}">
|
|
5600
|
+
<div class="field-group-header border-b border-zinc-950/5 dark:border-white/10 px-6 py-4 ${collapsible ? "cursor-pointer" : ""}" ${collapsible ? `onclick="toggleFieldGroup(this)"` : ""}>
|
|
5493
5601
|
<h3 class="text-base/7 font-semibold text-zinc-950 dark:text-white flex items-center">
|
|
5494
5602
|
${escapeHtml3(title)}
|
|
5495
5603
|
${collapsible ? `
|
|
@@ -5533,6 +5641,12 @@ function renderBlocksField(field, options, baseClasses, errorClasses) {
|
|
|
5533
5641
|
>
|
|
5534
5642
|
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${escapeHtml3(JSON.stringify(blockValues))}">
|
|
5535
5643
|
|
|
5644
|
+
<div class="flex items-center justify-between border-b border-zinc-950/5 dark:border-white/10 py-4">
|
|
5645
|
+
<h3 class="text-base/7 font-semibold text-zinc-950 dark:text-white">
|
|
5646
|
+
${escapeHtml3(field.field_label || "Content Blocks")}
|
|
5647
|
+
</h3>
|
|
5648
|
+
</div>
|
|
5649
|
+
|
|
5536
5650
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
5537
5651
|
<div class="flex-1">
|
|
5538
5652
|
<select
|
|
@@ -5563,12 +5677,14 @@ function renderBlocksField(field, options, baseClasses, errorClasses) {
|
|
|
5563
5677
|
`;
|
|
5564
5678
|
}
|
|
5565
5679
|
function renderStructuredObjectField(field, options, baseClasses, errorClasses) {
|
|
5566
|
-
const { value = {}, pluginStatuses = {} } = options;
|
|
5680
|
+
const { value = {}, pluginStatuses = {}, errors = [] } = options;
|
|
5567
5681
|
const opts = field.field_options || {};
|
|
5568
5682
|
const properties = opts.properties && typeof opts.properties === "object" ? opts.properties : {};
|
|
5569
5683
|
const fieldId = `field-${field.field_name}`;
|
|
5570
5684
|
const fieldName = field.field_name;
|
|
5571
5685
|
const objectValue = normalizeStructuredObjectValue(value);
|
|
5686
|
+
const objectLayout = opts.objectLayout || "nested";
|
|
5687
|
+
const useNestedLayout = objectLayout !== "flat";
|
|
5572
5688
|
const subfields = Object.entries(properties).map(
|
|
5573
5689
|
([propertyName, propertyConfig]) => renderStructuredSubfield(
|
|
5574
5690
|
field,
|
|
@@ -5579,11 +5695,40 @@ function renderStructuredObjectField(field, options, baseClasses, errorClasses)
|
|
|
5579
5695
|
field.field_name
|
|
5580
5696
|
)
|
|
5581
5697
|
).join("");
|
|
5698
|
+
const groupTitle = field.field_label || field.field_name;
|
|
5699
|
+
if (!useNestedLayout) {
|
|
5700
|
+
return `
|
|
5701
|
+
<div class="space-y-4" data-structured-object data-field-name="${escapeHtml3(fieldName)}">
|
|
5702
|
+
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${escapeHtml3(JSON.stringify(objectValue))}">
|
|
5703
|
+
<div class="flex items-center justify-between border-b border-zinc-950/5 dark:border-white/10 py-4 first-of-type:pt-0">
|
|
5704
|
+
<h3 class="text-base/7 font-semibold text-zinc-950 dark:text-white">
|
|
5705
|
+
${escapeHtml3(groupTitle)}
|
|
5706
|
+
</h3>
|
|
5707
|
+
</div>
|
|
5708
|
+
<div class="space-y-4" data-structured-object-fields>
|
|
5709
|
+
${subfields}
|
|
5710
|
+
</div>
|
|
5711
|
+
</div>
|
|
5712
|
+
${getStructuredFieldScript()}
|
|
5713
|
+
`;
|
|
5714
|
+
}
|
|
5715
|
+
const groupId = sanitizeStructuredGroupId(field.field_name);
|
|
5716
|
+
const isCollapsed = errors.length > 0 ? false : opts.collapsed !== false;
|
|
5582
5717
|
return `
|
|
5583
|
-
<div class="
|
|
5584
|
-
<
|
|
5585
|
-
|
|
5586
|
-
|
|
5718
|
+
<div class="field-group rounded-lg shadow-sm mb-6" data-group-id="${escapeHtml3(groupId)}" data-structured-object data-field-name="${escapeHtml3(fieldName)}">
|
|
5719
|
+
<div class="field-group-header border-b border-zinc-950/5 dark:border-white/10 pr-6 pb-4 cursor-pointer" onclick="toggleFieldGroup(this)">
|
|
5720
|
+
<h3 class="text-base/7 font-semibold text-zinc-950 dark:text-white flex items-center">
|
|
5721
|
+
${escapeHtml3(groupTitle)}
|
|
5722
|
+
<svg id="${groupId}-icon" class="w-5 h-5 ml-2 transform transition-transform ${isCollapsed ? "-rotate-90" : ""} text-zinc-500 dark:text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5723
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
|
5724
|
+
</svg>
|
|
5725
|
+
</h3>
|
|
5726
|
+
</div>
|
|
5727
|
+
<div id="${groupId}-content" class="field-group-content px-6 py-6 space-y-4 ${isCollapsed ? "hidden" : ""}">
|
|
5728
|
+
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${escapeHtml3(JSON.stringify(objectValue))}">
|
|
5729
|
+
<div class="space-y-4" data-structured-object-fields>
|
|
5730
|
+
${subfields}
|
|
5731
|
+
</div>
|
|
5587
5732
|
</div>
|
|
5588
5733
|
</div>
|
|
5589
5734
|
${getStructuredFieldScript()}
|
|
@@ -5654,7 +5799,7 @@ function renderStructuredArrayField(field, options, baseClasses, errorClasses) {
|
|
|
5654
5799
|
function renderStructuredArrayItem(field, itemConfig, index, itemValue, pluginStatuses, arrayItemTitle) {
|
|
5655
5800
|
const itemFields = renderStructuredItemFields(field, itemConfig, index, itemValue, pluginStatuses);
|
|
5656
5801
|
return `
|
|
5657
|
-
<div class="structured-array-item rounded-lg border border-zinc-200 dark:border-white/10 bg-white/60 dark:bg-
|
|
5802
|
+
<div class="structured-array-item rounded-lg border border-zinc-200 dark:border-white/10 bg-white/60 dark:bg-zinc-600/5 p-4 shadow-lg shadow-zinc-950/20" data-array-index="${escapeHtml3(index)}" draggable="true">
|
|
5658
5803
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
5659
5804
|
<div class="flex items-center gap-3">
|
|
5660
5805
|
<div class="drag-handle cursor-move text-zinc-400 dark:text-zinc-500 hover:text-zinc-600 dark:hover:text-zinc-400" data-action="drag-handle" title="Drag to reorder">
|
|
@@ -5667,6 +5812,11 @@ function renderStructuredArrayItem(field, itemConfig, index, itemValue, pluginSt
|
|
|
5667
5812
|
</div>
|
|
5668
5813
|
</div>
|
|
5669
5814
|
<div class="flex flex-wrap gap-2 text-xs">
|
|
5815
|
+
<button type="button" data-action="toggle-item" class="inline-flex items-center justify-center rounded-md border border-zinc-200 px-2 py-1 text-zinc-600 hover:bg-zinc-100 dark:border-white/10 dark:text-zinc-300 dark:hover:bg-white/10" aria-label="Expand item" title="Expand">
|
|
5816
|
+
<svg class="h-4 w-4 transition-transform -rotate-90 text-zinc-500 dark:text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" data-item-toggle-icon>
|
|
5817
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
|
|
5818
|
+
</svg>
|
|
5819
|
+
</button>
|
|
5670
5820
|
<button type="button" data-action="move-up" class="inline-flex items-center justify-center rounded-md border border-zinc-200 px-2 py-1 text-zinc-600 hover:bg-zinc-100 dark:border-white/10 dark:text-zinc-300 dark:hover:bg-white/10 disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent dark:disabled:hover:bg-transparent" aria-label="Move item up" title="Move up">
|
|
5671
5821
|
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="4">
|
|
5672
5822
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6l-4 4m4-4l4 4m-4-4v12"/>
|
|
@@ -5685,7 +5835,7 @@ function renderStructuredArrayItem(field, itemConfig, index, itemValue, pluginSt
|
|
|
5685
5835
|
</button>
|
|
5686
5836
|
</div>
|
|
5687
5837
|
</div>
|
|
5688
|
-
<div class="mt-4 space-y-4" data-array-item-fields>
|
|
5838
|
+
<div class="mt-4 space-y-4 hidden" data-array-item-fields>
|
|
5689
5839
|
${itemFields}
|
|
5690
5840
|
</div>
|
|
5691
5841
|
</div>
|
|
@@ -5795,7 +5945,7 @@ function normalizeBlocksValue(value, discriminator) {
|
|
|
5795
5945
|
function renderBlockTemplate(field, block, discriminator, pluginStatuses) {
|
|
5796
5946
|
return `
|
|
5797
5947
|
<template data-block-template="${escapeHtml3(block.name)}">
|
|
5798
|
-
${renderBlockCard(field, block, discriminator,
|
|
5948
|
+
${renderBlockCard(field, block, discriminator, BLOCK_INDEX_TOKEN, {}, pluginStatuses)}
|
|
5799
5949
|
</template>
|
|
5800
5950
|
`;
|
|
5801
5951
|
}
|
|
@@ -5837,7 +5987,7 @@ function renderBlockCard(field, block, discriminator, index, data, pluginStatuse
|
|
|
5837
5987
|
`;
|
|
5838
5988
|
}).join("");
|
|
5839
5989
|
return `
|
|
5840
|
-
<div class="blocks-item rounded-lg border border-zinc-200 dark:border-white/10
|
|
5990
|
+
<div class="blocks-item rounded-lg border border-zinc-200 dark:border-white/10 dark:bg-zinc-600/5 p-4 shadow-lg shadow-zinc-950/20" data-block-type="${escapeHtml3(block.name)}" data-block-discriminator="${escapeHtml3(discriminator)}" draggable="true">
|
|
5841
5991
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
5842
5992
|
<div class="flex items-start gap-3">
|
|
5843
5993
|
<div class="drag-handle cursor-move text-zinc-400 dark:text-zinc-500 hover:text-zinc-600 dark:hover:text-zinc-400" data-action="drag-handle" title="Drag to reorder">
|
|
@@ -5845,7 +5995,7 @@ function renderBlockCard(field, block, discriminator, index, data, pluginStatuse
|
|
|
5845
5995
|
<path stroke-linecap="round" stroke-linejoin="round" d="M4 8h16M4 16h16"/>
|
|
5846
5996
|
</svg>
|
|
5847
5997
|
</div>
|
|
5848
|
-
<div>
|
|
5998
|
+
<div class="cursor-pointer" data-action="toggle-block">
|
|
5849
5999
|
<div class="text-sm font-semibold text-zinc-900 dark:text-white">
|
|
5850
6000
|
${escapeHtml3(block.label)}
|
|
5851
6001
|
<span class="ml-2 text-xs font-normal text-zinc-500 dark:text-zinc-400" data-block-order-label></span>
|
|
@@ -5854,6 +6004,11 @@ function renderBlockCard(field, block, discriminator, index, data, pluginStatuse
|
|
|
5854
6004
|
</div>
|
|
5855
6005
|
</div>
|
|
5856
6006
|
<div class="flex flex-wrap gap-2 text-xs">
|
|
6007
|
+
<button type="button" data-action="toggle-block" class="inline-flex items-center justify-center rounded-md border border-zinc-200 px-2 py-1 text-zinc-600 hover:bg-zinc-100 dark:border-white/10 dark:text-zinc-300 dark:hover:bg-white/10" aria-label="Expand block" title="Expand">
|
|
6008
|
+
<svg class="h-4 w-4 transition-transform -rotate-90 text-zinc-500 dark:text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" data-block-toggle-icon>
|
|
6009
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
|
|
6010
|
+
</svg>
|
|
6011
|
+
</button>
|
|
5857
6012
|
<button type="button" data-action="move-up" class="inline-flex items-center justify-center rounded-md border border-zinc-200 px-2 py-1 text-zinc-600 hover:bg-zinc-100 dark:border-white/10 dark:text-zinc-300 dark:hover:bg-white/10 disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent dark:disabled:hover:bg-transparent" aria-label="Move block up" title="Move up">
|
|
5858
6013
|
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="4">
|
|
5859
6014
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6l-4 4m4-4l4 4m-4-4v12"/>
|
|
@@ -5872,7 +6027,7 @@ function renderBlockCard(field, block, discriminator, index, data, pluginStatuse
|
|
|
5872
6027
|
</button>
|
|
5873
6028
|
</div>
|
|
5874
6029
|
</div>
|
|
5875
|
-
<div class="mt-4 space-y-4">
|
|
6030
|
+
<div class="mt-4 space-y-4 hidden" data-block-content>
|
|
5876
6031
|
${blockFields}
|
|
5877
6032
|
</div>
|
|
5878
6033
|
</div>
|
|
@@ -5906,9 +6061,101 @@ function getStructuredFieldScript() {
|
|
|
5906
6061
|
|
|
5907
6062
|
function initializeStructuredFields() {
|
|
5908
6063
|
const readFieldValue = window.sonicReadFieldValue;
|
|
6064
|
+
const getDirectChild = (parent, selector) => {
|
|
6065
|
+
if (!(parent instanceof Element)) return null;
|
|
6066
|
+
return Array.from(parent.children).find(
|
|
6067
|
+
(child) => child instanceof Element && child.matches(selector),
|
|
6068
|
+
) || null;
|
|
6069
|
+
};
|
|
6070
|
+
const getDirectStructuredSubfields = (host) =>
|
|
6071
|
+
Array.from(host.children).filter(
|
|
6072
|
+
(child) => child instanceof Element && child.classList.contains('structured-subfield'),
|
|
6073
|
+
);
|
|
6074
|
+
const getStructuredValueHost = (container) => {
|
|
6075
|
+
const directObjectHost = getDirectChild(container, '[data-structured-object-fields]');
|
|
6076
|
+
if (directObjectHost) return directObjectHost;
|
|
6077
|
+
const groupContent = getDirectChild(container, '.field-group-content');
|
|
6078
|
+
const nestedObjectHost = groupContent
|
|
6079
|
+
? getDirectChild(groupContent, '[data-structured-object-fields]')
|
|
6080
|
+
: null;
|
|
6081
|
+
if (nestedObjectHost) return nestedObjectHost;
|
|
6082
|
+
return getDirectChild(container, '[data-array-item-fields]') || container;
|
|
6083
|
+
};
|
|
6084
|
+
const getCollectionScope = () => {
|
|
6085
|
+
const url = new URL(window.location.href);
|
|
6086
|
+
const collectionFromQuery = url.searchParams.get('collection');
|
|
6087
|
+
const form = document.getElementById('content-form');
|
|
6088
|
+
const collectionInput = form?.querySelector('input[name="collection_id"]');
|
|
6089
|
+
const collectionFromForm = collectionInput instanceof HTMLInputElement ? collectionInput.value : '';
|
|
6090
|
+
const collectionId = collectionFromQuery || collectionFromForm || '';
|
|
6091
|
+
return window.location.pathname + ':' + collectionId;
|
|
6092
|
+
};
|
|
6093
|
+
|
|
6094
|
+
const getArrayStateKey = (container) => {
|
|
6095
|
+
const fieldName = container.dataset.fieldName || 'unknown';
|
|
6096
|
+
return 'sonic:ui:repeaters:' + getCollectionScope() + ':' + fieldName;
|
|
6097
|
+
};
|
|
6098
|
+
|
|
6099
|
+
const readArrayState = (container) => {
|
|
6100
|
+
try {
|
|
6101
|
+
const raw = sessionStorage.getItem(getArrayStateKey(container));
|
|
6102
|
+
if (!raw) return null;
|
|
6103
|
+
const parsed = JSON.parse(raw);
|
|
6104
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
6105
|
+
} catch {
|
|
6106
|
+
return null;
|
|
6107
|
+
}
|
|
6108
|
+
};
|
|
6109
|
+
|
|
6110
|
+
const writeArrayState = (container, state) => {
|
|
6111
|
+
try {
|
|
6112
|
+
sessionStorage.setItem(getArrayStateKey(container), JSON.stringify(state));
|
|
6113
|
+
} catch {}
|
|
6114
|
+
};
|
|
6115
|
+
|
|
6116
|
+
const setArrayItemExpanded = (item, isExpanded) => {
|
|
6117
|
+
const content = item.querySelector('[data-array-item-fields]');
|
|
6118
|
+
const icon = item.querySelector('[data-item-toggle-icon]');
|
|
6119
|
+
if (content instanceof HTMLElement) {
|
|
6120
|
+
content.classList.toggle('hidden', !isExpanded);
|
|
6121
|
+
}
|
|
6122
|
+
if (icon instanceof Element) {
|
|
6123
|
+
icon.classList.toggle('-rotate-90', !isExpanded);
|
|
6124
|
+
}
|
|
6125
|
+
};
|
|
6126
|
+
|
|
6127
|
+
const getArrayItems = (container, list) => {
|
|
6128
|
+
if (list) {
|
|
6129
|
+
return Array.from(list.querySelectorAll(':scope > .structured-array-item'));
|
|
6130
|
+
}
|
|
6131
|
+
return Array.from(
|
|
6132
|
+
container.querySelectorAll(':scope > [data-structured-array-list] > .structured-array-item'),
|
|
6133
|
+
);
|
|
6134
|
+
};
|
|
6135
|
+
|
|
6136
|
+
const captureArrayState = (container) => {
|
|
6137
|
+
return getArrayItems(container).map((item) => {
|
|
6138
|
+
const content = item.querySelector('[data-array-item-fields]');
|
|
6139
|
+
return content instanceof HTMLElement ? !content.classList.contains('hidden') : false;
|
|
6140
|
+
});
|
|
6141
|
+
};
|
|
6142
|
+
|
|
6143
|
+
const applyArrayState = (container, state) => {
|
|
6144
|
+
const items = getArrayItems(container);
|
|
6145
|
+
items.forEach((item, index) => {
|
|
6146
|
+
if (typeof state[index] === 'boolean') {
|
|
6147
|
+
setArrayItemExpanded(item, state[index]);
|
|
6148
|
+
}
|
|
6149
|
+
});
|
|
6150
|
+
};
|
|
6151
|
+
|
|
6152
|
+
const syncArrayState = (container) => {
|
|
6153
|
+
writeArrayState(container, captureArrayState(container));
|
|
6154
|
+
};
|
|
5909
6155
|
|
|
5910
6156
|
const readStructuredValue = (container) => {
|
|
5911
|
-
const
|
|
6157
|
+
const fieldHost = getStructuredValueHost(container);
|
|
6158
|
+
const fields = getDirectStructuredSubfields(fieldHost);
|
|
5912
6159
|
if (fields.length === 1 && fields[0].dataset.structuredField === '__value') {
|
|
5913
6160
|
return readFieldValue(fields[0]);
|
|
5914
6161
|
}
|
|
@@ -5922,111 +6169,229 @@ function getStructuredFieldScript() {
|
|
|
5922
6169
|
};
|
|
5923
6170
|
|
|
5924
6171
|
document.querySelectorAll('[data-structured-object]').forEach((container) => {
|
|
6172
|
+
if (container.closest('template')) {
|
|
6173
|
+
return;
|
|
6174
|
+
}
|
|
5925
6175
|
if (container.dataset.structuredInitialized === 'true') {
|
|
5926
6176
|
return;
|
|
5927
6177
|
}
|
|
5928
|
-
container.dataset.
|
|
5929
|
-
|
|
6178
|
+
if (container.dataset.structuredInitializing === 'true') {
|
|
6179
|
+
return;
|
|
6180
|
+
}
|
|
6181
|
+
container.dataset.structuredInitializing = 'true';
|
|
6182
|
+
try {
|
|
6183
|
+
const hiddenInput = container.querySelector('input[type="hidden"]');
|
|
5930
6184
|
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
6185
|
+
const updateHiddenInput = () => {
|
|
6186
|
+
if (!hiddenInput) return;
|
|
6187
|
+
const value = readStructuredValue(container);
|
|
6188
|
+
hiddenInput.value = JSON.stringify(value);
|
|
6189
|
+
};
|
|
5936
6190
|
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
6191
|
+
container.addEventListener('input', updateHiddenInput);
|
|
6192
|
+
container.addEventListener('change', updateHiddenInput);
|
|
6193
|
+
updateHiddenInput();
|
|
6194
|
+
container.dataset.structuredInitialized = 'true';
|
|
6195
|
+
} catch (error) {
|
|
6196
|
+
delete container.dataset.structuredInitialized;
|
|
6197
|
+
console.error('[structured-object] initialization failed', error);
|
|
6198
|
+
} finally {
|
|
6199
|
+
delete container.dataset.structuredInitializing;
|
|
6200
|
+
}
|
|
5940
6201
|
});
|
|
5941
6202
|
|
|
5942
6203
|
document.querySelectorAll('[data-structured-array]').forEach((container) => {
|
|
6204
|
+
if (container.closest('template')) {
|
|
6205
|
+
return;
|
|
6206
|
+
}
|
|
5943
6207
|
if (container.dataset.structuredInitialized === 'true') {
|
|
5944
6208
|
return;
|
|
5945
6209
|
}
|
|
5946
|
-
container.dataset.
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
6210
|
+
if (container.dataset.structuredInitializing === 'true') {
|
|
6211
|
+
return;
|
|
6212
|
+
}
|
|
6213
|
+
container.dataset.structuredInitializing = 'true';
|
|
6214
|
+
try {
|
|
6215
|
+
const list = container.querySelector(':scope > [data-structured-array-list]');
|
|
6216
|
+
const hiddenInput = container.querySelector(':scope > input[type="hidden"]');
|
|
6217
|
+
const template = container.querySelector(':scope > template[data-structured-array-template]');
|
|
6218
|
+
if (
|
|
6219
|
+
template instanceof HTMLTemplateElement &&
|
|
6220
|
+
typeof template.innerHTML === 'string' &&
|
|
6221
|
+
template.innerHTML.trim()
|
|
6222
|
+
) {
|
|
6223
|
+
container.__sonicStructuredArrayTemplate = template.innerHTML;
|
|
6224
|
+
}
|
|
5950
6225
|
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
6226
|
+
const getLiveList = () =>
|
|
6227
|
+
list || container.querySelector(':scope > [data-structured-array-list]');
|
|
6228
|
+
const getLiveHiddenInput = () =>
|
|
6229
|
+
hiddenInput || container.querySelector(':scope > input[type="hidden"]');
|
|
6230
|
+
const getTemplateHtml = () => {
|
|
6231
|
+
if (typeof container.__sonicStructuredArrayTemplate === 'string' &&
|
|
6232
|
+
container.__sonicStructuredArrayTemplate.trim()) {
|
|
6233
|
+
return container.__sonicStructuredArrayTemplate;
|
|
5957
6234
|
}
|
|
5958
6235
|
|
|
5959
|
-
const
|
|
5960
|
-
|
|
5961
|
-
|
|
6236
|
+
const liveTemplate =
|
|
6237
|
+
template instanceof HTMLTemplateElement
|
|
6238
|
+
? template
|
|
6239
|
+
: container.querySelector(':scope > template[data-structured-array-template]');
|
|
6240
|
+
if (
|
|
6241
|
+
liveTemplate instanceof HTMLTemplateElement &&
|
|
6242
|
+
typeof liveTemplate.innerHTML === 'string' &&
|
|
6243
|
+
liveTemplate.innerHTML.trim()
|
|
6244
|
+
) {
|
|
6245
|
+
container.__sonicStructuredArrayTemplate = liveTemplate.innerHTML;
|
|
6246
|
+
return liveTemplate.innerHTML;
|
|
5962
6247
|
}
|
|
6248
|
+
return typeof container.__sonicStructuredArrayTemplate === 'string'
|
|
6249
|
+
? container.__sonicStructuredArrayTemplate
|
|
6250
|
+
: '';
|
|
6251
|
+
};
|
|
5963
6252
|
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
6253
|
+
const updateOrderLabels = () => {
|
|
6254
|
+
const liveList = getLiveList();
|
|
6255
|
+
if (!liveList) return;
|
|
6256
|
+
const items = getArrayItems(container, liveList);
|
|
6257
|
+
items.forEach((item, index) => {
|
|
6258
|
+
const label = item.querySelector('[data-array-order-label]');
|
|
6259
|
+
if (label) {
|
|
6260
|
+
label.textContent = '#'+ (index + 1);
|
|
6261
|
+
}
|
|
6262
|
+
|
|
6263
|
+
const moveUpButton = item.querySelector('[data-action="move-up"]');
|
|
6264
|
+
if (moveUpButton instanceof HTMLButtonElement) {
|
|
6265
|
+
moveUpButton.disabled = index === 0;
|
|
6266
|
+
}
|
|
6267
|
+
|
|
6268
|
+
const moveDownButton = item.querySelector('[data-action="move-down"]');
|
|
6269
|
+
if (moveDownButton instanceof HTMLButtonElement) {
|
|
6270
|
+
moveDownButton.disabled = index === items.length - 1;
|
|
6271
|
+
}
|
|
6272
|
+
});
|
|
6273
|
+
};
|
|
6274
|
+
|
|
6275
|
+
const updateHiddenInput = () => {
|
|
6276
|
+
const liveHiddenInput = getLiveHiddenInput();
|
|
6277
|
+
const liveList = getLiveList();
|
|
6278
|
+
if (!liveHiddenInput || !liveList) return;
|
|
6279
|
+
const items = getArrayItems(container, liveList);
|
|
6280
|
+
const values = items.map((item) => readStructuredValue(item));
|
|
6281
|
+
liveHiddenInput.value = JSON.stringify(values);
|
|
6282
|
+
// Notify parent structured containers after non-input actions (add/remove/move)
|
|
6283
|
+
// so nested array mutations are persisted correctly.
|
|
6284
|
+
liveHiddenInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
6285
|
+
|
|
6286
|
+
const emptyState = liveList.querySelector(':scope > [data-structured-empty]');
|
|
6287
|
+
if (emptyState) {
|
|
6288
|
+
emptyState.style.display = values.length === 0 ? 'block' : 'none';
|
|
5967
6289
|
}
|
|
5968
|
-
|
|
5969
|
-
|
|
6290
|
+
updateOrderLabels();
|
|
6291
|
+
};
|
|
5970
6292
|
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
6293
|
+
const addArrayItem = () => {
|
|
6294
|
+
const liveList = getLiveList();
|
|
6295
|
+
if (!liveList) return;
|
|
6296
|
+
const templateHtml = getTemplateHtml();
|
|
6297
|
+
if (!templateHtml) return;
|
|
6298
|
+
try {
|
|
6299
|
+
const nextIndex = getArrayItems(container, liveList).length;
|
|
6300
|
+
const html = templateHtml.replace(/__INDEX__/g, String(nextIndex));
|
|
6301
|
+
liveList.insertAdjacentHTML('beforeend', html);
|
|
6302
|
+
const newItem = liveList.lastElementChild;
|
|
6303
|
+
if (newItem instanceof HTMLElement) {
|
|
6304
|
+
// Ensure cloned template content can be initialized even if stale
|
|
6305
|
+
// data-structured-initialized attributes were copied.
|
|
6306
|
+
newItem
|
|
6307
|
+
.querySelectorAll('[data-structured-object], [data-structured-array]')
|
|
6308
|
+
.forEach((nestedContainer) => {
|
|
6309
|
+
if (nestedContainer instanceof HTMLElement) {
|
|
6310
|
+
delete nestedContainer.dataset.structuredInitialized;
|
|
6311
|
+
}
|
|
6312
|
+
});
|
|
6313
|
+
setArrayItemExpanded(newItem, true);
|
|
6314
|
+
}
|
|
6315
|
+
if (typeof initializeTinyMCE === 'function') {
|
|
6316
|
+
initializeTinyMCE();
|
|
6317
|
+
}
|
|
6318
|
+
if (typeof window.initializeQuillEditors === 'function') {
|
|
6319
|
+
window.initializeQuillEditors();
|
|
6320
|
+
}
|
|
6321
|
+
if (typeof initializeMDXEditor === 'function') {
|
|
6322
|
+
initializeMDXEditor();
|
|
6323
|
+
}
|
|
6324
|
+
if (typeof window.initializeStructuredFields === 'function') {
|
|
6325
|
+
window.initializeStructuredFields();
|
|
6326
|
+
}
|
|
6327
|
+
updateHiddenInput();
|
|
6328
|
+
syncArrayState(container);
|
|
6329
|
+
} catch (error) {
|
|
6330
|
+
console.error('[structured-array] add-item failed', error);
|
|
6331
|
+
}
|
|
6332
|
+
};
|
|
5976
6333
|
|
|
5977
|
-
const
|
|
5978
|
-
|
|
5979
|
-
|
|
6334
|
+
const topLevelAddButton = container.querySelector(
|
|
6335
|
+
':scope > .flex.items-center.justify-between.gap-3 [data-action="add-item"]',
|
|
6336
|
+
);
|
|
6337
|
+
if (topLevelAddButton instanceof HTMLElement) {
|
|
6338
|
+
topLevelAddButton.addEventListener('click', (event) => {
|
|
6339
|
+
event.preventDefault();
|
|
6340
|
+
event.stopPropagation();
|
|
6341
|
+
addArrayItem();
|
|
6342
|
+
});
|
|
5980
6343
|
}
|
|
5981
|
-
updateOrderLabels();
|
|
5982
|
-
};
|
|
5983
6344
|
|
|
5984
|
-
|
|
5985
|
-
window.initializeDragSortable
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
6345
|
+
const dragList = getLiveList();
|
|
6346
|
+
if (typeof window.initializeDragSortable === 'function' && dragList) {
|
|
6347
|
+
window.initializeDragSortable(dragList, {
|
|
6348
|
+
itemSelector: '.structured-array-item',
|
|
6349
|
+
handleSelector: '[data-action="drag-handle"]',
|
|
6350
|
+
onUpdate: () => {
|
|
6351
|
+
updateHiddenInput();
|
|
6352
|
+
syncArrayState(container);
|
|
6353
|
+
}
|
|
6354
|
+
});
|
|
6355
|
+
}
|
|
5991
6356
|
|
|
5992
|
-
|
|
6357
|
+
container.addEventListener('click', (event) => {
|
|
5993
6358
|
const target = event.target;
|
|
5994
6359
|
if (!(target instanceof Element)) return;
|
|
5995
6360
|
const actionButton = target.closest('[data-action]');
|
|
5996
6361
|
if (!actionButton || actionButton.hasAttribute('disabled')) return;
|
|
6362
|
+
const actionOwner = actionButton.closest('[data-structured-array]');
|
|
6363
|
+
if (actionOwner !== container) return;
|
|
5997
6364
|
|
|
5998
|
-
|
|
6365
|
+
const action = actionButton.getAttribute('data-action');
|
|
5999
6366
|
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
const html = template.innerHTML.replace(/__INDEX__/g, String(nextIndex));
|
|
6004
|
-
list.insertAdjacentHTML('beforeend', html);
|
|
6005
|
-
if (typeof initializeTinyMCE === 'function') {
|
|
6006
|
-
initializeTinyMCE();
|
|
6007
|
-
}
|
|
6008
|
-
if (typeof window.initializeQuillEditors === 'function') {
|
|
6009
|
-
window.initializeQuillEditors();
|
|
6010
|
-
}
|
|
6011
|
-
if (typeof initializeMDXEditor === 'function') {
|
|
6012
|
-
initializeMDXEditor();
|
|
6367
|
+
if (action === 'add-item') {
|
|
6368
|
+
addArrayItem();
|
|
6369
|
+
return;
|
|
6013
6370
|
}
|
|
6014
|
-
updateHiddenInput();
|
|
6015
|
-
return;
|
|
6016
|
-
}
|
|
6017
6371
|
|
|
6018
6372
|
const item = actionButton.closest('.structured-array-item');
|
|
6019
|
-
|
|
6373
|
+
const liveList = getLiveList();
|
|
6374
|
+
if (!item || !liveList) return;
|
|
6375
|
+
|
|
6376
|
+
if (action === 'toggle-item') {
|
|
6377
|
+
const content = item.querySelector('[data-array-item-fields]');
|
|
6378
|
+
if (!(content instanceof HTMLElement)) return;
|
|
6379
|
+
setArrayItemExpanded(item, content.classList.contains('hidden'));
|
|
6380
|
+
syncArrayState(container);
|
|
6381
|
+
return;
|
|
6382
|
+
}
|
|
6020
6383
|
|
|
6021
6384
|
if (action === 'remove-item') {
|
|
6022
6385
|
if (typeof requestRepeaterDelete === 'function') {
|
|
6023
6386
|
requestRepeaterDelete(() => {
|
|
6024
6387
|
item.remove();
|
|
6025
6388
|
updateHiddenInput();
|
|
6389
|
+
syncArrayState(container);
|
|
6026
6390
|
});
|
|
6027
6391
|
} else {
|
|
6028
6392
|
item.remove();
|
|
6029
6393
|
updateHiddenInput();
|
|
6394
|
+
syncArrayState(container);
|
|
6030
6395
|
}
|
|
6031
6396
|
return;
|
|
6032
6397
|
}
|
|
@@ -6034,8 +6399,9 @@ function getStructuredFieldScript() {
|
|
|
6034
6399
|
if (action === 'move-up') {
|
|
6035
6400
|
const previous = item.previousElementSibling;
|
|
6036
6401
|
if (previous) {
|
|
6037
|
-
|
|
6402
|
+
liveList.insertBefore(item, previous);
|
|
6038
6403
|
updateHiddenInput();
|
|
6404
|
+
syncArrayState(container);
|
|
6039
6405
|
}
|
|
6040
6406
|
return;
|
|
6041
6407
|
}
|
|
@@ -6043,29 +6409,43 @@ function getStructuredFieldScript() {
|
|
|
6043
6409
|
if (action === 'move-down') {
|
|
6044
6410
|
const next = item.nextElementSibling;
|
|
6045
6411
|
if (next) {
|
|
6046
|
-
|
|
6412
|
+
liveList.insertBefore(next, item);
|
|
6047
6413
|
updateHiddenInput();
|
|
6414
|
+
syncArrayState(container);
|
|
6048
6415
|
}
|
|
6049
6416
|
}
|
|
6050
|
-
|
|
6417
|
+
});
|
|
6051
6418
|
|
|
6052
|
-
|
|
6419
|
+
container.addEventListener('input', (event) => {
|
|
6053
6420
|
const target = event.target;
|
|
6054
6421
|
if (!(target instanceof Element)) return;
|
|
6055
6422
|
if (target.closest('[data-structured-array-list]')) {
|
|
6056
6423
|
updateHiddenInput();
|
|
6057
6424
|
}
|
|
6058
|
-
|
|
6425
|
+
});
|
|
6059
6426
|
|
|
6060
|
-
|
|
6427
|
+
container.addEventListener('change', (event) => {
|
|
6061
6428
|
const target = event.target;
|
|
6062
6429
|
if (!(target instanceof Element)) return;
|
|
6063
6430
|
if (target.closest('[data-structured-array-list]')) {
|
|
6064
6431
|
updateHiddenInput();
|
|
6065
6432
|
}
|
|
6066
|
-
|
|
6433
|
+
});
|
|
6067
6434
|
|
|
6068
|
-
|
|
6435
|
+
updateHiddenInput();
|
|
6436
|
+
const savedArrayState = readArrayState(container);
|
|
6437
|
+
if (savedArrayState) {
|
|
6438
|
+
applyArrayState(container, savedArrayState);
|
|
6439
|
+
} else {
|
|
6440
|
+
syncArrayState(container);
|
|
6441
|
+
}
|
|
6442
|
+
container.dataset.structuredInitialized = 'true';
|
|
6443
|
+
} catch (error) {
|
|
6444
|
+
delete container.dataset.structuredInitialized;
|
|
6445
|
+
console.error('[structured-array] initialization failed', error);
|
|
6446
|
+
} finally {
|
|
6447
|
+
delete container.dataset.structuredInitializing;
|
|
6448
|
+
}
|
|
6069
6449
|
});
|
|
6070
6450
|
}
|
|
6071
6451
|
|
|
@@ -6080,7 +6460,10 @@ function getStructuredFieldScript() {
|
|
|
6080
6460
|
document.addEventListener('htmx:afterSwap', function() {
|
|
6081
6461
|
setTimeout(initializeStructuredFields, 50);
|
|
6082
6462
|
});
|
|
6083
|
-
} else if (
|
|
6463
|
+
} else if (
|
|
6464
|
+
typeof window.initializeStructuredFields === 'function' &&
|
|
6465
|
+
document.readyState !== 'loading'
|
|
6466
|
+
) {
|
|
6084
6467
|
window.initializeStructuredFields();
|
|
6085
6468
|
}
|
|
6086
6469
|
</script>
|
|
@@ -6092,6 +6475,68 @@ function getBlocksFieldScript() {
|
|
|
6092
6475
|
<script>
|
|
6093
6476
|
if (!window.__sonicBlocksFieldInit) {
|
|
6094
6477
|
window.__sonicBlocksFieldInit = true;
|
|
6478
|
+
const getCollectionScope = () => {
|
|
6479
|
+
const url = new URL(window.location.href);
|
|
6480
|
+
const collectionFromQuery = url.searchParams.get('collection');
|
|
6481
|
+
const form = document.getElementById('content-form');
|
|
6482
|
+
const collectionInput = form?.querySelector('input[name="collection_id"]');
|
|
6483
|
+
const collectionFromForm = collectionInput instanceof HTMLInputElement ? collectionInput.value : '';
|
|
6484
|
+
const collectionId = collectionFromQuery || collectionFromForm || '';
|
|
6485
|
+
return window.location.pathname + ':' + collectionId;
|
|
6486
|
+
};
|
|
6487
|
+
|
|
6488
|
+
const getBlocksStateKey = (container) => {
|
|
6489
|
+
const fieldName = container.dataset.fieldName || 'unknown';
|
|
6490
|
+
return 'sonic:ui:blocks:' + getCollectionScope() + ':' + fieldName;
|
|
6491
|
+
};
|
|
6492
|
+
|
|
6493
|
+
const readBlocksState = (container) => {
|
|
6494
|
+
try {
|
|
6495
|
+
const raw = sessionStorage.getItem(getBlocksStateKey(container));
|
|
6496
|
+
if (!raw) return null;
|
|
6497
|
+
const parsed = JSON.parse(raw);
|
|
6498
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
6499
|
+
} catch {
|
|
6500
|
+
return null;
|
|
6501
|
+
}
|
|
6502
|
+
};
|
|
6503
|
+
|
|
6504
|
+
const writeBlocksState = (container, state) => {
|
|
6505
|
+
try {
|
|
6506
|
+
sessionStorage.setItem(getBlocksStateKey(container), JSON.stringify(state));
|
|
6507
|
+
} catch {}
|
|
6508
|
+
};
|
|
6509
|
+
|
|
6510
|
+
const setBlockExpanded = (item, isExpanded) => {
|
|
6511
|
+
const content = item.querySelector('[data-block-content]');
|
|
6512
|
+
const icon = item.querySelector('[data-block-toggle-icon]');
|
|
6513
|
+
if (content instanceof HTMLElement) {
|
|
6514
|
+
content.classList.toggle('hidden', !isExpanded);
|
|
6515
|
+
}
|
|
6516
|
+
if (icon instanceof Element) {
|
|
6517
|
+
icon.classList.toggle('-rotate-90', !isExpanded);
|
|
6518
|
+
}
|
|
6519
|
+
};
|
|
6520
|
+
|
|
6521
|
+
const captureBlocksState = (container) => {
|
|
6522
|
+
return Array.from(container.querySelectorAll('.blocks-item')).map((item) => {
|
|
6523
|
+
const content = item.querySelector('[data-block-content]');
|
|
6524
|
+
return content instanceof HTMLElement ? !content.classList.contains('hidden') : false;
|
|
6525
|
+
});
|
|
6526
|
+
};
|
|
6527
|
+
|
|
6528
|
+
const applyBlocksState = (container, state) => {
|
|
6529
|
+
const items = Array.from(container.querySelectorAll('.blocks-item'));
|
|
6530
|
+
items.forEach((item, index) => {
|
|
6531
|
+
if (typeof state[index] === 'boolean') {
|
|
6532
|
+
setBlockExpanded(item, state[index]);
|
|
6533
|
+
}
|
|
6534
|
+
});
|
|
6535
|
+
};
|
|
6536
|
+
|
|
6537
|
+
const syncBlocksState = (container) => {
|
|
6538
|
+
writeBlocksState(container, captureBlocksState(container));
|
|
6539
|
+
};
|
|
6095
6540
|
|
|
6096
6541
|
function initializeBlocksFields() {
|
|
6097
6542
|
document.querySelectorAll('.blocks-field').forEach((container) => {
|
|
@@ -6179,7 +6624,10 @@ function getBlocksFieldScript() {
|
|
|
6179
6624
|
window.initializeDragSortable(list, {
|
|
6180
6625
|
itemSelector: '.blocks-item',
|
|
6181
6626
|
handleSelector: '[data-action="drag-handle"]',
|
|
6182
|
-
onUpdate:
|
|
6627
|
+
onUpdate: () => {
|
|
6628
|
+
updateHiddenInput();
|
|
6629
|
+
syncBlocksState(container);
|
|
6630
|
+
}
|
|
6183
6631
|
});
|
|
6184
6632
|
}
|
|
6185
6633
|
|
|
@@ -6201,8 +6649,12 @@ function getBlocksFieldScript() {
|
|
|
6201
6649
|
if (!template) return;
|
|
6202
6650
|
|
|
6203
6651
|
const nextIndex = list.querySelectorAll('.blocks-item').length;
|
|
6204
|
-
const html = template.innerHTML.replace(/
|
|
6652
|
+
const html = template.innerHTML.replace(/__BLOCK_INDEX__/g, String(nextIndex));
|
|
6205
6653
|
list.insertAdjacentHTML('beforeend', html);
|
|
6654
|
+
const newItem = list.lastElementChild;
|
|
6655
|
+
if (newItem instanceof HTMLElement) {
|
|
6656
|
+
setBlockExpanded(newItem, true);
|
|
6657
|
+
}
|
|
6206
6658
|
if (typeSelect) {
|
|
6207
6659
|
typeSelect.value = '';
|
|
6208
6660
|
}
|
|
@@ -6211,21 +6663,32 @@ function getBlocksFieldScript() {
|
|
|
6211
6663
|
window.initializeStructuredFields();
|
|
6212
6664
|
}
|
|
6213
6665
|
updateHiddenInput();
|
|
6666
|
+
syncBlocksState(container);
|
|
6214
6667
|
return;
|
|
6215
6668
|
}
|
|
6216
6669
|
|
|
6217
6670
|
const item = actionButton.closest('.blocks-item');
|
|
6218
6671
|
if (!item || !list) return;
|
|
6219
6672
|
|
|
6673
|
+
if (action === 'toggle-block') {
|
|
6674
|
+
const content = item.querySelector('[data-block-content]');
|
|
6675
|
+
if (!(content instanceof HTMLElement)) return;
|
|
6676
|
+
setBlockExpanded(item, content.classList.contains('hidden'));
|
|
6677
|
+
syncBlocksState(container);
|
|
6678
|
+
return;
|
|
6679
|
+
}
|
|
6680
|
+
|
|
6220
6681
|
if (action === 'remove-block') {
|
|
6221
6682
|
if (typeof requestRepeaterDelete === 'function') {
|
|
6222
6683
|
requestRepeaterDelete(() => {
|
|
6223
6684
|
item.remove();
|
|
6224
6685
|
updateHiddenInput();
|
|
6686
|
+
syncBlocksState(container);
|
|
6225
6687
|
}, 'block');
|
|
6226
6688
|
} else {
|
|
6227
6689
|
item.remove();
|
|
6228
6690
|
updateHiddenInput();
|
|
6691
|
+
syncBlocksState(container);
|
|
6229
6692
|
}
|
|
6230
6693
|
return;
|
|
6231
6694
|
}
|
|
@@ -6235,6 +6698,7 @@ function getBlocksFieldScript() {
|
|
|
6235
6698
|
if (previous) {
|
|
6236
6699
|
list.insertBefore(item, previous);
|
|
6237
6700
|
updateHiddenInput();
|
|
6701
|
+
syncBlocksState(container);
|
|
6238
6702
|
}
|
|
6239
6703
|
return;
|
|
6240
6704
|
}
|
|
@@ -6244,6 +6708,7 @@ function getBlocksFieldScript() {
|
|
|
6244
6708
|
if (next) {
|
|
6245
6709
|
list.insertBefore(next, item);
|
|
6246
6710
|
updateHiddenInput();
|
|
6711
|
+
syncBlocksState(container);
|
|
6247
6712
|
}
|
|
6248
6713
|
}
|
|
6249
6714
|
});
|
|
@@ -6265,6 +6730,12 @@ function getBlocksFieldScript() {
|
|
|
6265
6730
|
});
|
|
6266
6731
|
|
|
6267
6732
|
updateHiddenInput();
|
|
6733
|
+
const savedBlocksState = readBlocksState(container);
|
|
6734
|
+
if (savedBlocksState) {
|
|
6735
|
+
applyBlocksState(container, savedBlocksState);
|
|
6736
|
+
} else {
|
|
6737
|
+
syncBlocksState(container);
|
|
6738
|
+
}
|
|
6268
6739
|
});
|
|
6269
6740
|
}
|
|
6270
6741
|
|
|
@@ -6301,6 +6772,7 @@ chunkLTKV7AE5_cjs.init_admin_layout_catalyst_template();
|
|
|
6301
6772
|
function renderContentFormPage(data) {
|
|
6302
6773
|
const isEdit = data.isEdit || !!data.id;
|
|
6303
6774
|
const title = isEdit ? `Edit: ${data.title || "Content"}` : `New ${data.collection.display_name}`;
|
|
6775
|
+
const hasValidationErrors = Boolean(data.validationErrors && Object.keys(data.validationErrors).length > 0);
|
|
6304
6776
|
const backUrl = data.referrerParams ? `/admin/content?${data.referrerParams}` : `/admin/content?collection=${data.collection.id}`;
|
|
6305
6777
|
const coreFields = data.fields.filter((f) => ["title", "slug", "content"].includes(f.field_name));
|
|
6306
6778
|
const contentFields = data.fields.filter((f) => !["title", "slug", "content"].includes(f.field_name) && !f.field_name.startsWith("meta_"));
|
|
@@ -6389,6 +6861,7 @@ function renderContentFormPage(data) {
|
|
|
6389
6861
|
${isEdit ? `hx-put="/admin/content/${data.id}"` : `hx-post="/admin/content"`}
|
|
6390
6862
|
hx-target="#form-messages"
|
|
6391
6863
|
hx-encoding="multipart/form-data"
|
|
6864
|
+
data-has-validation-errors="${hasValidationErrors ? "true" : "false"}"
|
|
6392
6865
|
class="space-y-6"
|
|
6393
6866
|
>
|
|
6394
6867
|
<input type="hidden" name="collection_id" value="${data.collection.id}">
|
|
@@ -6669,39 +7142,456 @@ function renderContentFormPage(data) {
|
|
|
6669
7142
|
|
|
6670
7143
|
<!-- Dynamic Field Scripts -->
|
|
6671
7144
|
<script>
|
|
7145
|
+
const contentFormCollectionId = ${JSON.stringify(data.collection.id)};
|
|
7146
|
+
|
|
7147
|
+
function getFieldGroupScope() {
|
|
7148
|
+
const url = new URL(window.location.href);
|
|
7149
|
+
const urlCollectionId = url.searchParams.get('collection');
|
|
7150
|
+
const effectiveCollectionId = urlCollectionId || contentFormCollectionId || '';
|
|
7151
|
+
return window.location.pathname + ':' + effectiveCollectionId;
|
|
7152
|
+
}
|
|
7153
|
+
|
|
7154
|
+
function getItemPosition(itemSelector, item) {
|
|
7155
|
+
if (!(item instanceof Element)) return -1;
|
|
7156
|
+
const parent = item.parentElement;
|
|
7157
|
+
if (!parent) return -1;
|
|
7158
|
+
return Array.from(parent.querySelectorAll(':scope > ' + itemSelector)).indexOf(item);
|
|
7159
|
+
}
|
|
7160
|
+
|
|
7161
|
+
function stripIndexedFieldPrefix(fullFieldName, prefix) {
|
|
7162
|
+
if (!fullFieldName || !prefix || !fullFieldName.startsWith(prefix)) {
|
|
7163
|
+
return fullFieldName;
|
|
7164
|
+
}
|
|
7165
|
+
|
|
7166
|
+
const remainder = fullFieldName.slice(prefix.length);
|
|
7167
|
+
const indexMatch = remainder.match(/^(\\d+)(-|__)(.*)$/);
|
|
7168
|
+
if (!indexMatch) {
|
|
7169
|
+
return fullFieldName;
|
|
7170
|
+
}
|
|
7171
|
+
|
|
7172
|
+
return indexMatch[3];
|
|
7173
|
+
}
|
|
7174
|
+
|
|
7175
|
+
function getFieldGroupStorageKey(groupOrId) {
|
|
7176
|
+
const defaultGroupId = typeof groupOrId === 'string' ? groupOrId : (groupOrId?.getAttribute('data-group-id') || 'unknown');
|
|
7177
|
+
const group = typeof groupOrId === 'string'
|
|
7178
|
+
? document.querySelector('.field-group[data-group-id="' + defaultGroupId + '"]')
|
|
7179
|
+
: groupOrId;
|
|
7180
|
+
|
|
7181
|
+
const scopePrefix = 'sonic:ui:objects:' + getFieldGroupScope() + ':';
|
|
7182
|
+
if (!(group instanceof Element)) {
|
|
7183
|
+
return scopePrefix + defaultGroupId;
|
|
7184
|
+
}
|
|
7185
|
+
|
|
7186
|
+
const fullFieldName = group.getAttribute('data-field-name') || '';
|
|
7187
|
+
|
|
7188
|
+
const blocksField = group.closest('.blocks-field');
|
|
7189
|
+
const blockItem = group.closest('.blocks-item');
|
|
7190
|
+
if (blocksField instanceof Element && blockItem instanceof Element) {
|
|
7191
|
+
const blocksFieldName = blocksField.getAttribute('data-field-name') || 'unknown';
|
|
7192
|
+
const blockPosition = getItemPosition('.blocks-item', blockItem);
|
|
7193
|
+
const relativePath = stripIndexedFieldPrefix(fullFieldName, 'block-' + blocksFieldName + '-') || defaultGroupId;
|
|
7194
|
+
return scopePrefix + 'blocks:' + blocksFieldName + ':' + blockPosition + ':' + relativePath;
|
|
7195
|
+
}
|
|
7196
|
+
|
|
7197
|
+
const arrayField = group.closest('[data-structured-array][data-field-name]');
|
|
7198
|
+
const arrayItem = group.closest('.structured-array-item');
|
|
7199
|
+
if (arrayField instanceof Element && arrayItem instanceof Element) {
|
|
7200
|
+
const arrayFieldName = arrayField.getAttribute('data-field-name') || 'unknown';
|
|
7201
|
+
const itemPosition = getItemPosition('.structured-array-item', arrayItem);
|
|
7202
|
+
const relativePath = stripIndexedFieldPrefix(fullFieldName, 'array-' + arrayFieldName + '-') || defaultGroupId;
|
|
7203
|
+
return scopePrefix + 'repeaters:' + arrayFieldName + ':' + itemPosition + ':' + relativePath;
|
|
7204
|
+
}
|
|
7205
|
+
|
|
7206
|
+
return scopePrefix + defaultGroupId;
|
|
7207
|
+
}
|
|
7208
|
+
|
|
7209
|
+
function loadFieldGroupState(group) {
|
|
7210
|
+
try {
|
|
7211
|
+
const value = sessionStorage.getItem(getFieldGroupStorageKey(group));
|
|
7212
|
+
if (value === '1') return true;
|
|
7213
|
+
if (value === '0') return false;
|
|
7214
|
+
} catch {}
|
|
7215
|
+
return null;
|
|
7216
|
+
}
|
|
7217
|
+
|
|
7218
|
+
function saveFieldGroupState(group, isCollapsed) {
|
|
7219
|
+
try {
|
|
7220
|
+
sessionStorage.setItem(getFieldGroupStorageKey(group), isCollapsed ? '1' : '0');
|
|
7221
|
+
} catch {}
|
|
7222
|
+
}
|
|
7223
|
+
|
|
7224
|
+
function resolveFieldGroupElements(groupOrId) {
|
|
7225
|
+
let group = null;
|
|
7226
|
+
|
|
7227
|
+
if (groupOrId instanceof Element) {
|
|
7228
|
+
group = groupOrId.classList.contains('field-group')
|
|
7229
|
+
? groupOrId
|
|
7230
|
+
: groupOrId.closest('.field-group[data-group-id]');
|
|
7231
|
+
} else if (typeof groupOrId === 'string' && groupOrId) {
|
|
7232
|
+
group = document.querySelector('.field-group[data-group-id="' + groupOrId + '"]');
|
|
7233
|
+
}
|
|
7234
|
+
|
|
7235
|
+
let content = null;
|
|
7236
|
+
let icon = null;
|
|
7237
|
+
|
|
7238
|
+
if (group instanceof Element) {
|
|
7239
|
+
content = group.querySelector(':scope > .field-group-content');
|
|
7240
|
+
icon = group.querySelector(':scope > .field-group-header svg[id$="-icon"]');
|
|
7241
|
+
}
|
|
7242
|
+
|
|
7243
|
+
// Legacy fallback for any existing calls still passing string IDs.
|
|
7244
|
+
if (!(content instanceof HTMLElement) && typeof groupOrId === 'string') {
|
|
7245
|
+
content = document.getElementById(groupOrId + '-content');
|
|
7246
|
+
}
|
|
7247
|
+
if (!(icon instanceof Element) && typeof groupOrId === 'string') {
|
|
7248
|
+
icon = document.getElementById(groupOrId + '-icon');
|
|
7249
|
+
}
|
|
7250
|
+
|
|
7251
|
+
if (!(group instanceof Element) && content instanceof Element) {
|
|
7252
|
+
group = content.closest('.field-group[data-group-id]');
|
|
7253
|
+
}
|
|
7254
|
+
|
|
7255
|
+
return { group, content, icon };
|
|
7256
|
+
}
|
|
7257
|
+
|
|
7258
|
+
function applyFieldGroupState(groupOrId, isCollapsed) {
|
|
7259
|
+
const { content, icon } = resolveFieldGroupElements(groupOrId);
|
|
7260
|
+
if (!(content instanceof HTMLElement) || !(icon instanceof Element)) return;
|
|
7261
|
+
content.classList.toggle('hidden', isCollapsed);
|
|
7262
|
+
icon.classList.toggle('-rotate-90', isCollapsed);
|
|
7263
|
+
}
|
|
7264
|
+
|
|
7265
|
+
function restoreFieldGroupStates() {
|
|
7266
|
+
document.querySelectorAll('.field-group[data-group-id]').forEach((group) => {
|
|
7267
|
+
const savedState = loadFieldGroupState(group);
|
|
7268
|
+
if (savedState === null) return;
|
|
7269
|
+
applyFieldGroupState(group, savedState);
|
|
7270
|
+
});
|
|
7271
|
+
}
|
|
7272
|
+
|
|
7273
|
+
function persistAllFieldGroupStates() {
|
|
7274
|
+
document.querySelectorAll('.field-group[data-group-id]').forEach((group) => {
|
|
7275
|
+
const { content } = resolveFieldGroupElements(group);
|
|
7276
|
+
if (!(content instanceof HTMLElement)) return;
|
|
7277
|
+
saveFieldGroupState(group, content.classList.contains('hidden'));
|
|
7278
|
+
});
|
|
7279
|
+
}
|
|
7280
|
+
|
|
7281
|
+
function setValidationHeaderIndicator(container) {
|
|
7282
|
+
if (!(container instanceof Element)) return;
|
|
7283
|
+
let header = null;
|
|
7284
|
+
let markerTarget = null;
|
|
7285
|
+
|
|
7286
|
+
if (container.classList.contains('field-group')) {
|
|
7287
|
+
header = container.querySelector(':scope > .field-group-header');
|
|
7288
|
+
markerTarget = container.querySelector(':scope > .field-group-header h3');
|
|
7289
|
+
} else if (container.classList.contains('structured-array-item')) {
|
|
7290
|
+
header = container.querySelector('[data-action="toggle-item"]');
|
|
7291
|
+
markerTarget = header;
|
|
7292
|
+
} else if (container.classList.contains('blocks-item')) {
|
|
7293
|
+
header = container.querySelector('[data-action="toggle-block"]');
|
|
7294
|
+
markerTarget = header;
|
|
7295
|
+
}
|
|
7296
|
+
|
|
7297
|
+
if (!(header instanceof HTMLElement)) return;
|
|
7298
|
+
if (!(markerTarget instanceof HTMLElement)) {
|
|
7299
|
+
markerTarget = header;
|
|
7300
|
+
}
|
|
7301
|
+
|
|
7302
|
+
header.dataset.validationHeaderError = 'true';
|
|
7303
|
+
header.classList.add('text-pink-700', 'dark:text-pink-300');
|
|
7304
|
+
|
|
7305
|
+
if (!markerTarget.querySelector('[data-validation-indicator]')) {
|
|
7306
|
+
const marker = document.createElement('span');
|
|
7307
|
+
marker.setAttribute('data-validation-indicator', 'true');
|
|
7308
|
+
marker.className = 'ml-2 inline-block h-2 w-2 rounded-full bg-pink-500 align-middle';
|
|
7309
|
+
marker.setAttribute('aria-hidden', 'true');
|
|
7310
|
+
markerTarget.appendChild(marker);
|
|
7311
|
+
}
|
|
7312
|
+
}
|
|
7313
|
+
|
|
7314
|
+
function clearValidationIndicators() {
|
|
7315
|
+
document.querySelectorAll('[data-validation-header-error="true"]').forEach((el) => {
|
|
7316
|
+
if (!(el instanceof HTMLElement)) return;
|
|
7317
|
+
delete el.dataset.validationHeaderError;
|
|
7318
|
+
el.classList.remove('text-pink-700', 'dark:text-pink-300');
|
|
7319
|
+
});
|
|
7320
|
+
|
|
7321
|
+
document.querySelectorAll('[data-validation-indicator]').forEach((el) => el.remove());
|
|
7322
|
+
}
|
|
7323
|
+
|
|
7324
|
+
function expandContainerForValidation(container) {
|
|
7325
|
+
if (!(container instanceof Element)) return;
|
|
7326
|
+
|
|
7327
|
+
if (container.classList.contains('field-group')) {
|
|
7328
|
+
applyFieldGroupState(container, false);
|
|
7329
|
+
return;
|
|
7330
|
+
}
|
|
7331
|
+
|
|
7332
|
+
if (container.classList.contains('structured-array-item')) {
|
|
7333
|
+
const content = container.querySelector('[data-array-item-fields]');
|
|
7334
|
+
const icon = container.querySelector('[data-item-toggle-icon]');
|
|
7335
|
+
if (content instanceof HTMLElement) {
|
|
7336
|
+
content.classList.remove('hidden');
|
|
7337
|
+
}
|
|
7338
|
+
if (icon instanceof Element) {
|
|
7339
|
+
icon.classList.remove('-rotate-90');
|
|
7340
|
+
}
|
|
7341
|
+
return;
|
|
7342
|
+
}
|
|
7343
|
+
|
|
7344
|
+
if (container.classList.contains('blocks-item')) {
|
|
7345
|
+
const content = container.querySelector('[data-block-content]');
|
|
7346
|
+
const icon = container.querySelector('[data-block-toggle-icon]');
|
|
7347
|
+
if (content instanceof HTMLElement) {
|
|
7348
|
+
content.classList.remove('hidden');
|
|
7349
|
+
}
|
|
7350
|
+
if (icon instanceof Element) {
|
|
7351
|
+
icon.classList.remove('-rotate-90');
|
|
7352
|
+
}
|
|
7353
|
+
}
|
|
7354
|
+
}
|
|
7355
|
+
|
|
7356
|
+
function walkErrorContainers(node, expand) {
|
|
7357
|
+
if (!(node instanceof Element)) return;
|
|
7358
|
+
const visited = new Set();
|
|
7359
|
+
let cursor = node;
|
|
7360
|
+
while (cursor) {
|
|
7361
|
+
const candidates = [
|
|
7362
|
+
cursor.closest('.structured-array-item'),
|
|
7363
|
+
cursor.closest('.blocks-item'),
|
|
7364
|
+
cursor.closest('.field-group[data-group-id]')
|
|
7365
|
+
].filter((c) => c instanceof Element && !visited.has(c));
|
|
7366
|
+
|
|
7367
|
+
if (candidates.length === 0) break;
|
|
7368
|
+
|
|
7369
|
+
// Pick nearest ancestor container to preserve "first-error path only".
|
|
7370
|
+
let nearest = candidates[0];
|
|
7371
|
+
let bestDistance = Number.MAX_SAFE_INTEGER;
|
|
7372
|
+
for (const candidate of candidates) {
|
|
7373
|
+
let distance = 0;
|
|
7374
|
+
let walker = cursor;
|
|
7375
|
+
while (walker && walker !== candidate) {
|
|
7376
|
+
walker = walker.parentElement;
|
|
7377
|
+
distance += 1;
|
|
7378
|
+
}
|
|
7379
|
+
if (distance < bestDistance) {
|
|
7380
|
+
bestDistance = distance;
|
|
7381
|
+
nearest = candidate;
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
|
|
7385
|
+
visited.add(nearest);
|
|
7386
|
+
setValidationHeaderIndicator(nearest);
|
|
7387
|
+
if (expand) {
|
|
7388
|
+
expandContainerForValidation(nearest);
|
|
7389
|
+
}
|
|
7390
|
+
cursor = nearest.parentElement;
|
|
7391
|
+
}
|
|
7392
|
+
}
|
|
7393
|
+
|
|
7394
|
+
function getFocusableTargetFromErrorGroup(group) {
|
|
7395
|
+
if (!(group instanceof Element)) return null;
|
|
7396
|
+
return (
|
|
7397
|
+
group.querySelector('input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), [contenteditable="true"]') ||
|
|
7398
|
+
group.querySelector('button:not([disabled])')
|
|
7399
|
+
);
|
|
7400
|
+
}
|
|
7401
|
+
|
|
7402
|
+
function revealServerValidationErrors() {
|
|
7403
|
+
clearValidationIndicators();
|
|
7404
|
+
|
|
7405
|
+
const errorGroups = Array.from(document.querySelectorAll('.form-group[data-has-errors="true"]'));
|
|
7406
|
+
if (errorGroups.length === 0) return;
|
|
7407
|
+
|
|
7408
|
+
// Add indicators for all errored sections, expand only first-error path.
|
|
7409
|
+
errorGroups.forEach((group, index) => {
|
|
7410
|
+
walkErrorContainers(group, index === 0);
|
|
7411
|
+
});
|
|
7412
|
+
|
|
7413
|
+
const firstTarget = getFocusableTargetFromErrorGroup(errorGroups[0]);
|
|
7414
|
+
if (firstTarget instanceof HTMLElement) {
|
|
7415
|
+
firstTarget.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
7416
|
+
firstTarget.focus({ preventScroll: true });
|
|
7417
|
+
}
|
|
7418
|
+
}
|
|
7419
|
+
|
|
7420
|
+
function revealNativeValidationErrors(form) {
|
|
7421
|
+
if (!(form instanceof HTMLFormElement)) return;
|
|
7422
|
+
clearValidationIndicators();
|
|
7423
|
+
|
|
7424
|
+
const invalidControls = Array.from(form.querySelectorAll(':invalid'));
|
|
7425
|
+
if (invalidControls.length === 0) return;
|
|
7426
|
+
|
|
7427
|
+
invalidControls.forEach((control, index) => {
|
|
7428
|
+
walkErrorContainers(control, index === 0);
|
|
7429
|
+
});
|
|
7430
|
+
|
|
7431
|
+
const first = invalidControls[0];
|
|
7432
|
+
if (first instanceof HTMLElement) {
|
|
7433
|
+
first.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
7434
|
+
first.focus({ preventScroll: true });
|
|
7435
|
+
}
|
|
7436
|
+
}
|
|
7437
|
+
|
|
6672
7438
|
// Field group toggle
|
|
6673
|
-
function toggleFieldGroup(
|
|
6674
|
-
const content =
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
7439
|
+
function toggleFieldGroup(groupOrTrigger) {
|
|
7440
|
+
const { group, content } = resolveFieldGroupElements(groupOrTrigger);
|
|
7441
|
+
if (!(group instanceof Element)) return;
|
|
7442
|
+
if (!(content instanceof HTMLElement)) return;
|
|
7443
|
+
|
|
7444
|
+
const isCollapsed = !content.classList.contains('hidden');
|
|
7445
|
+
applyFieldGroupState(group, isCollapsed);
|
|
7446
|
+
saveFieldGroupState(group, isCollapsed);
|
|
7447
|
+
}
|
|
7448
|
+
|
|
7449
|
+
if (document.readyState === 'loading') {
|
|
7450
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
7451
|
+
restoreFieldGroupStates();
|
|
7452
|
+
const form = document.getElementById('content-form');
|
|
7453
|
+
if (form?.getAttribute('data-has-validation-errors') === 'true') {
|
|
7454
|
+
revealServerValidationErrors();
|
|
7455
|
+
}
|
|
7456
|
+
});
|
|
7457
|
+
} else {
|
|
7458
|
+
restoreFieldGroupStates();
|
|
7459
|
+
const form = document.getElementById('content-form');
|
|
7460
|
+
if (form?.getAttribute('data-has-validation-errors') === 'true') {
|
|
7461
|
+
revealServerValidationErrors();
|
|
6683
7462
|
}
|
|
6684
7463
|
}
|
|
6685
7464
|
|
|
7465
|
+
document.addEventListener('htmx:afterSwap', function() {
|
|
7466
|
+
setTimeout(() => {
|
|
7467
|
+
restoreFieldGroupStates();
|
|
7468
|
+
const form = document.getElementById('content-form');
|
|
7469
|
+
if (form?.getAttribute('data-has-validation-errors') === 'true') {
|
|
7470
|
+
revealServerValidationErrors();
|
|
7471
|
+
}
|
|
7472
|
+
}, 50);
|
|
7473
|
+
});
|
|
7474
|
+
|
|
7475
|
+
const contentFormEl = document.getElementById('content-form');
|
|
7476
|
+
if (contentFormEl instanceof HTMLFormElement) {
|
|
7477
|
+
contentFormEl.addEventListener('submit', () => {
|
|
7478
|
+
persistAllFieldGroupStates();
|
|
7479
|
+
}, true);
|
|
7480
|
+
}
|
|
7481
|
+
|
|
7482
|
+
window.addEventListener('beforeunload', () => {
|
|
7483
|
+
persistAllFieldGroupStates();
|
|
7484
|
+
});
|
|
7485
|
+
|
|
7486
|
+
document.addEventListener('visibilitychange', () => {
|
|
7487
|
+
if (document.visibilityState === 'hidden') {
|
|
7488
|
+
persistAllFieldGroupStates();
|
|
7489
|
+
}
|
|
7490
|
+
});
|
|
7491
|
+
|
|
7492
|
+
let pendingNativeValidationReveal = false;
|
|
7493
|
+
document.addEventListener('invalid', function(event) {
|
|
7494
|
+
const target = event.target;
|
|
7495
|
+
if (!(target instanceof Element)) return;
|
|
7496
|
+
const form = target.closest('form');
|
|
7497
|
+
if (!(form instanceof HTMLFormElement)) return;
|
|
7498
|
+
|
|
7499
|
+
if (pendingNativeValidationReveal) return;
|
|
7500
|
+
pendingNativeValidationReveal = true;
|
|
7501
|
+
|
|
7502
|
+
// Expand only first invalid path synchronously so the browser can focus it
|
|
7503
|
+
// and avoid "invalid form control is not focusable" errors.
|
|
7504
|
+
walkErrorContainers(target, true);
|
|
7505
|
+
|
|
7506
|
+
setTimeout(() => {
|
|
7507
|
+
pendingNativeValidationReveal = false;
|
|
7508
|
+
revealNativeValidationErrors(form);
|
|
7509
|
+
}, 0);
|
|
7510
|
+
}, true);
|
|
7511
|
+
|
|
6686
7512
|
// Media field functions
|
|
6687
|
-
|
|
7513
|
+
function notifyFieldChange(input) {
|
|
7514
|
+
if (!input) return;
|
|
7515
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
7516
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
7517
|
+
}
|
|
7518
|
+
|
|
7519
|
+
function getActiveMediaModal() {
|
|
7520
|
+
const modal = document.getElementById('media-selector-modal');
|
|
7521
|
+
return modal instanceof HTMLElement ? modal : null;
|
|
7522
|
+
}
|
|
7523
|
+
|
|
7524
|
+
function getMediaFieldElements(fieldId) {
|
|
7525
|
+
if (!fieldId) {
|
|
7526
|
+
return {
|
|
7527
|
+
fieldId: '',
|
|
7528
|
+
hiddenInput: null,
|
|
7529
|
+
preview: null,
|
|
7530
|
+
mediaField: null,
|
|
7531
|
+
actionsDiv: null,
|
|
7532
|
+
};
|
|
7533
|
+
}
|
|
7534
|
+
|
|
7535
|
+
const hiddenInput = document.getElementById(fieldId);
|
|
7536
|
+
const preview = document.getElementById(fieldId + '-preview');
|
|
7537
|
+
const mediaField = hiddenInput?.closest('.media-field-container') || null;
|
|
7538
|
+
const actionsDiv = mediaField?.querySelector('.media-actions') || null;
|
|
7539
|
+
|
|
7540
|
+
return {
|
|
7541
|
+
fieldId,
|
|
7542
|
+
hiddenInput,
|
|
7543
|
+
preview,
|
|
7544
|
+
mediaField,
|
|
7545
|
+
actionsDiv,
|
|
7546
|
+
};
|
|
7547
|
+
}
|
|
7548
|
+
|
|
7549
|
+
function getActiveMediaTarget() {
|
|
7550
|
+
const modal = getActiveMediaModal();
|
|
7551
|
+
const fieldId = modal?.dataset.targetFieldId || '';
|
|
7552
|
+
return {
|
|
7553
|
+
modal,
|
|
7554
|
+
originalValue: modal?.dataset.originalValue || '',
|
|
7555
|
+
...getMediaFieldElements(fieldId),
|
|
7556
|
+
};
|
|
7557
|
+
}
|
|
7558
|
+
|
|
7559
|
+
function ensureSingleMediaRemoveButton(fieldId, actionsDiv) {
|
|
7560
|
+
if (!(actionsDiv instanceof HTMLElement)) return;
|
|
7561
|
+
const existingRemoveButton = actionsDiv.querySelector('[data-media-remove="true"]');
|
|
7562
|
+
if (existingRemoveButton) return;
|
|
7563
|
+
|
|
7564
|
+
const removeBtn = document.createElement('button');
|
|
7565
|
+
removeBtn.type = 'button';
|
|
7566
|
+
removeBtn.setAttribute('data-media-remove', 'true');
|
|
7567
|
+
removeBtn.onclick = () => clearMediaField(fieldId);
|
|
7568
|
+
removeBtn.className = 'inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-xl hover:bg-red-700 transition-all';
|
|
7569
|
+
removeBtn.textContent = 'Remove';
|
|
7570
|
+
actionsDiv.appendChild(removeBtn);
|
|
7571
|
+
}
|
|
6688
7572
|
|
|
6689
7573
|
function openMediaSelector(fieldId) {
|
|
6690
|
-
|
|
7574
|
+
const existingModal = getActiveMediaModal();
|
|
7575
|
+
if (existingModal) {
|
|
7576
|
+
existingModal.remove();
|
|
7577
|
+
}
|
|
7578
|
+
|
|
6691
7579
|
// Store the original value in case user cancels
|
|
6692
|
-
const originalValue =
|
|
7580
|
+
const originalValue = getMediaFieldElements(fieldId).hiddenInput?.value || '';
|
|
6693
7581
|
|
|
6694
7582
|
// Open media library modal
|
|
6695
7583
|
const modal = document.createElement('div');
|
|
6696
7584
|
modal.className = 'fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50';
|
|
6697
7585
|
modal.id = 'media-selector-modal';
|
|
7586
|
+
modal.dataset.targetFieldId = fieldId;
|
|
7587
|
+
modal.dataset.originalValue = originalValue;
|
|
6698
7588
|
modal.innerHTML = \`
|
|
6699
7589
|
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-xl ring-1 ring-zinc-950/5 dark:ring-white/10 p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto">
|
|
6700
7590
|
<h3 class="text-lg font-semibold text-zinc-950 dark:text-white mb-4">Select Media</h3>
|
|
6701
7591
|
<div id="media-grid-container" hx-get="/admin/media/selector" hx-trigger="load"></div>
|
|
6702
7592
|
<div class="mt-4 flex justify-end space-x-2">
|
|
6703
7593
|
<button
|
|
6704
|
-
onclick="cancelMediaSelection(
|
|
7594
|
+
onclick="cancelMediaSelection()"
|
|
6705
7595
|
class="rounded-lg bg-white dark:bg-zinc-800 px-4 py-2 text-sm font-semibold text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors">
|
|
6706
7596
|
Cancel
|
|
6707
7597
|
</button>
|
|
@@ -6721,23 +7611,23 @@ function renderContentFormPage(data) {
|
|
|
6721
7611
|
}
|
|
6722
7612
|
|
|
6723
7613
|
function closeMediaSelector() {
|
|
6724
|
-
const modal =
|
|
7614
|
+
const modal = getActiveMediaModal();
|
|
6725
7615
|
if (modal) {
|
|
6726
7616
|
modal.remove();
|
|
6727
7617
|
}
|
|
6728
|
-
currentMediaFieldId = null;
|
|
6729
7618
|
}
|
|
6730
7619
|
|
|
6731
|
-
function cancelMediaSelection(
|
|
7620
|
+
function cancelMediaSelection() {
|
|
7621
|
+
const { hiddenInput, preview, originalValue } = getActiveMediaTarget();
|
|
7622
|
+
|
|
6732
7623
|
// Restore original value
|
|
6733
|
-
const hiddenInput = document.getElementById(fieldId);
|
|
6734
7624
|
if (hiddenInput) {
|
|
6735
7625
|
hiddenInput.value = originalValue;
|
|
7626
|
+
notifyFieldChange(hiddenInput);
|
|
6736
7627
|
}
|
|
6737
7628
|
|
|
6738
7629
|
// If original value was empty, hide the preview and show select button
|
|
6739
7630
|
if (!originalValue) {
|
|
6740
|
-
const preview = document.getElementById(fieldId + '-preview');
|
|
6741
7631
|
if (preview) {
|
|
6742
7632
|
preview.classList.add('hidden');
|
|
6743
7633
|
}
|
|
@@ -6748,11 +7638,11 @@ function renderContentFormPage(data) {
|
|
|
6748
7638
|
}
|
|
6749
7639
|
|
|
6750
7640
|
function clearMediaField(fieldId) {
|
|
6751
|
-
const hiddenInput =
|
|
6752
|
-
const preview = document.getElementById(fieldId + '-preview');
|
|
7641
|
+
const { hiddenInput, preview, actionsDiv } = getMediaFieldElements(fieldId);
|
|
6753
7642
|
|
|
6754
7643
|
if (hiddenInput) {
|
|
6755
7644
|
hiddenInput.value = '';
|
|
7645
|
+
notifyFieldChange(hiddenInput);
|
|
6756
7646
|
}
|
|
6757
7647
|
|
|
6758
7648
|
if (preview) {
|
|
@@ -6762,25 +7652,34 @@ function renderContentFormPage(data) {
|
|
|
6762
7652
|
}
|
|
6763
7653
|
preview.classList.add('hidden');
|
|
6764
7654
|
}
|
|
7655
|
+
|
|
7656
|
+
const removeButton = actionsDiv?.querySelector('[data-media-remove="true"]');
|
|
7657
|
+
if (removeButton) {
|
|
7658
|
+
removeButton.remove();
|
|
7659
|
+
}
|
|
6765
7660
|
}
|
|
6766
7661
|
|
|
6767
7662
|
// Global function to remove a single media from multiple selection
|
|
6768
7663
|
window.removeMediaFromMultiple = function(fieldId, urlToRemove) {
|
|
6769
|
-
const hiddenInput =
|
|
7664
|
+
const { hiddenInput, preview } = getMediaFieldElements(fieldId);
|
|
6770
7665
|
if (!hiddenInput) return;
|
|
6771
7666
|
|
|
6772
7667
|
const values = hiddenInput.value.split(',').filter(url => url !== urlToRemove);
|
|
6773
7668
|
hiddenInput.value = values.join(',');
|
|
7669
|
+
notifyFieldChange(hiddenInput);
|
|
6774
7670
|
|
|
6775
7671
|
// Remove preview item
|
|
6776
|
-
const previewItem =
|
|
7672
|
+
const previewItem =
|
|
7673
|
+
preview &&
|
|
7674
|
+
Array.from(preview.querySelectorAll('[data-url]')).find(
|
|
7675
|
+
(item) => item.getAttribute('data-url') === urlToRemove,
|
|
7676
|
+
);
|
|
6777
7677
|
if (previewItem) {
|
|
6778
7678
|
previewItem.remove();
|
|
6779
7679
|
}
|
|
6780
7680
|
|
|
6781
7681
|
// Hide preview grid if empty
|
|
6782
7682
|
if (values.length === 0) {
|
|
6783
|
-
const preview = document.getElementById(fieldId + '-preview');
|
|
6784
7683
|
if (preview) {
|
|
6785
7684
|
preview.classList.add('hidden');
|
|
6786
7685
|
}
|
|
@@ -6789,39 +7688,24 @@ function renderContentFormPage(data) {
|
|
|
6789
7688
|
|
|
6790
7689
|
// Global function called by media selector buttons
|
|
6791
7690
|
window.selectMediaFile = function(mediaId, mediaUrl, filename) {
|
|
6792
|
-
|
|
7691
|
+
const { fieldId, hiddenInput, preview, actionsDiv } = getActiveMediaTarget();
|
|
7692
|
+
if (!fieldId || !hiddenInput) {
|
|
6793
7693
|
console.error('No field ID set for media selection');
|
|
6794
7694
|
return;
|
|
6795
7695
|
}
|
|
6796
7696
|
|
|
6797
|
-
const fieldId = currentMediaFieldId;
|
|
6798
|
-
|
|
6799
7697
|
// Set the hidden input value to the media URL (not ID)
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
hiddenInput.value = mediaUrl;
|
|
6803
|
-
}
|
|
7698
|
+
hiddenInput.value = mediaUrl;
|
|
7699
|
+
notifyFieldChange(hiddenInput);
|
|
6804
7700
|
|
|
6805
7701
|
// Update the preview
|
|
6806
|
-
const preview = document.getElementById(fieldId + '-preview');
|
|
6807
7702
|
if (preview) {
|
|
6808
7703
|
preview.innerHTML = \`<img src="\${mediaUrl}" alt="\${filename}" class="w-32 h-32 object-cover rounded-lg border border-white/20">\`;
|
|
6809
7704
|
preview.classList.remove('hidden');
|
|
6810
7705
|
}
|
|
6811
7706
|
|
|
6812
7707
|
// Show the remove button by finding the media actions container and updating it
|
|
6813
|
-
|
|
6814
|
-
if (mediaField) {
|
|
6815
|
-
const actionsDiv = mediaField.querySelector('.media-actions');
|
|
6816
|
-
if (actionsDiv && !actionsDiv.querySelector('button:has-text("Remove")')) {
|
|
6817
|
-
const removeBtn = document.createElement('button');
|
|
6818
|
-
removeBtn.type = 'button';
|
|
6819
|
-
removeBtn.onclick = () => clearMediaField(fieldId);
|
|
6820
|
-
removeBtn.className = 'inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-xl hover:bg-red-700 transition-all';
|
|
6821
|
-
removeBtn.textContent = 'Remove';
|
|
6822
|
-
actionsDiv.appendChild(removeBtn);
|
|
6823
|
-
}
|
|
6824
|
-
}
|
|
7708
|
+
ensureSingleMediaRemoveButton(fieldId, actionsDiv);
|
|
6825
7709
|
|
|
6826
7710
|
// DON'T close the modal - let user click OK button
|
|
6827
7711
|
// Visual feedback: highlight the selected item
|
|
@@ -6835,7 +7719,9 @@ function renderContentFormPage(data) {
|
|
|
6835
7719
|
};
|
|
6836
7720
|
|
|
6837
7721
|
function setMediaField(fieldId, mediaUrl) {
|
|
6838
|
-
document.getElementById(fieldId)
|
|
7722
|
+
const hiddenInput = document.getElementById(fieldId);
|
|
7723
|
+
hiddenInput.value = mediaUrl;
|
|
7724
|
+
notifyFieldChange(hiddenInput);
|
|
6839
7725
|
const preview = document.getElementById(fieldId + '-preview');
|
|
6840
7726
|
preview.innerHTML = \`<img src="\${mediaUrl}" alt="Selected media" class="w-32 h-32 object-cover rounded-lg ring-1 ring-zinc-950/10 dark:ring-white/10">\`;
|
|
6841
7727
|
preview.classList.remove('hidden');
|
|
@@ -8242,15 +9128,49 @@ function renderContentListPage(data) {
|
|
|
8242
9128
|
return chunkLTKV7AE5_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
8243
9129
|
}
|
|
8244
9130
|
|
|
9131
|
+
// src/routes/admin-content-field-types.ts
|
|
9132
|
+
function resolveSchemaFieldType(fieldConfig) {
|
|
9133
|
+
if (fieldConfig.type === "slug" || fieldConfig.format === "slug") {
|
|
9134
|
+
return "slug";
|
|
9135
|
+
}
|
|
9136
|
+
if (fieldConfig.type && fieldConfig.type !== "string") {
|
|
9137
|
+
return fieldConfig.type;
|
|
9138
|
+
}
|
|
9139
|
+
if (fieldConfig.format === "richtext") {
|
|
9140
|
+
return "richtext";
|
|
9141
|
+
}
|
|
9142
|
+
if (fieldConfig.format === "media") {
|
|
9143
|
+
return "media";
|
|
9144
|
+
}
|
|
9145
|
+
if (fieldConfig.format === "date-time") {
|
|
9146
|
+
return "date";
|
|
9147
|
+
}
|
|
9148
|
+
if (Array.isArray(fieldConfig.enum)) {
|
|
9149
|
+
return "select";
|
|
9150
|
+
}
|
|
9151
|
+
return fieldConfig.type || "string";
|
|
9152
|
+
}
|
|
9153
|
+
function buildSchemaFieldOptions(fieldConfig) {
|
|
9154
|
+
const fieldOptions = { ...fieldConfig };
|
|
9155
|
+
const resolvedFieldType = resolveSchemaFieldType(fieldConfig);
|
|
9156
|
+
if (resolvedFieldType === "select" && Array.isArray(fieldConfig.enum)) {
|
|
9157
|
+
fieldOptions.options = fieldConfig.enum.map((value, index) => ({
|
|
9158
|
+
value,
|
|
9159
|
+
label: fieldConfig.enumLabels?.[index] || value
|
|
9160
|
+
}));
|
|
9161
|
+
}
|
|
9162
|
+
return fieldOptions;
|
|
9163
|
+
}
|
|
9164
|
+
|
|
8245
9165
|
// src/routes/admin-content.ts
|
|
8246
9166
|
var adminContentRoutes = new hono.Hono();
|
|
8247
9167
|
function parseFieldValue(field, formData, options = {}) {
|
|
8248
9168
|
const { skipValidation = false } = options;
|
|
8249
9169
|
const value = formData.get(field.field_name);
|
|
8250
9170
|
const errors = [];
|
|
8251
|
-
const blocksConfig =
|
|
9171
|
+
const blocksConfig = chunkEKPLKUZT_cjs.getBlocksFieldConfig(field.field_options);
|
|
8252
9172
|
if (blocksConfig) {
|
|
8253
|
-
const parsed =
|
|
9173
|
+
const parsed = chunkEKPLKUZT_cjs.parseBlocksValue(value, blocksConfig);
|
|
8254
9174
|
if (!skipValidation && field.is_required && parsed.value.length === 0) {
|
|
8255
9175
|
parsed.errors.push(`${field.field_label} is required`);
|
|
8256
9176
|
}
|
|
@@ -8360,9 +9280,9 @@ function extractFieldData(fields, formData, options = {}) {
|
|
|
8360
9280
|
}
|
|
8361
9281
|
return { data, errors };
|
|
8362
9282
|
}
|
|
8363
|
-
adminContentRoutes.use("*",
|
|
9283
|
+
adminContentRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
8364
9284
|
async function getCollectionFields(db, collectionId) {
|
|
8365
|
-
const cache =
|
|
9285
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.collection);
|
|
8366
9286
|
return cache.getOrSet(
|
|
8367
9287
|
cache.generateKey("fields", collectionId),
|
|
8368
9288
|
async () => {
|
|
@@ -8374,17 +9294,11 @@ async function getCollectionFields(db, collectionId) {
|
|
|
8374
9294
|
if (schema && schema.properties) {
|
|
8375
9295
|
let fieldOrder = 0;
|
|
8376
9296
|
return Object.entries(schema.properties).map(([fieldName, fieldConfig]) => {
|
|
8377
|
-
|
|
8378
|
-
if (fieldConfig.type === "select" && fieldConfig.enum) {
|
|
8379
|
-
fieldOptions.options = fieldConfig.enum.map((value, index) => ({
|
|
8380
|
-
value,
|
|
8381
|
-
label: fieldConfig.enumLabels?.[index] || value
|
|
8382
|
-
}));
|
|
8383
|
-
}
|
|
9297
|
+
const fieldOptions = buildSchemaFieldOptions(fieldConfig);
|
|
8384
9298
|
return {
|
|
8385
9299
|
id: `schema-${fieldName}`,
|
|
8386
9300
|
field_name: fieldName,
|
|
8387
|
-
field_type: fieldConfig
|
|
9301
|
+
field_type: resolveSchemaFieldType(fieldConfig),
|
|
8388
9302
|
field_label: fieldConfig.title || fieldName,
|
|
8389
9303
|
field_options: fieldOptions,
|
|
8390
9304
|
field_order: fieldOrder++,
|
|
@@ -8417,7 +9331,7 @@ async function getCollectionFields(db, collectionId) {
|
|
|
8417
9331
|
);
|
|
8418
9332
|
}
|
|
8419
9333
|
async function getCollection(db, collectionId) {
|
|
8420
|
-
const cache =
|
|
9334
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.collection);
|
|
8421
9335
|
return cache.getOrSet(
|
|
8422
9336
|
cache.generateKey("collection", collectionId),
|
|
8423
9337
|
async () => {
|
|
@@ -8642,21 +9556,21 @@ adminContentRoutes.get("/new", async (c) => {
|
|
|
8642
9556
|
const tinymceEnabled = await isPluginActive2(db, "tinymce-plugin");
|
|
8643
9557
|
let tinymceSettings;
|
|
8644
9558
|
if (tinymceEnabled) {
|
|
8645
|
-
const pluginService = new
|
|
9559
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
8646
9560
|
const tinymcePlugin2 = await pluginService.getPlugin("tinymce-plugin");
|
|
8647
9561
|
tinymceSettings = tinymcePlugin2?.settings;
|
|
8648
9562
|
}
|
|
8649
9563
|
const quillEnabled = await isPluginActive2(db, "quill-editor");
|
|
8650
9564
|
let quillSettings;
|
|
8651
9565
|
if (quillEnabled) {
|
|
8652
|
-
const pluginService = new
|
|
9566
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
8653
9567
|
const quillPlugin = await pluginService.getPlugin("quill-editor");
|
|
8654
9568
|
quillSettings = quillPlugin?.settings;
|
|
8655
9569
|
}
|
|
8656
9570
|
const mdxeditorEnabled = await isPluginActive2(db, "easy-mdx");
|
|
8657
9571
|
let mdxeditorSettings;
|
|
8658
9572
|
if (mdxeditorEnabled) {
|
|
8659
|
-
const pluginService = new
|
|
9573
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
8660
9574
|
const mdxeditorPlugin = await pluginService.getPlugin("easy-mdx");
|
|
8661
9575
|
mdxeditorSettings = mdxeditorPlugin?.settings;
|
|
8662
9576
|
}
|
|
@@ -8706,7 +9620,7 @@ adminContentRoutes.get("/:id/edit", async (c) => {
|
|
|
8706
9620
|
const db = c.env.DB;
|
|
8707
9621
|
const url = new URL(c.req.url);
|
|
8708
9622
|
const referrerParams = url.searchParams.get("ref") || "";
|
|
8709
|
-
const cache =
|
|
9623
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
|
|
8710
9624
|
const content = await cache.getOrSet(
|
|
8711
9625
|
cache.generateKey("content", id),
|
|
8712
9626
|
async () => {
|
|
@@ -8747,21 +9661,21 @@ adminContentRoutes.get("/:id/edit", async (c) => {
|
|
|
8747
9661
|
const tinymceEnabled = await isPluginActive2(db, "tinymce-plugin");
|
|
8748
9662
|
let tinymceSettings;
|
|
8749
9663
|
if (tinymceEnabled) {
|
|
8750
|
-
const pluginService = new
|
|
9664
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
8751
9665
|
const tinymcePlugin2 = await pluginService.getPlugin("tinymce-plugin");
|
|
8752
9666
|
tinymceSettings = tinymcePlugin2?.settings;
|
|
8753
9667
|
}
|
|
8754
9668
|
const quillEnabled = await isPluginActive2(db, "quill-editor");
|
|
8755
9669
|
let quillSettings;
|
|
8756
9670
|
if (quillEnabled) {
|
|
8757
|
-
const pluginService = new
|
|
9671
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
8758
9672
|
const quillPlugin = await pluginService.getPlugin("quill-editor");
|
|
8759
9673
|
quillSettings = quillPlugin?.settings;
|
|
8760
9674
|
}
|
|
8761
9675
|
const mdxeditorEnabled = await isPluginActive2(db, "easy-mdx");
|
|
8762
9676
|
let mdxeditorSettings;
|
|
8763
9677
|
if (mdxeditorEnabled) {
|
|
8764
|
-
const pluginService = new
|
|
9678
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
8765
9679
|
const mdxeditorPlugin = await pluginService.getPlugin("easy-mdx");
|
|
8766
9680
|
mdxeditorSettings = mdxeditorPlugin?.settings;
|
|
8767
9681
|
}
|
|
@@ -8882,7 +9796,7 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
8882
9796
|
now,
|
|
8883
9797
|
now
|
|
8884
9798
|
).run();
|
|
8885
|
-
const cache =
|
|
9799
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
|
|
8886
9800
|
await cache.invalidate(`content:list:${collectionId}:*`);
|
|
8887
9801
|
const versionStmt = db.prepare(`
|
|
8888
9802
|
INSERT INTO content_versions (id, content_id, version, data, author_id, created_at)
|
|
@@ -9001,7 +9915,7 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
9001
9915
|
now,
|
|
9002
9916
|
id
|
|
9003
9917
|
).run();
|
|
9004
|
-
const cache =
|
|
9918
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
|
|
9005
9919
|
await cache.delete(cache.generateKey("content", id));
|
|
9006
9920
|
await cache.invalidate(`content:list:${existingContent.collection_id}:*`);
|
|
9007
9921
|
const existingData = JSON.parse(existingContent.data || "{}");
|
|
@@ -9056,7 +9970,7 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
9056
9970
|
`);
|
|
9057
9971
|
}
|
|
9058
9972
|
});
|
|
9059
|
-
adminContentRoutes.post("/preview",
|
|
9973
|
+
adminContentRoutes.post("/preview", chunkIT2TC4ZD_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
|
|
9060
9974
|
try {
|
|
9061
9975
|
const formData = await c.req.formData();
|
|
9062
9976
|
const collectionId = formData.get("collection_id");
|
|
@@ -9278,7 +10192,7 @@ adminContentRoutes.post("/bulk-action", async (c) => {
|
|
|
9278
10192
|
} else {
|
|
9279
10193
|
return c.json({ success: false, error: "Invalid action" });
|
|
9280
10194
|
}
|
|
9281
|
-
const cache =
|
|
10195
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
|
|
9282
10196
|
for (const contentId of ids) {
|
|
9283
10197
|
await cache.delete(cache.generateKey("content", contentId));
|
|
9284
10198
|
}
|
|
@@ -9306,7 +10220,7 @@ adminContentRoutes.delete("/:id", async (c) => {
|
|
|
9306
10220
|
WHERE id = ?
|
|
9307
10221
|
`);
|
|
9308
10222
|
await deleteStmt.bind(now, id).run();
|
|
9309
|
-
const cache =
|
|
10223
|
+
const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
|
|
9310
10224
|
await cache.delete(cache.generateKey("content", id));
|
|
9311
10225
|
await cache.invalidate("content:list:*");
|
|
9312
10226
|
return c.html(`
|
|
@@ -9434,7 +10348,7 @@ adminContentRoutes.post("/:id/restore/:version", async (c) => {
|
|
|
9434
10348
|
return c.json({ success: false, error: "Failed to restore version" });
|
|
9435
10349
|
}
|
|
9436
10350
|
});
|
|
9437
|
-
adminContentRoutes.get("/:id/version/:version/preview",
|
|
10351
|
+
adminContentRoutes.get("/:id/version/:version/preview", chunkIT2TC4ZD_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
|
|
9438
10352
|
try {
|
|
9439
10353
|
const id = c.req.param("id");
|
|
9440
10354
|
const version = parseInt(c.req.param("version") || "0");
|
|
@@ -11401,7 +12315,14 @@ function renderUsersListPage(data) {
|
|
|
11401
12315
|
|
|
11402
12316
|
// src/routes/admin-users.ts
|
|
11403
12317
|
var userRoutes = new hono.Hono();
|
|
11404
|
-
userRoutes.use("*",
|
|
12318
|
+
userRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
12319
|
+
userRoutes.use("/users/*", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
12320
|
+
userRoutes.use("/users", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
12321
|
+
userRoutes.use("/invite-user", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
12322
|
+
userRoutes.use("/resend-invitation/*", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
12323
|
+
userRoutes.use("/cancel-invitation/*", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
12324
|
+
userRoutes.use("/activity-logs", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
12325
|
+
userRoutes.use("/activity-logs/*", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
11405
12326
|
userRoutes.get("/", (c) => {
|
|
11406
12327
|
return c.redirect("/admin/dashboard");
|
|
11407
12328
|
});
|
|
@@ -11556,7 +12477,7 @@ userRoutes.put("/profile", async (c) => {
|
|
|
11556
12477
|
Date.now(),
|
|
11557
12478
|
user.userId
|
|
11558
12479
|
).run();
|
|
11559
|
-
await
|
|
12480
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
11560
12481
|
db,
|
|
11561
12482
|
user.userId,
|
|
11562
12483
|
"profile.update",
|
|
@@ -11619,7 +12540,7 @@ userRoutes.post("/profile/avatar", async (c) => {
|
|
|
11619
12540
|
SELECT first_name, last_name FROM users WHERE id = ?
|
|
11620
12541
|
`);
|
|
11621
12542
|
const userData = await userStmt.bind(user.userId).first();
|
|
11622
|
-
await
|
|
12543
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
11623
12544
|
db,
|
|
11624
12545
|
user.userId,
|
|
11625
12546
|
"profile.avatar_update",
|
|
@@ -11690,7 +12611,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
11690
12611
|
dismissible: true
|
|
11691
12612
|
}));
|
|
11692
12613
|
}
|
|
11693
|
-
const validPassword = await
|
|
12614
|
+
const validPassword = await chunkIT2TC4ZD_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
|
|
11694
12615
|
if (!validPassword) {
|
|
11695
12616
|
return c.html(renderAlert2({
|
|
11696
12617
|
type: "error",
|
|
@@ -11698,7 +12619,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
11698
12619
|
dismissible: true
|
|
11699
12620
|
}));
|
|
11700
12621
|
}
|
|
11701
|
-
const newPasswordHash = await
|
|
12622
|
+
const newPasswordHash = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword(newPassword);
|
|
11702
12623
|
const historyStmt = db.prepare(`
|
|
11703
12624
|
INSERT INTO password_history (id, user_id, password_hash, created_at)
|
|
11704
12625
|
VALUES (?, ?, ?, ?)
|
|
@@ -11714,7 +12635,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
11714
12635
|
WHERE id = ?
|
|
11715
12636
|
`);
|
|
11716
12637
|
await updateStmt.bind(newPasswordHash, Date.now(), user.userId).run();
|
|
11717
|
-
await
|
|
12638
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
11718
12639
|
db,
|
|
11719
12640
|
user.userId,
|
|
11720
12641
|
"profile.password_change",
|
|
@@ -11781,7 +12702,7 @@ userRoutes.get("/users", async (c) => {
|
|
|
11781
12702
|
`);
|
|
11782
12703
|
const countResult = await countStmt.bind(...params).first();
|
|
11783
12704
|
const totalUsers = countResult?.total || 0;
|
|
11784
|
-
await
|
|
12705
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
11785
12706
|
db,
|
|
11786
12707
|
user.userId,
|
|
11787
12708
|
"users.list_view",
|
|
@@ -11889,7 +12810,9 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
11889
12810
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
11890
12811
|
const phone = chunkMNWKYY5E_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
11891
12812
|
const bio = chunkMNWKYY5E_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
11892
|
-
const
|
|
12813
|
+
const roleInput = formData.get("role")?.toString() || "viewer";
|
|
12814
|
+
const validRoles = ["admin", "editor", "author", "viewer"];
|
|
12815
|
+
const role = validRoles.includes(roleInput) ? roleInput : "viewer";
|
|
11893
12816
|
const password = formData.get("password")?.toString() || "";
|
|
11894
12817
|
const confirmPassword = formData.get("confirm_password")?.toString() || "";
|
|
11895
12818
|
const isActive = formData.get("is_active") === "1";
|
|
@@ -11935,7 +12858,7 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
11935
12858
|
dismissible: true
|
|
11936
12859
|
}));
|
|
11937
12860
|
}
|
|
11938
|
-
const passwordHash = await
|
|
12861
|
+
const passwordHash = await chunkIT2TC4ZD_cjs.AuthManager.hashPassword(password);
|
|
11939
12862
|
const userId = crypto.randomUUID();
|
|
11940
12863
|
const createStmt = db.prepare(`
|
|
11941
12864
|
INSERT INTO users (
|
|
@@ -11958,7 +12881,7 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
11958
12881
|
Date.now(),
|
|
11959
12882
|
Date.now()
|
|
11960
12883
|
).run();
|
|
11961
|
-
await
|
|
12884
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
11962
12885
|
db,
|
|
11963
12886
|
user.userId,
|
|
11964
12887
|
"user!.create",
|
|
@@ -11996,7 +12919,7 @@ userRoutes.get("/users/:id", async (c) => {
|
|
|
11996
12919
|
if (!userRecord) {
|
|
11997
12920
|
return c.json({ error: "User not found" }, 404);
|
|
11998
12921
|
}
|
|
11999
|
-
await
|
|
12922
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12000
12923
|
db,
|
|
12001
12924
|
user.userId,
|
|
12002
12925
|
"user!.view",
|
|
@@ -12109,7 +13032,9 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
12109
13032
|
const username = chunkMNWKYY5E_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
12110
13033
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
12111
13034
|
const phone = chunkMNWKYY5E_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
12112
|
-
const
|
|
13035
|
+
const roleInput = formData.get("role")?.toString() || "viewer";
|
|
13036
|
+
const validRoles = ["admin", "editor", "author", "viewer"];
|
|
13037
|
+
const role = validRoles.includes(roleInput) ? roleInput : "viewer";
|
|
12113
13038
|
const isActive = formData.get("is_active") === "1";
|
|
12114
13039
|
const emailVerified = formData.get("email_verified") === "1";
|
|
12115
13040
|
const profileDisplayName = chunkMNWKYY5E_cjs.sanitizeInput(formData.get("profile_display_name")?.toString()) || null;
|
|
@@ -12221,7 +13146,7 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
12221
13146
|
).run();
|
|
12222
13147
|
}
|
|
12223
13148
|
}
|
|
12224
|
-
await
|
|
13149
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12225
13150
|
db,
|
|
12226
13151
|
user.userId,
|
|
12227
13152
|
"user.update",
|
|
@@ -12266,7 +13191,7 @@ userRoutes.post("/users/:id/toggle", async (c) => {
|
|
|
12266
13191
|
UPDATE users SET is_active = ?, updated_at = ? WHERE id = ?
|
|
12267
13192
|
`);
|
|
12268
13193
|
await toggleStmt.bind(active ? 1 : 0, Date.now(), userId).run();
|
|
12269
|
-
await
|
|
13194
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12270
13195
|
db,
|
|
12271
13196
|
user.userId,
|
|
12272
13197
|
active ? "user.activate" : "user.deactivate",
|
|
@@ -12307,7 +13232,7 @@ userRoutes.delete("/users/:id", async (c) => {
|
|
|
12307
13232
|
DELETE FROM users WHERE id = ?
|
|
12308
13233
|
`);
|
|
12309
13234
|
await deleteStmt.bind(userId).run();
|
|
12310
|
-
await
|
|
13235
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12311
13236
|
db,
|
|
12312
13237
|
user.userId,
|
|
12313
13238
|
"user!.hard_delete",
|
|
@@ -12326,7 +13251,7 @@ userRoutes.delete("/users/:id", async (c) => {
|
|
|
12326
13251
|
UPDATE users SET is_active = 0, updated_at = ? WHERE id = ?
|
|
12327
13252
|
`);
|
|
12328
13253
|
await deleteStmt.bind(Date.now(), userId).run();
|
|
12329
|
-
await
|
|
13254
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12330
13255
|
db,
|
|
12331
13256
|
user.userId,
|
|
12332
13257
|
"user!.soft_delete",
|
|
@@ -12392,7 +13317,7 @@ userRoutes.post("/invite-user", async (c) => {
|
|
|
12392
13317
|
Date.now(),
|
|
12393
13318
|
Date.now()
|
|
12394
13319
|
).run();
|
|
12395
|
-
await
|
|
13320
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12396
13321
|
db,
|
|
12397
13322
|
user.userId,
|
|
12398
13323
|
"user!.invite_sent",
|
|
@@ -12449,7 +13374,7 @@ userRoutes.post("/resend-invitation/:id", async (c) => {
|
|
|
12449
13374
|
Date.now(),
|
|
12450
13375
|
userId
|
|
12451
13376
|
).run();
|
|
12452
|
-
await
|
|
13377
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12453
13378
|
db,
|
|
12454
13379
|
user.userId,
|
|
12455
13380
|
"user!.invitation_resent",
|
|
@@ -12485,7 +13410,7 @@ userRoutes.delete("/cancel-invitation/:id", async (c) => {
|
|
|
12485
13410
|
}
|
|
12486
13411
|
const deleteStmt = db.prepare(`DELETE FROM users WHERE id = ?`);
|
|
12487
13412
|
await deleteStmt.bind(userId).run();
|
|
12488
|
-
await
|
|
13413
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12489
13414
|
db,
|
|
12490
13415
|
user.userId,
|
|
12491
13416
|
"user!.invitation_cancelled",
|
|
@@ -12568,7 +13493,7 @@ userRoutes.get("/activity-logs", async (c) => {
|
|
|
12568
13493
|
...log,
|
|
12569
13494
|
details: log.details ? JSON.parse(log.details) : null
|
|
12570
13495
|
}));
|
|
12571
|
-
await
|
|
13496
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12572
13497
|
db,
|
|
12573
13498
|
user.userId,
|
|
12574
13499
|
"activity.logs_viewed",
|
|
@@ -12675,7 +13600,7 @@ userRoutes.get("/activity-logs/export", async (c) => {
|
|
|
12675
13600
|
csvRows.push(row.join(","));
|
|
12676
13601
|
}
|
|
12677
13602
|
const csvContent = csvRows.join("\n");
|
|
12678
|
-
await
|
|
13603
|
+
await chunkIT2TC4ZD_cjs.logActivity(
|
|
12679
13604
|
db,
|
|
12680
13605
|
user.userId,
|
|
12681
13606
|
"activity.logs_exported",
|
|
@@ -14014,7 +14939,7 @@ var fileValidationSchema2 = zod.z.object({
|
|
|
14014
14939
|
// 50MB max
|
|
14015
14940
|
});
|
|
14016
14941
|
var adminMediaRoutes = new hono.Hono();
|
|
14017
|
-
adminMediaRoutes.use("*",
|
|
14942
|
+
adminMediaRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
14018
14943
|
adminMediaRoutes.get("/", async (c) => {
|
|
14019
14944
|
try {
|
|
14020
14945
|
const user = c.get("user");
|
|
@@ -14600,7 +15525,7 @@ adminMediaRoutes.put("/:id", async (c) => {
|
|
|
14600
15525
|
`);
|
|
14601
15526
|
}
|
|
14602
15527
|
});
|
|
14603
|
-
adminMediaRoutes.delete("/cleanup",
|
|
15528
|
+
adminMediaRoutes.delete("/cleanup", chunkIT2TC4ZD_cjs.requireRole("admin"), async (c) => {
|
|
14604
15529
|
try {
|
|
14605
15530
|
const db = c.env.DB;
|
|
14606
15531
|
const allMediaStmt = db.prepare("SELECT id, r2_key, filename FROM media WHERE deleted_at IS NULL");
|
|
@@ -16823,7 +17748,7 @@ function renderEmailSettingsContent(plugin, settings) {
|
|
|
16823
17748
|
|
|
16824
17749
|
// src/routes/admin-plugins.ts
|
|
16825
17750
|
var adminPluginRoutes = new hono.Hono();
|
|
16826
|
-
adminPluginRoutes.use("*",
|
|
17751
|
+
adminPluginRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
16827
17752
|
var AVAILABLE_PLUGINS = [
|
|
16828
17753
|
{
|
|
16829
17754
|
id: "third-party-faq",
|
|
@@ -16950,7 +17875,7 @@ adminPluginRoutes.get("/", async (c) => {
|
|
|
16950
17875
|
if (user?.role !== "admin") {
|
|
16951
17876
|
return c.text("Access denied", 403);
|
|
16952
17877
|
}
|
|
16953
|
-
const pluginService = new
|
|
17878
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
16954
17879
|
let installedPlugins = [];
|
|
16955
17880
|
let stats = { total: 0, active: 0, inactive: 0, errors: 0, uninstalled: 0 };
|
|
16956
17881
|
try {
|
|
@@ -17026,7 +17951,7 @@ adminPluginRoutes.get("/:id", async (c) => {
|
|
|
17026
17951
|
if (user?.role !== "admin") {
|
|
17027
17952
|
return c.redirect("/admin/plugins");
|
|
17028
17953
|
}
|
|
17029
|
-
const pluginService = new
|
|
17954
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
17030
17955
|
const plugin = await pluginService.getPlugin(pluginId);
|
|
17031
17956
|
if (!plugin) {
|
|
17032
17957
|
return c.text("Plugin not found", 404);
|
|
@@ -17110,7 +18035,7 @@ adminPluginRoutes.post("/:id/activate", async (c) => {
|
|
|
17110
18035
|
if (user?.role !== "admin") {
|
|
17111
18036
|
return c.json({ error: "Access denied" }, 403);
|
|
17112
18037
|
}
|
|
17113
|
-
const pluginService = new
|
|
18038
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
17114
18039
|
await pluginService.activatePlugin(pluginId);
|
|
17115
18040
|
return c.json({ success: true });
|
|
17116
18041
|
} catch (error) {
|
|
@@ -17127,7 +18052,7 @@ adminPluginRoutes.post("/:id/deactivate", async (c) => {
|
|
|
17127
18052
|
if (user?.role !== "admin") {
|
|
17128
18053
|
return c.json({ error: "Access denied" }, 403);
|
|
17129
18054
|
}
|
|
17130
|
-
const pluginService = new
|
|
18055
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
17131
18056
|
await pluginService.deactivatePlugin(pluginId);
|
|
17132
18057
|
return c.json({ success: true });
|
|
17133
18058
|
} catch (error) {
|
|
@@ -17144,7 +18069,7 @@ adminPluginRoutes.post("/install", async (c) => {
|
|
|
17144
18069
|
return c.json({ error: "Access denied" }, 403);
|
|
17145
18070
|
}
|
|
17146
18071
|
const body = await c.req.json();
|
|
17147
|
-
const pluginService = new
|
|
18072
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
17148
18073
|
if (body.name === "faq-plugin") {
|
|
17149
18074
|
const faqPlugin = await pluginService.installPlugin({
|
|
17150
18075
|
id: "third-party-faq",
|
|
@@ -17414,7 +18339,7 @@ adminPluginRoutes.post("/:id/uninstall", async (c) => {
|
|
|
17414
18339
|
if (user?.role !== "admin") {
|
|
17415
18340
|
return c.json({ error: "Access denied" }, 403);
|
|
17416
18341
|
}
|
|
17417
|
-
const pluginService = new
|
|
18342
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
17418
18343
|
await pluginService.uninstallPlugin(pluginId);
|
|
17419
18344
|
return c.json({ success: true });
|
|
17420
18345
|
} catch (error) {
|
|
@@ -17432,8 +18357,20 @@ adminPluginRoutes.post("/:id/settings", async (c) => {
|
|
|
17432
18357
|
return c.json({ error: "Access denied" }, 403);
|
|
17433
18358
|
}
|
|
17434
18359
|
const settings = await c.req.json();
|
|
17435
|
-
const pluginService = new
|
|
18360
|
+
const pluginService = new chunkIIBRG5S5_cjs.PluginService(db);
|
|
17436
18361
|
await pluginService.updatePluginSettings(pluginId, settings);
|
|
18362
|
+
if (pluginId === "core-auth") {
|
|
18363
|
+
try {
|
|
18364
|
+
const cacheKv = c.env.CACHE_KV;
|
|
18365
|
+
if (cacheKv) {
|
|
18366
|
+
await cacheKv.delete("auth:settings");
|
|
18367
|
+
await cacheKv.delete("auth:registration-enabled");
|
|
18368
|
+
console.log("[AuthSettings] Cache cleared after updating authentication settings");
|
|
18369
|
+
}
|
|
18370
|
+
} catch (cacheError) {
|
|
18371
|
+
console.error("[AuthSettings] Failed to clear cache:", cacheError);
|
|
18372
|
+
}
|
|
18373
|
+
}
|
|
17437
18374
|
return c.json({ success: true });
|
|
17438
18375
|
} catch (error) {
|
|
17439
18376
|
console.error("Error updating plugin settings:", error);
|
|
@@ -18228,11 +19165,11 @@ function renderLogConfigPage(data) {
|
|
|
18228
19165
|
|
|
18229
19166
|
// src/routes/admin-logs.ts
|
|
18230
19167
|
var adminLogsRoutes = new hono.Hono();
|
|
18231
|
-
adminLogsRoutes.use("*",
|
|
19168
|
+
adminLogsRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
18232
19169
|
adminLogsRoutes.get("/", async (c) => {
|
|
18233
19170
|
try {
|
|
18234
19171
|
const user = c.get("user");
|
|
18235
|
-
const logger =
|
|
19172
|
+
const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
|
|
18236
19173
|
const query = c.req.query();
|
|
18237
19174
|
const page = parseInt(query.page || "1");
|
|
18238
19175
|
const limit = parseInt(query.limit || "50");
|
|
@@ -18312,7 +19249,7 @@ adminLogsRoutes.get("/:id", async (c) => {
|
|
|
18312
19249
|
try {
|
|
18313
19250
|
const id = c.req.param("id");
|
|
18314
19251
|
const user = c.get("user");
|
|
18315
|
-
const logger =
|
|
19252
|
+
const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
|
|
18316
19253
|
const { logs } = await logger.getLogs({
|
|
18317
19254
|
limit: 1,
|
|
18318
19255
|
offset: 0,
|
|
@@ -18349,7 +19286,7 @@ adminLogsRoutes.get("/:id", async (c) => {
|
|
|
18349
19286
|
adminLogsRoutes.get("/config", async (c) => {
|
|
18350
19287
|
try {
|
|
18351
19288
|
const user = c.get("user");
|
|
18352
|
-
const logger =
|
|
19289
|
+
const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
|
|
18353
19290
|
const configs = await logger.getAllConfigs();
|
|
18354
19291
|
const pageData = {
|
|
18355
19292
|
configs,
|
|
@@ -18373,7 +19310,7 @@ adminLogsRoutes.post("/config/:category", async (c) => {
|
|
|
18373
19310
|
const level = formData.get("level");
|
|
18374
19311
|
const retention = parseInt(formData.get("retention"));
|
|
18375
19312
|
const maxSize = parseInt(formData.get("max_size"));
|
|
18376
|
-
const logger =
|
|
19313
|
+
const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
|
|
18377
19314
|
await logger.updateConfig(category, {
|
|
18378
19315
|
enabled,
|
|
18379
19316
|
level,
|
|
@@ -18402,7 +19339,7 @@ adminLogsRoutes.get("/export", async (c) => {
|
|
|
18402
19339
|
const category = query.category;
|
|
18403
19340
|
const startDate = query.start_date;
|
|
18404
19341
|
const endDate = query.end_date;
|
|
18405
|
-
const logger =
|
|
19342
|
+
const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
|
|
18406
19343
|
const filter = {
|
|
18407
19344
|
limit: 1e4,
|
|
18408
19345
|
// Export up to 10k logs
|
|
@@ -18483,7 +19420,7 @@ adminLogsRoutes.post("/cleanup", async (c) => {
|
|
|
18483
19420
|
error: "Unauthorized. Admin access required."
|
|
18484
19421
|
}, 403);
|
|
18485
19422
|
}
|
|
18486
|
-
const logger =
|
|
19423
|
+
const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
|
|
18487
19424
|
await logger.cleanupByRetention();
|
|
18488
19425
|
return c.html(html.html`
|
|
18489
19426
|
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
|
|
@@ -18505,7 +19442,7 @@ adminLogsRoutes.post("/search", async (c) => {
|
|
|
18505
19442
|
const search = formData.get("search");
|
|
18506
19443
|
const level = formData.get("level");
|
|
18507
19444
|
const category = formData.get("category");
|
|
18508
|
-
const logger =
|
|
19445
|
+
const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
|
|
18509
19446
|
const filter = {
|
|
18510
19447
|
limit: 20,
|
|
18511
19448
|
offset: 0,
|
|
@@ -20556,9 +21493,9 @@ function renderStorageUsage(databaseSizeBytes, mediaSizeBytes) {
|
|
|
20556
21493
|
}
|
|
20557
21494
|
|
|
20558
21495
|
// src/routes/admin-dashboard.ts
|
|
20559
|
-
var VERSION =
|
|
21496
|
+
var VERSION = chunkEKPLKUZT_cjs.getCoreVersion();
|
|
20560
21497
|
var router = new hono.Hono();
|
|
20561
|
-
router.use("*",
|
|
21498
|
+
router.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
20562
21499
|
router.get("/", async (c) => {
|
|
20563
21500
|
const user = c.get("user");
|
|
20564
21501
|
try {
|
|
@@ -20782,6 +21719,20 @@ router.get("/system-status", async (c) => {
|
|
|
20782
21719
|
}
|
|
20783
21720
|
});
|
|
20784
21721
|
|
|
21722
|
+
// src/routes/admin-collections-field-types.ts
|
|
21723
|
+
function isMarkdownEditorType(fieldType) {
|
|
21724
|
+
return fieldType === "markdown" || fieldType === "mdxeditor" || fieldType === "easymde";
|
|
21725
|
+
}
|
|
21726
|
+
function normalizeFieldType(fieldType) {
|
|
21727
|
+
if (isMarkdownEditorType(fieldType)) {
|
|
21728
|
+
return "markdown";
|
|
21729
|
+
}
|
|
21730
|
+
if (fieldType === "tinymce") {
|
|
21731
|
+
return "richtext";
|
|
21732
|
+
}
|
|
21733
|
+
return fieldType;
|
|
21734
|
+
}
|
|
21735
|
+
|
|
20785
21736
|
// src/templates/pages/admin-collections-list.template.ts
|
|
20786
21737
|
chunkLTKV7AE5_cjs.init_admin_layout_catalyst_template();
|
|
20787
21738
|
|
|
@@ -21268,7 +22219,9 @@ function getFieldTypeBadge(fieldType) {
|
|
|
21268
22219
|
"slug": "URL Slug",
|
|
21269
22220
|
"richtext": "Rich Text (TinyMCE)",
|
|
21270
22221
|
"quill": "Rich Text (Quill)",
|
|
21271
|
-
"
|
|
22222
|
+
"markdown": "Markdown",
|
|
22223
|
+
"mdxeditor": "Markdown",
|
|
22224
|
+
"easymde": "Markdown",
|
|
21272
22225
|
"number": "Number",
|
|
21273
22226
|
"boolean": "Boolean",
|
|
21274
22227
|
"date": "Date",
|
|
@@ -21281,7 +22234,9 @@ function getFieldTypeBadge(fieldType) {
|
|
|
21281
22234
|
"slug": "bg-sky-500/10 dark:bg-sky-400/10 text-sky-700 dark:text-sky-300 ring-sky-500/20 dark:ring-sky-400/20",
|
|
21282
22235
|
"richtext": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
|
|
21283
22236
|
"quill": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
|
|
22237
|
+
"markdown": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
|
|
21284
22238
|
"mdxeditor": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
|
|
22239
|
+
"easymde": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
|
|
21285
22240
|
"number": "bg-green-500/10 dark:bg-green-400/10 text-green-700 dark:text-green-300 ring-green-500/20 dark:ring-green-400/20",
|
|
21286
22241
|
"boolean": "bg-amber-500/10 dark:bg-amber-400/10 text-amber-700 dark:text-amber-300 ring-amber-500/20 dark:ring-amber-400/20",
|
|
21287
22242
|
"date": "bg-cyan-500/10 dark:bg-cyan-400/10 text-cyan-700 dark:text-cyan-300 ring-cyan-500/20 dark:ring-cyan-400/20",
|
|
@@ -21762,7 +22717,7 @@ function renderCollectionFormPage(data) {
|
|
|
21762
22717
|
<option value="slug">URL Slug</option>
|
|
21763
22718
|
${data.editorPlugins?.tinymce ? '<option value="richtext">Rich Text (TinyMCE)</option>' : ""}
|
|
21764
22719
|
${data.editorPlugins?.quill ? '<option value="quill">Rich Text (Quill)</option>' : ""}
|
|
21765
|
-
${data.editorPlugins?.easyMdx ? '<option value="
|
|
22720
|
+
${data.editorPlugins?.easyMdx ? '<option value="markdown">Markdown</option>' : ""}
|
|
21766
22721
|
<option value="number">Number</option>
|
|
21767
22722
|
<option value="boolean">Boolean</option>
|
|
21768
22723
|
<option value="date">Date</option>
|
|
@@ -21987,7 +22942,7 @@ function renderCollectionFormPage(data) {
|
|
|
21987
22942
|
// Check if it's a schema field with field_options that might indicate the actual type
|
|
21988
22943
|
if (field.field_options && typeof field.field_options === 'object') {
|
|
21989
22944
|
// Only convert to richtext if type is explicitly 'string' and format is richtext
|
|
21990
|
-
// Don't convert if it's already a specific editor type like '
|
|
22945
|
+
// Don't convert if it's already a specific editor type like 'markdown', 'quill', etc.
|
|
21991
22946
|
if (field.field_options.format === 'richtext' && uiFieldType === 'string') {
|
|
21992
22947
|
uiFieldType = 'richtext';
|
|
21993
22948
|
}
|
|
@@ -22008,6 +22963,12 @@ function renderCollectionFormPage(data) {
|
|
|
22008
22963
|
uiFieldType = typeMapping[uiFieldType];
|
|
22009
22964
|
}
|
|
22010
22965
|
|
|
22966
|
+
if (uiFieldType === 'mdxeditor' || uiFieldType === 'easymde') {
|
|
22967
|
+
uiFieldType = 'markdown';
|
|
22968
|
+
} else if (uiFieldType === 'tinymce') {
|
|
22969
|
+
uiFieldType = 'richtext';
|
|
22970
|
+
}
|
|
22971
|
+
|
|
22011
22972
|
// Log all available options
|
|
22012
22973
|
const availableOptions = Array.from(fieldTypeSelect.options).map(opt => ({ value: opt.value, text: opt.text }));
|
|
22013
22974
|
console.log('Available dropdown options:', availableOptions);
|
|
@@ -22094,7 +23055,7 @@ function renderCollectionFormPage(data) {
|
|
|
22094
23055
|
|
|
22095
23056
|
console.log('[Edit Field] Showing options for field type:', fieldType, '(original:', field.field_type, ')');
|
|
22096
23057
|
|
|
22097
|
-
if (['select', 'radio', 'media', 'richtext', 'reference'].includes(fieldType)) {
|
|
23058
|
+
if (['select', 'radio', 'media', 'richtext', 'markdown', 'reference'].includes(fieldType)) {
|
|
22098
23059
|
optionsContainer.classList.remove('hidden');
|
|
22099
23060
|
|
|
22100
23061
|
// Set help text based on type
|
|
@@ -22111,6 +23072,9 @@ function renderCollectionFormPage(data) {
|
|
|
22111
23072
|
case 'richtext':
|
|
22112
23073
|
helpText.textContent = 'Full-featured WYSIWYG text editor with formatting options';
|
|
22113
23074
|
break;
|
|
23075
|
+
case 'markdown':
|
|
23076
|
+
helpText.textContent = 'Markdown editor with live preview powered by the EasyMDE plugin';
|
|
23077
|
+
break;
|
|
22114
23078
|
case 'reference':
|
|
22115
23079
|
helpText.textContent = 'Link to content from other collections';
|
|
22116
23080
|
break;
|
|
@@ -22251,7 +23215,7 @@ function renderCollectionFormPage(data) {
|
|
|
22251
23215
|
const fieldNameInput = document.getElementById('modal-field-name');
|
|
22252
23216
|
|
|
22253
23217
|
// Show/hide options based on field type
|
|
22254
|
-
if (['select', 'radio', 'media', 'richtext', 'guid', 'reference'].includes(this.value)) {
|
|
23218
|
+
if (['select', 'radio', 'media', 'richtext', 'markdown', 'guid', 'reference'].includes(this.value)) {
|
|
22255
23219
|
optionsContainer.classList.remove('hidden');
|
|
22256
23220
|
|
|
22257
23221
|
// Set default options and help text based on type
|
|
@@ -22272,6 +23236,10 @@ function renderCollectionFormPage(data) {
|
|
|
22272
23236
|
fieldOptions.value = '{"toolbar": "full", "height": 400}';
|
|
22273
23237
|
helpText.textContent = 'Full-featured WYSIWYG text editor with formatting options';
|
|
22274
23238
|
break;
|
|
23239
|
+
case 'markdown':
|
|
23240
|
+
fieldOptions.value = '{"toolbar": "full", "height": 400}';
|
|
23241
|
+
helpText.textContent = 'Markdown editor with live preview powered by the EasyMDE plugin';
|
|
23242
|
+
break;
|
|
22275
23243
|
case 'reference':
|
|
22276
23244
|
fieldOptions.value = '{"collection": ["pages", "posts"]}';
|
|
22277
23245
|
helpText.textContent = 'Link to content from other collections';
|
|
@@ -22346,7 +23314,10 @@ function renderCollectionFormPage(data) {
|
|
|
22346
23314
|
|
|
22347
23315
|
// src/routes/admin-collections.ts
|
|
22348
23316
|
var adminCollectionsRoutes = new hono.Hono();
|
|
22349
|
-
adminCollectionsRoutes.use("*",
|
|
23317
|
+
adminCollectionsRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
23318
|
+
adminCollectionsRoutes.post("*", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
23319
|
+
adminCollectionsRoutes.put("*", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
23320
|
+
adminCollectionsRoutes.delete("*", chunkIT2TC4ZD_cjs.requireRole(["admin"]));
|
|
22350
23321
|
adminCollectionsRoutes.get("/", async (c) => {
|
|
22351
23322
|
try {
|
|
22352
23323
|
const user = c.get("user");
|
|
@@ -22832,11 +23803,12 @@ adminCollectionsRoutes.post("/:id/fields", async (c) => {
|
|
|
22832
23803
|
searchable: isSearchable,
|
|
22833
23804
|
...parsedOptions
|
|
22834
23805
|
};
|
|
22835
|
-
|
|
23806
|
+
const normalizedFieldType = normalizeFieldType(fieldType);
|
|
23807
|
+
if (normalizedFieldType === "richtext") {
|
|
22836
23808
|
fieldConfig.format = "richtext";
|
|
22837
|
-
} else if (
|
|
23809
|
+
} else if (normalizedFieldType === "date") {
|
|
22838
23810
|
fieldConfig.format = "date-time";
|
|
22839
|
-
} else if (
|
|
23811
|
+
} else if (normalizedFieldType === "select") {
|
|
22840
23812
|
fieldConfig.enum = parsedOptions.options || [];
|
|
22841
23813
|
} else if (fieldType === "radio") {
|
|
22842
23814
|
fieldConfig.type = "radio";
|
|
@@ -22845,20 +23817,14 @@ adminCollectionsRoutes.post("/:id/fields", async (c) => {
|
|
|
22845
23817
|
}
|
|
22846
23818
|
} else if (fieldType === "media") {
|
|
22847
23819
|
fieldConfig.format = "media";
|
|
22848
|
-
} else if (
|
|
23820
|
+
} else if (normalizedFieldType === "slug") {
|
|
22849
23821
|
fieldConfig.type = "slug";
|
|
22850
23822
|
fieldConfig.format = "slug";
|
|
22851
|
-
} else if (
|
|
23823
|
+
} else if (normalizedFieldType === "quill") {
|
|
22852
23824
|
fieldConfig.type = "quill";
|
|
22853
|
-
} else if (
|
|
22854
|
-
fieldConfig.type = "mdxeditor";
|
|
22855
|
-
} else if (fieldType === "tinymce") {
|
|
22856
|
-
fieldConfig.type = "tinymce";
|
|
22857
|
-
} else if (fieldType === "easymde") {
|
|
22858
|
-
fieldConfig.type = "easymde";
|
|
22859
|
-
} else if (fieldType === "markdown") {
|
|
23825
|
+
} else if (normalizedFieldType === "markdown") {
|
|
22860
23826
|
fieldConfig.type = "markdown";
|
|
22861
|
-
} else if (
|
|
23827
|
+
} else if (normalizedFieldType === "reference") {
|
|
22862
23828
|
fieldConfig.type = "reference";
|
|
22863
23829
|
}
|
|
22864
23830
|
schema.properties[fieldName] = fieldConfig;
|
|
@@ -24546,7 +25512,7 @@ function renderDatabaseToolsSettings(settings) {
|
|
|
24546
25512
|
|
|
24547
25513
|
// src/routes/admin-settings.ts
|
|
24548
25514
|
var adminSettingsRoutes = new hono.Hono();
|
|
24549
|
-
adminSettingsRoutes.use("*",
|
|
25515
|
+
adminSettingsRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
24550
25516
|
function getMockSettings(user) {
|
|
24551
25517
|
return {
|
|
24552
25518
|
general: {
|
|
@@ -24611,7 +25577,7 @@ adminSettingsRoutes.get("/", (c) => {
|
|
|
24611
25577
|
adminSettingsRoutes.get("/general", async (c) => {
|
|
24612
25578
|
const user = c.get("user");
|
|
24613
25579
|
const db = c.env.DB;
|
|
24614
|
-
const settingsService = new
|
|
25580
|
+
const settingsService = new chunk64APW3DW_cjs.SettingsService(db);
|
|
24615
25581
|
const generalSettings = await settingsService.getGeneralSettings(user?.email);
|
|
24616
25582
|
const mockSettings = getMockSettings(user);
|
|
24617
25583
|
mockSettings.general = generalSettings;
|
|
@@ -24714,7 +25680,7 @@ adminSettingsRoutes.get("/database-tools", (c) => {
|
|
|
24714
25680
|
adminSettingsRoutes.get("/api/migrations/status", async (c) => {
|
|
24715
25681
|
try {
|
|
24716
25682
|
const db = c.env.DB;
|
|
24717
|
-
const migrationService = new
|
|
25683
|
+
const migrationService = new chunkZMVWMJ3S_cjs.MigrationService(db);
|
|
24718
25684
|
const status = await migrationService.getMigrationStatus();
|
|
24719
25685
|
return c.json({
|
|
24720
25686
|
success: true,
|
|
@@ -24738,7 +25704,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
|
|
|
24738
25704
|
}, 403);
|
|
24739
25705
|
}
|
|
24740
25706
|
const db = c.env.DB;
|
|
24741
|
-
const migrationService = new
|
|
25707
|
+
const migrationService = new chunkZMVWMJ3S_cjs.MigrationService(db);
|
|
24742
25708
|
const result = await migrationService.runPendingMigrations();
|
|
24743
25709
|
return c.json({
|
|
24744
25710
|
success: result.success,
|
|
@@ -24756,7 +25722,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
|
|
|
24756
25722
|
adminSettingsRoutes.get("/api/migrations/validate", async (c) => {
|
|
24757
25723
|
try {
|
|
24758
25724
|
const db = c.env.DB;
|
|
24759
|
-
const migrationService = new
|
|
25725
|
+
const migrationService = new chunkZMVWMJ3S_cjs.MigrationService(db);
|
|
24760
25726
|
const validation = await migrationService.validateSchema();
|
|
24761
25727
|
return c.json({
|
|
24762
25728
|
success: true,
|
|
@@ -24925,7 +25891,7 @@ adminSettingsRoutes.post("/general", async (c) => {
|
|
|
24925
25891
|
}
|
|
24926
25892
|
const formData = await c.req.formData();
|
|
24927
25893
|
const db = c.env.DB;
|
|
24928
|
-
const settingsService = new
|
|
25894
|
+
const settingsService = new chunk64APW3DW_cjs.SettingsService(db);
|
|
24929
25895
|
const settings = {
|
|
24930
25896
|
siteName: formData.get("siteName"),
|
|
24931
25897
|
siteDescription: formData.get("siteDescription"),
|
|
@@ -26646,7 +27612,7 @@ function renderFormCreatePage(data) {
|
|
|
26646
27612
|
|
|
26647
27613
|
// src/routes/admin-forms.ts
|
|
26648
27614
|
var adminFormsRoutes = new hono.Hono();
|
|
26649
|
-
adminFormsRoutes.use("*",
|
|
27615
|
+
adminFormsRoutes.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
26650
27616
|
adminFormsRoutes.get("/", async (c) => {
|
|
26651
27617
|
try {
|
|
26652
27618
|
const user = c.get("user");
|
|
@@ -27463,6 +28429,33 @@ var public_forms_default = publicFormsRoutes;
|
|
|
27463
28429
|
|
|
27464
28430
|
// src/templates/pages/admin-api-reference.template.ts
|
|
27465
28431
|
chunkLTKV7AE5_cjs.init_admin_layout_catalyst_template();
|
|
28432
|
+
function renderAuthBadge(auth) {
|
|
28433
|
+
if (auth === true) {
|
|
28434
|
+
return `
|
|
28435
|
+
<span class="shrink-0 inline-flex items-center gap-x-1 rounded-md bg-amber-50 dark:bg-amber-500/10 px-2 py-1 text-xs font-medium text-amber-700 dark:text-amber-300 ring-1 ring-inset ring-amber-700/10 dark:ring-amber-400/20">
|
|
28436
|
+
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
28437
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
|
|
28438
|
+
</svg>
|
|
28439
|
+
Auth
|
|
28440
|
+
</span>`;
|
|
28441
|
+
}
|
|
28442
|
+
if (auth === false) {
|
|
28443
|
+
return `
|
|
28444
|
+
<span class="shrink-0 inline-flex items-center gap-x-1 rounded-md bg-lime-50 dark:bg-lime-500/10 px-2 py-1 text-xs font-medium text-lime-700 dark:text-lime-300 ring-1 ring-inset ring-lime-700/10 dark:ring-lime-400/20">
|
|
28445
|
+
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
28446
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
28447
|
+
</svg>
|
|
28448
|
+
Public
|
|
28449
|
+
</span>`;
|
|
28450
|
+
}
|
|
28451
|
+
return `
|
|
28452
|
+
<span class="shrink-0 inline-flex items-center gap-x-1 rounded-md bg-zinc-50 dark:bg-zinc-500/10 px-2 py-1 text-xs font-medium text-zinc-500 dark:text-zinc-400 ring-1 ring-inset ring-zinc-500/10 dark:ring-zinc-400/20">
|
|
28453
|
+
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
28454
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
28455
|
+
</svg>
|
|
28456
|
+
Unknown
|
|
28457
|
+
</span>`;
|
|
28458
|
+
}
|
|
27466
28459
|
function renderAPIReferencePage(data) {
|
|
27467
28460
|
const endpointsByCategory = data.endpoints.reduce((acc, endpoint) => {
|
|
27468
28461
|
if (!acc[endpoint.category]) {
|
|
@@ -27471,40 +28464,18 @@ function renderAPIReferencePage(data) {
|
|
|
27471
28464
|
acc[endpoint.category].push(endpoint);
|
|
27472
28465
|
return acc;
|
|
27473
28466
|
}, {});
|
|
27474
|
-
const
|
|
27475
|
-
|
|
27476
|
-
|
|
27477
|
-
|
|
27478
|
-
|
|
27479
|
-
},
|
|
27480
|
-
"Content": {
|
|
27481
|
-
title: "Content Management",
|
|
27482
|
-
description: "Content creation, retrieval, and management",
|
|
27483
|
-
icon: "\u{1F4DD}"
|
|
27484
|
-
},
|
|
27485
|
-
"Media": {
|
|
27486
|
-
title: "Media Management",
|
|
27487
|
-
description: "File upload, storage, and media operations",
|
|
27488
|
-
icon: "\u{1F5BC}\uFE0F"
|
|
27489
|
-
},
|
|
27490
|
-
"Admin": {
|
|
27491
|
-
title: "Admin Interface",
|
|
27492
|
-
description: "Administrative panel and management features",
|
|
27493
|
-
icon: "\u2699\uFE0F"
|
|
27494
|
-
},
|
|
27495
|
-
"System": {
|
|
27496
|
-
title: "System",
|
|
27497
|
-
description: "Health checks and system information",
|
|
27498
|
-
icon: "\u{1F527}"
|
|
27499
|
-
}
|
|
27500
|
-
};
|
|
28467
|
+
const categories = Object.keys(endpointsByCategory);
|
|
28468
|
+
const totalEndpoints = data.endpoints.length;
|
|
28469
|
+
const publicEndpoints = data.endpoints.filter((e) => e.authentication === false).length;
|
|
28470
|
+
const protectedEndpoints = data.endpoints.filter((e) => e.authentication === true).length;
|
|
28471
|
+
const undocumentedCount = data.endpoints.filter((e) => e.documented === false).length;
|
|
27501
28472
|
const pageContent = `
|
|
27502
28473
|
<div class="space-y-6">
|
|
27503
28474
|
<!-- Header -->
|
|
27504
28475
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
|
27505
28476
|
<div>
|
|
27506
28477
|
<h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">API Reference</h1>
|
|
27507
|
-
<p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">
|
|
28478
|
+
<p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">Auto-discovered documentation of all registered API endpoints</p>
|
|
27508
28479
|
</div>
|
|
27509
28480
|
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
|
27510
28481
|
<a href="/api" target="_blank" class="inline-flex items-center justify-center gap-x-1.5 rounded-lg bg-zinc-950 dark:bg-white px-3.5 py-2.5 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 transition-colors shadow-sm">
|
|
@@ -27517,29 +28488,35 @@ function renderAPIReferencePage(data) {
|
|
|
27517
28488
|
</div>
|
|
27518
28489
|
|
|
27519
28490
|
<!-- Stats -->
|
|
27520
|
-
<dl class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-
|
|
28491
|
+
<dl class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-5">
|
|
27521
28492
|
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
|
|
27522
28493
|
<dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Total Endpoints</dt>
|
|
27523
28494
|
<dd class="mt-2 flex items-baseline gap-x-2">
|
|
27524
|
-
<span class="text-4xl font-semibold tracking-tight text-zinc-950 dark:text-white">${
|
|
28495
|
+
<span class="text-4xl font-semibold tracking-tight text-zinc-950 dark:text-white">${totalEndpoints}</span>
|
|
27525
28496
|
</dd>
|
|
27526
28497
|
</div>
|
|
27527
28498
|
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
|
|
27528
28499
|
<dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Public Endpoints</dt>
|
|
27529
28500
|
<dd class="mt-2 flex items-baseline gap-x-2">
|
|
27530
|
-
<span class="text-4xl font-semibold tracking-tight text-lime-600 dark:text-lime-400">${
|
|
28501
|
+
<span class="text-4xl font-semibold tracking-tight text-lime-600 dark:text-lime-400">${publicEndpoints}</span>
|
|
27531
28502
|
</dd>
|
|
27532
28503
|
</div>
|
|
27533
28504
|
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
|
|
27534
28505
|
<dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Protected Endpoints</dt>
|
|
27535
28506
|
<dd class="mt-2 flex items-baseline gap-x-2">
|
|
27536
|
-
<span class="text-4xl font-semibold tracking-tight text-amber-600 dark:text-amber-400">${
|
|
28507
|
+
<span class="text-4xl font-semibold tracking-tight text-amber-600 dark:text-amber-400">${protectedEndpoints}</span>
|
|
27537
28508
|
</dd>
|
|
27538
28509
|
</div>
|
|
27539
28510
|
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
|
|
27540
28511
|
<dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Categories</dt>
|
|
27541
28512
|
<dd class="mt-2 flex items-baseline gap-x-2">
|
|
27542
|
-
<span class="text-4xl font-semibold tracking-tight text-cyan-600 dark:text-cyan-400">${
|
|
28513
|
+
<span class="text-4xl font-semibold tracking-tight text-cyan-600 dark:text-cyan-400">${categories.length}</span>
|
|
28514
|
+
</dd>
|
|
28515
|
+
</div>
|
|
28516
|
+
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
|
|
28517
|
+
<dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Undocumented</dt>
|
|
28518
|
+
<dd class="mt-2 flex items-baseline gap-x-2">
|
|
28519
|
+
<span class="text-4xl font-semibold tracking-tight ${undocumentedCount > 0 ? "text-zinc-400 dark:text-zinc-500" : "text-lime-600 dark:text-lime-400"}">${undocumentedCount}</span>
|
|
27543
28520
|
</dd>
|
|
27544
28521
|
</div>
|
|
27545
28522
|
</dl>
|
|
@@ -27591,9 +28568,11 @@ function renderAPIReferencePage(data) {
|
|
|
27591
28568
|
class="col-start-1 row-start-1 w-full appearance-none rounded-lg bg-white dark:bg-zinc-800 py-2 pl-3 pr-8 text-sm text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-zinc-950/10 dark:outline-white/10 *:bg-white dark:*:bg-zinc-800 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-zinc-950 dark:focus:outline-white min-w-[200px]"
|
|
27592
28569
|
>
|
|
27593
28570
|
<option value="">All Categories</option>
|
|
27594
|
-
${
|
|
27595
|
-
|
|
27596
|
-
|
|
28571
|
+
${categories.map((category) => {
|
|
28572
|
+
const info = chunk64APW3DW_cjs.CATEGORY_INFO[category];
|
|
28573
|
+
const title = info ? info.title : category;
|
|
28574
|
+
return `<option value="${category}">${title}</option>`;
|
|
28575
|
+
}).join("\n ")}
|
|
27597
28576
|
</select>
|
|
27598
28577
|
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-zinc-500 dark:text-zinc-400 sm:size-4">
|
|
27599
28578
|
<path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
|
|
@@ -27607,7 +28586,7 @@ function renderAPIReferencePage(data) {
|
|
|
27607
28586
|
<!-- API Categories -->
|
|
27608
28587
|
<div class="space-y-6">
|
|
27609
28588
|
${Object.entries(endpointsByCategory).map(([category, endpoints]) => {
|
|
27610
|
-
const info =
|
|
28589
|
+
const info = chunk64APW3DW_cjs.CATEGORY_INFO[category] || { title: category, description: "", icon: "📋" };
|
|
27611
28590
|
return `
|
|
27612
28591
|
<div class="api-category" data-category="${category}">
|
|
27613
28592
|
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 overflow-hidden">
|
|
@@ -27641,23 +28620,14 @@ function renderAPIReferencePage(data) {
|
|
|
27641
28620
|
<div class="flex-1 min-w-0">
|
|
27642
28621
|
<div class="flex items-center gap-x-2 mb-2">
|
|
27643
28622
|
<code class="text-zinc-950 dark:text-white text-sm font-mono font-medium break-all">${endpoint.path}</code>
|
|
27644
|
-
${endpoint.authentication
|
|
27645
|
-
|
|
27646
|
-
|
|
27647
|
-
|
|
27648
|
-
</svg>
|
|
27649
|
-
Auth
|
|
27650
|
-
</span>
|
|
27651
|
-
` : `
|
|
27652
|
-
<span class="shrink-0 inline-flex items-center gap-x-1 rounded-md bg-lime-50 dark:bg-lime-500/10 px-2 py-1 text-xs font-medium text-lime-700 dark:text-lime-300 ring-1 ring-inset ring-lime-700/10 dark:ring-lime-400/20">
|
|
27653
|
-
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
27654
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
27655
|
-
</svg>
|
|
27656
|
-
Public
|
|
28623
|
+
${renderAuthBadge(endpoint.authentication)}
|
|
28624
|
+
${endpoint.documented === false ? `
|
|
28625
|
+
<span class="shrink-0 inline-flex items-center rounded-md bg-zinc-50 dark:bg-zinc-800 px-2 py-1 text-xs font-medium text-zinc-400 dark:text-zinc-500 ring-1 ring-inset ring-zinc-200 dark:ring-zinc-700">
|
|
28626
|
+
Auto-discovered
|
|
27657
28627
|
</span>
|
|
27658
|
-
`}
|
|
28628
|
+
` : ""}
|
|
27659
28629
|
</div>
|
|
27660
|
-
<p class="text-zinc-600 dark:text-zinc-400 text-sm leading-6">${endpoint.description}</p>
|
|
28630
|
+
<p class="text-zinc-600 dark:text-zinc-400 text-sm leading-6">${endpoint.description || '<em class="text-zinc-400 dark:text-zinc-500">No description available</em>'}</p>
|
|
27661
28631
|
</div>
|
|
27662
28632
|
</div>
|
|
27663
28633
|
</div>
|
|
@@ -27735,8 +28705,8 @@ function renderAPIReferencePage(data) {
|
|
|
27735
28705
|
const path = endpoint.dataset.path.toLowerCase();
|
|
27736
28706
|
const description = endpoint.dataset.description.toLowerCase();
|
|
27737
28707
|
|
|
27738
|
-
const matchesSearch = !searchTerm ||
|
|
27739
|
-
path.includes(searchTerm) ||
|
|
28708
|
+
const matchesSearch = !searchTerm ||
|
|
28709
|
+
path.includes(searchTerm) ||
|
|
27740
28710
|
description.includes(searchTerm);
|
|
27741
28711
|
const matchesMethod = !selectedMethod || method === selectedMethod;
|
|
27742
28712
|
|
|
@@ -27793,210 +28763,16 @@ function renderAPIReferencePage(data) {
|
|
|
27793
28763
|
}
|
|
27794
28764
|
|
|
27795
28765
|
// src/routes/admin-api-reference.ts
|
|
27796
|
-
var VERSION2 =
|
|
28766
|
+
var VERSION2 = chunkEKPLKUZT_cjs.getCoreVersion();
|
|
27797
28767
|
var router2 = new hono.Hono();
|
|
27798
|
-
router2.use("*",
|
|
27799
|
-
var apiEndpoints = [
|
|
27800
|
-
// Auth endpoints
|
|
27801
|
-
{
|
|
27802
|
-
method: "POST",
|
|
27803
|
-
path: "/auth/login",
|
|
27804
|
-
description: "Authenticate user with email and password",
|
|
27805
|
-
authentication: false,
|
|
27806
|
-
category: "Auth"
|
|
27807
|
-
},
|
|
27808
|
-
{
|
|
27809
|
-
method: "POST",
|
|
27810
|
-
path: "/auth/register",
|
|
27811
|
-
description: "Register a new user account",
|
|
27812
|
-
authentication: false,
|
|
27813
|
-
category: "Auth"
|
|
27814
|
-
},
|
|
27815
|
-
{
|
|
27816
|
-
method: "POST",
|
|
27817
|
-
path: "/auth/logout",
|
|
27818
|
-
description: "Log out the current user and invalidate session",
|
|
27819
|
-
authentication: true,
|
|
27820
|
-
category: "Auth"
|
|
27821
|
-
},
|
|
27822
|
-
{
|
|
27823
|
-
method: "GET",
|
|
27824
|
-
path: "/auth/me",
|
|
27825
|
-
description: "Get current authenticated user information",
|
|
27826
|
-
authentication: true,
|
|
27827
|
-
category: "Auth"
|
|
27828
|
-
},
|
|
27829
|
-
{
|
|
27830
|
-
method: "POST",
|
|
27831
|
-
path: "/auth/refresh",
|
|
27832
|
-
description: "Refresh authentication token",
|
|
27833
|
-
authentication: true,
|
|
27834
|
-
category: "Auth"
|
|
27835
|
-
},
|
|
27836
|
-
// Content endpoints
|
|
27837
|
-
{
|
|
27838
|
-
method: "GET",
|
|
27839
|
-
path: "/api/collections",
|
|
27840
|
-
description: "List all available collections",
|
|
27841
|
-
authentication: false,
|
|
27842
|
-
category: "Content"
|
|
27843
|
-
},
|
|
27844
|
-
{
|
|
27845
|
-
method: "GET",
|
|
27846
|
-
path: "/api/collections/:collection/content",
|
|
27847
|
-
description: "Get all content items from a specific collection",
|
|
27848
|
-
authentication: false,
|
|
27849
|
-
category: "Content"
|
|
27850
|
-
},
|
|
27851
|
-
{
|
|
27852
|
-
method: "GET",
|
|
27853
|
-
path: "/api/content/:id",
|
|
27854
|
-
description: "Get a specific content item by ID",
|
|
27855
|
-
authentication: false,
|
|
27856
|
-
category: "Content"
|
|
27857
|
-
},
|
|
27858
|
-
{
|
|
27859
|
-
method: "POST",
|
|
27860
|
-
path: "/api/content",
|
|
27861
|
-
description: "Create a new content item",
|
|
27862
|
-
authentication: true,
|
|
27863
|
-
category: "Content"
|
|
27864
|
-
},
|
|
27865
|
-
{
|
|
27866
|
-
method: "PUT",
|
|
27867
|
-
path: "/api/content/:id",
|
|
27868
|
-
description: "Update an existing content item",
|
|
27869
|
-
authentication: true,
|
|
27870
|
-
category: "Content"
|
|
27871
|
-
},
|
|
27872
|
-
{
|
|
27873
|
-
method: "DELETE",
|
|
27874
|
-
path: "/api/content/:id",
|
|
27875
|
-
description: "Delete a content item",
|
|
27876
|
-
authentication: true,
|
|
27877
|
-
category: "Content"
|
|
27878
|
-
},
|
|
27879
|
-
// Media endpoints
|
|
27880
|
-
{
|
|
27881
|
-
method: "GET",
|
|
27882
|
-
path: "/api/media",
|
|
27883
|
-
description: "List all media files with pagination",
|
|
27884
|
-
authentication: false,
|
|
27885
|
-
category: "Media"
|
|
27886
|
-
},
|
|
27887
|
-
{
|
|
27888
|
-
method: "GET",
|
|
27889
|
-
path: "/api/media/:id",
|
|
27890
|
-
description: "Get a specific media file by ID",
|
|
27891
|
-
authentication: false,
|
|
27892
|
-
category: "Media"
|
|
27893
|
-
},
|
|
27894
|
-
{
|
|
27895
|
-
method: "POST",
|
|
27896
|
-
path: "/api/media/upload",
|
|
27897
|
-
description: "Upload a new media file to R2 storage",
|
|
27898
|
-
authentication: true,
|
|
27899
|
-
category: "Media"
|
|
27900
|
-
},
|
|
27901
|
-
{
|
|
27902
|
-
method: "DELETE",
|
|
27903
|
-
path: "/api/media/:id",
|
|
27904
|
-
description: "Delete a media file from storage",
|
|
27905
|
-
authentication: true,
|
|
27906
|
-
category: "Media"
|
|
27907
|
-
},
|
|
27908
|
-
// Admin endpoints
|
|
27909
|
-
{
|
|
27910
|
-
method: "GET",
|
|
27911
|
-
path: "/admin/api/stats",
|
|
27912
|
-
description: "Get dashboard statistics (collections, content, media, users)",
|
|
27913
|
-
authentication: true,
|
|
27914
|
-
category: "Admin"
|
|
27915
|
-
},
|
|
27916
|
-
{
|
|
27917
|
-
method: "GET",
|
|
27918
|
-
path: "/admin/api/storage",
|
|
27919
|
-
description: "Get storage usage information",
|
|
27920
|
-
authentication: true,
|
|
27921
|
-
category: "Admin"
|
|
27922
|
-
},
|
|
27923
|
-
{
|
|
27924
|
-
method: "GET",
|
|
27925
|
-
path: "/admin/api/activity",
|
|
27926
|
-
description: "Get recent activity logs",
|
|
27927
|
-
authentication: true,
|
|
27928
|
-
category: "Admin"
|
|
27929
|
-
},
|
|
27930
|
-
{
|
|
27931
|
-
method: "GET",
|
|
27932
|
-
path: "/admin/api/collections",
|
|
27933
|
-
description: "List all collections with field counts",
|
|
27934
|
-
authentication: true,
|
|
27935
|
-
category: "Admin"
|
|
27936
|
-
},
|
|
27937
|
-
{
|
|
27938
|
-
method: "POST",
|
|
27939
|
-
path: "/admin/api/collections",
|
|
27940
|
-
description: "Create a new collection",
|
|
27941
|
-
authentication: true,
|
|
27942
|
-
category: "Admin"
|
|
27943
|
-
},
|
|
27944
|
-
{
|
|
27945
|
-
method: "PATCH",
|
|
27946
|
-
path: "/admin/api/collections/:id",
|
|
27947
|
-
description: "Update an existing collection",
|
|
27948
|
-
authentication: true,
|
|
27949
|
-
category: "Admin"
|
|
27950
|
-
},
|
|
27951
|
-
{
|
|
27952
|
-
method: "DELETE",
|
|
27953
|
-
path: "/admin/api/collections/:id",
|
|
27954
|
-
description: "Delete a collection (must be empty)",
|
|
27955
|
-
authentication: true,
|
|
27956
|
-
category: "Admin"
|
|
27957
|
-
},
|
|
27958
|
-
{
|
|
27959
|
-
method: "GET",
|
|
27960
|
-
path: "/admin/api/migrations/status",
|
|
27961
|
-
description: "Get database migration status",
|
|
27962
|
-
authentication: true,
|
|
27963
|
-
category: "Admin"
|
|
27964
|
-
},
|
|
27965
|
-
{
|
|
27966
|
-
method: "POST",
|
|
27967
|
-
path: "/admin/api/migrations/run",
|
|
27968
|
-
description: "Run pending database migrations",
|
|
27969
|
-
authentication: true,
|
|
27970
|
-
category: "Admin"
|
|
27971
|
-
},
|
|
27972
|
-
// System endpoints
|
|
27973
|
-
{
|
|
27974
|
-
method: "GET",
|
|
27975
|
-
path: "/health",
|
|
27976
|
-
description: "Health check endpoint for monitoring",
|
|
27977
|
-
authentication: false,
|
|
27978
|
-
category: "System"
|
|
27979
|
-
},
|
|
27980
|
-
{
|
|
27981
|
-
method: "GET",
|
|
27982
|
-
path: "/api/health",
|
|
27983
|
-
description: "API health check with schema information",
|
|
27984
|
-
authentication: false,
|
|
27985
|
-
category: "System"
|
|
27986
|
-
},
|
|
27987
|
-
{
|
|
27988
|
-
method: "GET",
|
|
27989
|
-
path: "/api",
|
|
27990
|
-
description: "API root - returns API information and OpenAPI spec",
|
|
27991
|
-
authentication: false,
|
|
27992
|
-
category: "System"
|
|
27993
|
-
}
|
|
27994
|
-
];
|
|
28768
|
+
router2.use("*", chunkIT2TC4ZD_cjs.requireAuth());
|
|
27995
28769
|
router2.get("/", async (c) => {
|
|
27996
28770
|
const user = c.get("user");
|
|
27997
28771
|
try {
|
|
28772
|
+
const app2 = chunk64APW3DW_cjs.getAppInstance();
|
|
28773
|
+
const endpoints = chunk64APW3DW_cjs.buildRouteList(app2);
|
|
27998
28774
|
const pageData = {
|
|
27999
|
-
endpoints
|
|
28775
|
+
endpoints,
|
|
28000
28776
|
user: user ? {
|
|
28001
28777
|
name: user.email.split("@")[0] || user.email,
|
|
28002
28778
|
email: user.email,
|
|
@@ -28076,5 +28852,5 @@ exports.router = router;
|
|
|
28076
28852
|
exports.router2 = router2;
|
|
28077
28853
|
exports.test_cleanup_default = test_cleanup_default;
|
|
28078
28854
|
exports.userRoutes = userRoutes;
|
|
28079
|
-
//# sourceMappingURL=chunk-
|
|
28080
|
-
//# sourceMappingURL=chunk-
|
|
28855
|
+
//# sourceMappingURL=chunk-RCA6R6VE.cjs.map
|
|
28856
|
+
//# sourceMappingURL=chunk-RCA6R6VE.cjs.map
|