@sonicjs-cms/core 2.4.0 → 2.5.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/{chunk-CPXAVWCU.js → chunk-3YUHXWSG.js} +278 -3
- package/dist/chunk-3YUHXWSG.js.map +1 -0
- package/dist/chunk-AI2JJIJX.cjs +211 -0
- package/dist/chunk-AI2JJIJX.cjs.map +1 -0
- package/dist/{chunk-VNCYCH3H.js → chunk-BHNDALCA.js} +56 -4
- package/dist/chunk-BHNDALCA.js.map +1 -0
- package/dist/{chunk-2MI3LZFH.cjs → chunk-I4V3VZWF.cjs} +46 -2
- package/dist/chunk-I4V3VZWF.cjs.map +1 -0
- package/dist/{chunk-DTLB6UIH.cjs → chunk-LWG2MWDA.cjs} +280 -2
- package/dist/chunk-LWG2MWDA.cjs.map +1 -0
- package/dist/{chunk-FT6NBHNX.js → chunk-OJZ45OJD.js} +507 -275
- package/dist/chunk-OJZ45OJD.js.map +1 -0
- package/dist/chunk-QDBNW7KQ.js +209 -0
- package/dist/chunk-QDBNW7KQ.js.map +1 -0
- package/dist/{chunk-DXM575E2.js → chunk-TJTWRO4G.js} +5 -5
- package/dist/chunk-TJTWRO4G.js.map +1 -0
- package/dist/{chunk-A4SVOGG6.cjs → chunk-UAQL2VWX.cjs} +591 -360
- package/dist/chunk-UAQL2VWX.cjs.map +1 -0
- package/dist/{chunk-D2NLCPO2.js → chunk-VEL7QRYI.js} +46 -2
- package/dist/chunk-VEL7QRYI.js.map +1 -0
- package/dist/{chunk-7I5INVNR.cjs → chunk-YYV3XQOQ.cjs} +6 -6
- package/dist/chunk-YYV3XQOQ.cjs.map +1 -0
- package/dist/{chunk-FYEDK7K7.cjs → chunk-ZWV3EBZ7.cjs} +58 -3
- package/dist/chunk-ZWV3EBZ7.cjs.map +1 -0
- package/dist/{collection-config-FLlGtsh9.d.cts → collection-config-B6gMPunn.d.cts} +9 -1
- package/dist/{collection-config-FLlGtsh9.d.ts → collection-config-B6gMPunn.d.ts} +9 -1
- package/dist/index.cjs +90 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +12 -9
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +23 -23
- package/dist/middleware.js +2 -2
- package/dist/migrations-NIEUFG44.cjs +13 -0
- package/dist/{migrations-32QAYLWJ.cjs.map → migrations-NIEUFG44.cjs.map} +1 -1
- package/dist/migrations-TGZKJKV4.js +4 -0
- package/dist/{migrations-57ZHBQ4X.js.map → migrations-TGZKJKV4.js.map} +1 -1
- package/dist/{plugin-bootstrap-C0E3jdz-.d.cts → plugin-bootstrap-SHsdjE6X.d.cts} +1 -1
- package/dist/{plugin-bootstrap-CDh0JHtW.d.ts → plugin-bootstrap-dYhD9fQR.d.ts} +1 -1
- package/dist/plugin-manager-Baa6xXqB.d.ts +328 -0
- package/dist/plugin-manager-vBal9Zip.d.cts +328 -0
- package/dist/plugins.cjs +20 -7
- package/dist/plugins.d.cts +53 -310
- package/dist/plugins.d.ts +53 -310
- package/dist/plugins.js +2 -1
- package/dist/routes.cjs +25 -24
- package/dist/routes.js +5 -4
- package/dist/services.cjs +2 -2
- package/dist/services.d.cts +2 -2
- package/dist/services.d.ts +2 -2
- package/dist/services.js +1 -1
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utils.cjs +23 -11
- package/dist/utils.d.cts +38 -1
- package/dist/utils.d.ts +38 -1
- package/dist/utils.js +1 -1
- package/migrations/027_fix_slug_field_type.sql +18 -0
- package/migrations/028_fix_slug_field_type_in_schemas.sql +30 -0
- package/package.json +2 -1
- package/dist/chunk-2MI3LZFH.cjs.map +0 -1
- package/dist/chunk-7I5INVNR.cjs.map +0 -1
- package/dist/chunk-A4SVOGG6.cjs.map +0 -1
- package/dist/chunk-CPXAVWCU.js.map +0 -1
- package/dist/chunk-D2NLCPO2.js.map +0 -1
- package/dist/chunk-DTLB6UIH.cjs.map +0 -1
- package/dist/chunk-DXM575E2.js.map +0 -1
- package/dist/chunk-FT6NBHNX.js.map +0 -1
- package/dist/chunk-FYEDK7K7.cjs.map +0 -1
- package/dist/chunk-VNCYCH3H.js.map +0 -1
- package/dist/migrations-32QAYLWJ.cjs +0 -13
- package/dist/migrations-57ZHBQ4X.js +0 -4
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunk7FOAMNTI_cjs = require('./chunk-7FOAMNTI.cjs');
|
|
4
|
-
var
|
|
4
|
+
var chunkYYV3XQOQ_cjs = require('./chunk-YYV3XQOQ.cjs');
|
|
5
5
|
var chunkILZ3DP4I_cjs = require('./chunk-ILZ3DP4I.cjs');
|
|
6
|
-
var
|
|
6
|
+
var chunkI4V3VZWF_cjs = require('./chunk-I4V3VZWF.cjs');
|
|
7
7
|
var chunkAZLU3ROK_cjs = require('./chunk-AZLU3ROK.cjs');
|
|
8
|
-
var
|
|
8
|
+
var chunkAI2JJIJX_cjs = require('./chunk-AI2JJIJX.cjs');
|
|
9
|
+
var chunkZWV3EBZ7_cjs = require('./chunk-ZWV3EBZ7.cjs');
|
|
9
10
|
var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
|
|
10
11
|
var hono = require('hono');
|
|
11
12
|
var cors = require('hono/cors');
|
|
@@ -16,6 +17,37 @@ var html = require('hono/html');
|
|
|
16
17
|
// src/schemas/index.ts
|
|
17
18
|
var schemaDefinitions = [];
|
|
18
19
|
var apiContentCrudRoutes = new hono.Hono();
|
|
20
|
+
apiContentCrudRoutes.get("/check-slug", async (c) => {
|
|
21
|
+
try {
|
|
22
|
+
const db = c.env.DB;
|
|
23
|
+
const collectionId = c.req.query("collectionId");
|
|
24
|
+
const slug = c.req.query("slug");
|
|
25
|
+
const excludeId = c.req.query("excludeId");
|
|
26
|
+
if (!collectionId || !slug) {
|
|
27
|
+
return c.json({ error: "collectionId and slug are required" }, 400);
|
|
28
|
+
}
|
|
29
|
+
let query = "SELECT id FROM content WHERE collection_id = ? AND slug = ?";
|
|
30
|
+
const params = [collectionId, slug];
|
|
31
|
+
if (excludeId) {
|
|
32
|
+
query += " AND id != ?";
|
|
33
|
+
params.push(excludeId);
|
|
34
|
+
}
|
|
35
|
+
const existing = await db.prepare(query).bind(...params).first();
|
|
36
|
+
if (existing) {
|
|
37
|
+
return c.json({
|
|
38
|
+
available: false,
|
|
39
|
+
message: "This URL slug is already in use in this collection"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return c.json({ available: true });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error("Error checking slug:", error);
|
|
45
|
+
return c.json({
|
|
46
|
+
error: "Failed to check slug availability",
|
|
47
|
+
details: error instanceof Error ? error.message : String(error)
|
|
48
|
+
}, 500);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
19
51
|
apiContentCrudRoutes.get("/:id", async (c) => {
|
|
20
52
|
try {
|
|
21
53
|
const id = c.req.param("id");
|
|
@@ -44,7 +76,7 @@ apiContentCrudRoutes.get("/:id", async (c) => {
|
|
|
44
76
|
}, 500);
|
|
45
77
|
}
|
|
46
78
|
});
|
|
47
|
-
apiContentCrudRoutes.post("/",
|
|
79
|
+
apiContentCrudRoutes.post("/", chunkYYV3XQOQ_cjs.requireAuth(), async (c) => {
|
|
48
80
|
try {
|
|
49
81
|
const db = c.env.DB;
|
|
50
82
|
const user = c.get("user");
|
|
@@ -110,7 +142,7 @@ apiContentCrudRoutes.post("/", chunk7I5INVNR_cjs.requireAuth(), async (c) => {
|
|
|
110
142
|
}, 500);
|
|
111
143
|
}
|
|
112
144
|
});
|
|
113
|
-
apiContentCrudRoutes.put("/:id",
|
|
145
|
+
apiContentCrudRoutes.put("/:id", chunkYYV3XQOQ_cjs.requireAuth(), async (c) => {
|
|
114
146
|
try {
|
|
115
147
|
const id = c.req.param("id");
|
|
116
148
|
const db = c.env.DB;
|
|
@@ -174,7 +206,7 @@ apiContentCrudRoutes.put("/:id", chunk7I5INVNR_cjs.requireAuth(), async (c) => {
|
|
|
174
206
|
}, 500);
|
|
175
207
|
}
|
|
176
208
|
});
|
|
177
|
-
apiContentCrudRoutes.delete("/:id",
|
|
209
|
+
apiContentCrudRoutes.delete("/:id", chunkYYV3XQOQ_cjs.requireAuth(), async (c) => {
|
|
178
210
|
try {
|
|
179
211
|
const id = c.req.param("id");
|
|
180
212
|
const db = c.env.DB;
|
|
@@ -210,7 +242,7 @@ apiRoutes.use("*", async (c, next) => {
|
|
|
210
242
|
c.header("X-Response-Time", `${totalTime}ms`);
|
|
211
243
|
});
|
|
212
244
|
apiRoutes.use("*", async (c, next) => {
|
|
213
|
-
const cacheEnabled = await
|
|
245
|
+
const cacheEnabled = await chunkYYV3XQOQ_cjs.isPluginActive(c.env.DB, "core-cache");
|
|
214
246
|
c.set("cacheEnabled", cacheEnabled);
|
|
215
247
|
await next();
|
|
216
248
|
});
|
|
@@ -335,12 +367,12 @@ apiRoutes.get("/content", async (c) => {
|
|
|
335
367
|
});
|
|
336
368
|
}
|
|
337
369
|
}
|
|
338
|
-
const filter =
|
|
370
|
+
const filter = chunkZWV3EBZ7_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
|
|
339
371
|
if (!filter.limit) {
|
|
340
372
|
filter.limit = 50;
|
|
341
373
|
}
|
|
342
374
|
filter.limit = Math.min(filter.limit, 1e3);
|
|
343
|
-
const builder3 = new
|
|
375
|
+
const builder3 = new chunkZWV3EBZ7_cjs.QueryFilterBuilder();
|
|
344
376
|
const queryResult = builder3.build("content", filter);
|
|
345
377
|
if (queryResult.errors.length > 0) {
|
|
346
378
|
return c.json({
|
|
@@ -427,7 +459,7 @@ apiRoutes.get("/collections/:collection/content", async (c) => {
|
|
|
427
459
|
if (!collectionResult) {
|
|
428
460
|
return c.json({ error: "Collection not found" }, 404);
|
|
429
461
|
}
|
|
430
|
-
const filter =
|
|
462
|
+
const filter = chunkZWV3EBZ7_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
|
|
431
463
|
if (!filter.where) {
|
|
432
464
|
filter.where = { and: [] };
|
|
433
465
|
}
|
|
@@ -443,7 +475,7 @@ apiRoutes.get("/collections/:collection/content", async (c) => {
|
|
|
443
475
|
filter.limit = 50;
|
|
444
476
|
}
|
|
445
477
|
filter.limit = Math.min(filter.limit, 1e3);
|
|
446
|
-
const builder3 = new
|
|
478
|
+
const builder3 = new chunkZWV3EBZ7_cjs.QueryFilterBuilder();
|
|
447
479
|
const queryResult = builder3.build("content", filter);
|
|
448
480
|
if (queryResult.errors.length > 0) {
|
|
449
481
|
return c.json({
|
|
@@ -568,7 +600,7 @@ var fileValidationSchema = zod.z.object({
|
|
|
568
600
|
// 50MB max
|
|
569
601
|
});
|
|
570
602
|
var apiMediaRoutes = new hono.Hono();
|
|
571
|
-
apiMediaRoutes.use("*",
|
|
603
|
+
apiMediaRoutes.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
572
604
|
apiMediaRoutes.post("/upload", async (c) => {
|
|
573
605
|
try {
|
|
574
606
|
const user = c.get("user");
|
|
@@ -1312,8 +1344,8 @@ apiSystemRoutes.get("/env", (c) => {
|
|
|
1312
1344
|
});
|
|
1313
1345
|
var api_system_default = apiSystemRoutes;
|
|
1314
1346
|
var adminApiRoutes = new hono.Hono();
|
|
1315
|
-
adminApiRoutes.use("*",
|
|
1316
|
-
adminApiRoutes.use("*",
|
|
1347
|
+
adminApiRoutes.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
1348
|
+
adminApiRoutes.use("*", chunkYYV3XQOQ_cjs.requireRole(["admin", "editor"]));
|
|
1317
1349
|
adminApiRoutes.get("/stats", async (c) => {
|
|
1318
1350
|
try {
|
|
1319
1351
|
const db = c.env.DB;
|
|
@@ -1722,7 +1754,7 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
|
|
|
1722
1754
|
});
|
|
1723
1755
|
adminApiRoutes.get("/migrations/status", async (c) => {
|
|
1724
1756
|
try {
|
|
1725
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1757
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-NIEUFG44.cjs');
|
|
1726
1758
|
const db = c.env.DB;
|
|
1727
1759
|
const migrationService = new MigrationService2(db);
|
|
1728
1760
|
const status = await migrationService.getMigrationStatus();
|
|
@@ -1747,7 +1779,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
1747
1779
|
error: "Unauthorized. Admin access required."
|
|
1748
1780
|
}, 403);
|
|
1749
1781
|
}
|
|
1750
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1782
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-NIEUFG44.cjs');
|
|
1751
1783
|
const db = c.env.DB;
|
|
1752
1784
|
const migrationService = new MigrationService2(db);
|
|
1753
1785
|
const result = await migrationService.runPendingMigrations();
|
|
@@ -1766,7 +1798,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
1766
1798
|
});
|
|
1767
1799
|
adminApiRoutes.get("/migrations/validate", async (c) => {
|
|
1768
1800
|
try {
|
|
1769
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1801
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-NIEUFG44.cjs');
|
|
1770
1802
|
const db = c.env.DB;
|
|
1771
1803
|
const migrationService = new MigrationService2(db);
|
|
1772
1804
|
const validation = await migrationService.validateSchema();
|
|
@@ -2289,7 +2321,7 @@ authRoutes.post(
|
|
|
2289
2321
|
if (existingUser) {
|
|
2290
2322
|
return c.json({ error: "User with this email or username already exists" }, 400);
|
|
2291
2323
|
}
|
|
2292
|
-
const passwordHash = await
|
|
2324
|
+
const passwordHash = await chunkYYV3XQOQ_cjs.AuthManager.hashPassword(password);
|
|
2293
2325
|
const userId = crypto.randomUUID();
|
|
2294
2326
|
const now = /* @__PURE__ */ new Date();
|
|
2295
2327
|
await db.prepare(`
|
|
@@ -2309,7 +2341,7 @@ authRoutes.post(
|
|
|
2309
2341
|
now.getTime(),
|
|
2310
2342
|
now.getTime()
|
|
2311
2343
|
).run();
|
|
2312
|
-
const token = await
|
|
2344
|
+
const token = await chunkYYV3XQOQ_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer");
|
|
2313
2345
|
cookie.setCookie(c, "auth_token", token, {
|
|
2314
2346
|
httpOnly: true,
|
|
2315
2347
|
secure: true,
|
|
@@ -2362,11 +2394,11 @@ authRoutes.post("/login", async (c) => {
|
|
|
2362
2394
|
if (!user) {
|
|
2363
2395
|
return c.json({ error: "Invalid email or password" }, 401);
|
|
2364
2396
|
}
|
|
2365
|
-
const isValidPassword = await
|
|
2397
|
+
const isValidPassword = await chunkYYV3XQOQ_cjs.AuthManager.verifyPassword(password, user.password_hash);
|
|
2366
2398
|
if (!isValidPassword) {
|
|
2367
2399
|
return c.json({ error: "Invalid email or password" }, 401);
|
|
2368
2400
|
}
|
|
2369
|
-
const token = await
|
|
2401
|
+
const token = await chunkYYV3XQOQ_cjs.AuthManager.generateToken(user.id, user.email, user.role);
|
|
2370
2402
|
cookie.setCookie(c, "auth_token", token, {
|
|
2371
2403
|
httpOnly: true,
|
|
2372
2404
|
secure: true,
|
|
@@ -2415,7 +2447,7 @@ authRoutes.get("/logout", (c) => {
|
|
|
2415
2447
|
});
|
|
2416
2448
|
return c.redirect("/auth/login?message=You have been logged out successfully");
|
|
2417
2449
|
});
|
|
2418
|
-
authRoutes.get("/me",
|
|
2450
|
+
authRoutes.get("/me", chunkYYV3XQOQ_cjs.requireAuth(), async (c) => {
|
|
2419
2451
|
try {
|
|
2420
2452
|
const user = c.get("user");
|
|
2421
2453
|
if (!user) {
|
|
@@ -2432,13 +2464,13 @@ authRoutes.get("/me", chunk7I5INVNR_cjs.requireAuth(), async (c) => {
|
|
|
2432
2464
|
return c.json({ error: "Failed to get user" }, 500);
|
|
2433
2465
|
}
|
|
2434
2466
|
});
|
|
2435
|
-
authRoutes.post("/refresh",
|
|
2467
|
+
authRoutes.post("/refresh", chunkYYV3XQOQ_cjs.requireAuth(), async (c) => {
|
|
2436
2468
|
try {
|
|
2437
2469
|
const user = c.get("user");
|
|
2438
2470
|
if (!user) {
|
|
2439
2471
|
return c.json({ error: "Not authenticated" }, 401);
|
|
2440
2472
|
}
|
|
2441
|
-
const token = await
|
|
2473
|
+
const token = await chunkYYV3XQOQ_cjs.AuthManager.generateToken(user.userId, user.email, user.role);
|
|
2442
2474
|
cookie.setCookie(c, "auth_token", token, {
|
|
2443
2475
|
httpOnly: true,
|
|
2444
2476
|
secure: true,
|
|
@@ -2498,7 +2530,7 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2498
2530
|
</div>
|
|
2499
2531
|
`);
|
|
2500
2532
|
}
|
|
2501
|
-
const passwordHash = await
|
|
2533
|
+
const passwordHash = await chunkYYV3XQOQ_cjs.AuthManager.hashPassword(password);
|
|
2502
2534
|
const role = isFirstUser ? "admin" : "viewer";
|
|
2503
2535
|
const userId = crypto.randomUUID();
|
|
2504
2536
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -2521,7 +2553,7 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2521
2553
|
if (isFirstUser) {
|
|
2522
2554
|
setAdminExists();
|
|
2523
2555
|
}
|
|
2524
|
-
const token = await
|
|
2556
|
+
const token = await chunkYYV3XQOQ_cjs.AuthManager.generateToken(userId, normalizedEmail, role);
|
|
2525
2557
|
cookie.setCookie(c, "auth_token", token, {
|
|
2526
2558
|
httpOnly: true,
|
|
2527
2559
|
secure: false,
|
|
@@ -2573,7 +2605,7 @@ authRoutes.post("/login/form", async (c) => {
|
|
|
2573
2605
|
</div>
|
|
2574
2606
|
`);
|
|
2575
2607
|
}
|
|
2576
|
-
const isValidPassword = await
|
|
2608
|
+
const isValidPassword = await chunkYYV3XQOQ_cjs.AuthManager.verifyPassword(password, user.password_hash);
|
|
2577
2609
|
if (!isValidPassword) {
|
|
2578
2610
|
return c.html(html.html`
|
|
2579
2611
|
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
|
@@ -2581,7 +2613,7 @@ authRoutes.post("/login/form", async (c) => {
|
|
|
2581
2613
|
</div>
|
|
2582
2614
|
`);
|
|
2583
2615
|
}
|
|
2584
|
-
const token = await
|
|
2616
|
+
const token = await chunkYYV3XQOQ_cjs.AuthManager.generateToken(user.id, user.email, user.role);
|
|
2585
2617
|
cookie.setCookie(c, "auth_token", token, {
|
|
2586
2618
|
httpOnly: true,
|
|
2587
2619
|
secure: false,
|
|
@@ -2640,7 +2672,7 @@ authRoutes.post("/seed-admin", async (c) => {
|
|
|
2640
2672
|
`).run();
|
|
2641
2673
|
const existingAdmin = await db.prepare("SELECT id FROM users WHERE email = ? OR username = ?").bind("admin@sonicjs.com", "admin").first();
|
|
2642
2674
|
if (existingAdmin) {
|
|
2643
|
-
const passwordHash2 = await
|
|
2675
|
+
const passwordHash2 = await chunkYYV3XQOQ_cjs.AuthManager.hashPassword("sonicjs!");
|
|
2644
2676
|
await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(passwordHash2, Date.now(), existingAdmin.id).run();
|
|
2645
2677
|
setAdminExists();
|
|
2646
2678
|
return c.json({
|
|
@@ -2653,7 +2685,7 @@ authRoutes.post("/seed-admin", async (c) => {
|
|
|
2653
2685
|
}
|
|
2654
2686
|
});
|
|
2655
2687
|
}
|
|
2656
|
-
const passwordHash = await
|
|
2688
|
+
const passwordHash = await chunkYYV3XQOQ_cjs.AuthManager.hashPassword("sonicjs!");
|
|
2657
2689
|
const userId = "admin-user-id";
|
|
2658
2690
|
const now = Date.now();
|
|
2659
2691
|
const adminEmail = "admin@sonicjs.com".toLowerCase();
|
|
@@ -2874,7 +2906,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
2874
2906
|
if (existingUsername) {
|
|
2875
2907
|
return c.json({ error: "Username is already taken" }, 400);
|
|
2876
2908
|
}
|
|
2877
|
-
const passwordHash = await
|
|
2909
|
+
const passwordHash = await chunkYYV3XQOQ_cjs.AuthManager.hashPassword(password);
|
|
2878
2910
|
const updateStmt = db.prepare(`
|
|
2879
2911
|
UPDATE users SET
|
|
2880
2912
|
username = ?,
|
|
@@ -2893,7 +2925,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
2893
2925
|
Date.now(),
|
|
2894
2926
|
invitedUser.id
|
|
2895
2927
|
).run();
|
|
2896
|
-
const authToken = await
|
|
2928
|
+
const authToken = await chunkYYV3XQOQ_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role);
|
|
2897
2929
|
cookie.setCookie(c, "auth_token", authToken, {
|
|
2898
2930
|
httpOnly: true,
|
|
2899
2931
|
secure: true,
|
|
@@ -3123,7 +3155,7 @@ authRoutes.post("/reset-password", async (c) => {
|
|
|
3123
3155
|
if (Date.now() > user.password_reset_expires) {
|
|
3124
3156
|
return c.json({ error: "Reset token has expired" }, 400);
|
|
3125
3157
|
}
|
|
3126
|
-
const newPasswordHash = await
|
|
3158
|
+
const newPasswordHash = await chunkYYV3XQOQ_cjs.AuthManager.hashPassword(password);
|
|
3127
3159
|
try {
|
|
3128
3160
|
const historyStmt = db.prepare(`
|
|
3129
3161
|
INSERT INTO password_history (id, user_id, password_hash, created_at)
|
|
@@ -3385,7 +3417,7 @@ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
|
|
|
3385
3417
|
|
|
3386
3418
|
// src/templates/components/dynamic-field.template.ts
|
|
3387
3419
|
function renderDynamicField(field, options = {}) {
|
|
3388
|
-
const { value = "", errors = [], disabled = false, className = "", pluginStatuses = {} } = options;
|
|
3420
|
+
const { value = "", errors = [], disabled = false, className = "", pluginStatuses = {}, collectionId = "", contentId = "" } = options;
|
|
3389
3421
|
const opts = field.field_options || {};
|
|
3390
3422
|
const required = field.is_required ? "required" : "";
|
|
3391
3423
|
const baseClasses = `w-full rounded-lg px-3 py-2 text-sm text-zinc-950 dark:text-white bg-white dark:bg-zinc-800 shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow ${className}`;
|
|
@@ -3638,67 +3670,171 @@ function renderDynamicField(field, options = {}) {
|
|
|
3638
3670
|
`;
|
|
3639
3671
|
break;
|
|
3640
3672
|
case "slug":
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3673
|
+
const slugPattern = opts.pattern || "^[a-z0-9-]+$";
|
|
3674
|
+
const collectionIdValue = collectionId || opts.collectionId || "";
|
|
3675
|
+
const contentIdValue = contentId || opts.contentId || "";
|
|
3676
|
+
const isEditMode = !!value;
|
|
3644
3677
|
fieldHTML = `
|
|
3645
|
-
<
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3678
|
+
<div class="slug-field-container">
|
|
3679
|
+
<input
|
|
3680
|
+
type="text"
|
|
3681
|
+
id="${fieldId}"
|
|
3682
|
+
name="${fieldName}"
|
|
3683
|
+
value="${escapeHtml2(value)}"
|
|
3684
|
+
placeholder="${opts.placeholder || "url-friendly-slug"}"
|
|
3685
|
+
maxlength="${opts.maxLength || 100}"
|
|
3686
|
+
data-pattern="${slugPattern}"
|
|
3687
|
+
data-collection-id="${collectionIdValue}"
|
|
3688
|
+
data-content-id="${contentIdValue}"
|
|
3689
|
+
data-is-edit-mode="${isEditMode}"
|
|
3690
|
+
class="${baseClasses} ${errorClasses}"
|
|
3691
|
+
${required}
|
|
3692
|
+
${disabled ? "disabled" : ""}
|
|
3693
|
+
>
|
|
3694
|
+
<div id="${fieldId}-status" class="slug-status mt-1 text-sm min-h-[20px]"></div>
|
|
3695
|
+
<button
|
|
3696
|
+
type="button"
|
|
3697
|
+
class="regenerate-slug-btn mt-2 text-sm text-cyan-600 dark:text-cyan-400 hover:text-cyan-700 dark:hover:text-cyan-300 flex items-center gap-1 transition-colors"
|
|
3698
|
+
onclick="window.regenerateSlugFromTitle_${fieldId.replace(/-/g, "_")}()"
|
|
3699
|
+
>
|
|
3700
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
3701
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
|
3702
|
+
</svg>
|
|
3703
|
+
Regenerate from title
|
|
3704
|
+
</button>
|
|
3705
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">Use lowercase letters, numbers, and hyphens only</p>
|
|
3706
|
+
</div>
|
|
3707
|
+
|
|
3658
3708
|
<script>
|
|
3659
3709
|
(function() {
|
|
3660
|
-
const
|
|
3710
|
+
const slugField = document.getElementById('${fieldId}');
|
|
3711
|
+
const statusDiv = document.getElementById('${fieldId}-status');
|
|
3712
|
+
const isEditMode = slugField.dataset.isEditMode === 'true';
|
|
3661
3713
|
const pattern = new RegExp('${slugPattern}');
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
})();
|
|
3675
|
-
|
|
3676
|
-
function generateSlugFromTitle(slugFieldId) {
|
|
3677
|
-
const titleField = document.querySelector('input[name="title"]');
|
|
3678
|
-
const slugField = document.getElementById(slugFieldId);
|
|
3679
|
-
if (titleField && slugField) {
|
|
3680
|
-
const slug = titleField.value
|
|
3714
|
+
const collectionId = slugField.dataset.collectionId;
|
|
3715
|
+
const contentId = slugField.dataset.contentId;
|
|
3716
|
+
|
|
3717
|
+
let checkTimeout;
|
|
3718
|
+
let lastCheckedSlug = '';
|
|
3719
|
+
let manuallyEdited = false;
|
|
3720
|
+
|
|
3721
|
+
// Shared slug generation function
|
|
3722
|
+
function generateSlug(text) {
|
|
3723
|
+
if (!text) return '';
|
|
3724
|
+
|
|
3725
|
+
return text
|
|
3681
3726
|
.toLowerCase()
|
|
3727
|
+
.normalize('NFD')
|
|
3728
|
+
.replace(/[\\u0300-\\u036f]/g, '')
|
|
3682
3729
|
.replace(/[^a-z0-9\\s_-]/g, '')
|
|
3683
3730
|
.replace(/\\s+/g, '-')
|
|
3684
3731
|
.replace(/[-_]+/g, '-')
|
|
3685
|
-
.replace(/^[-_]
|
|
3686
|
-
|
|
3732
|
+
.replace(/^[-_]+|[-_]+$/g, '')
|
|
3733
|
+
.substring(0, 100);
|
|
3687
3734
|
}
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3735
|
+
|
|
3736
|
+
// Check if slug is available
|
|
3737
|
+
async function checkSlugAvailability(slug) {
|
|
3738
|
+
if (!slug || !collectionId) return;
|
|
3739
|
+
|
|
3740
|
+
// Don't check if it's the same as last time
|
|
3741
|
+
if (slug === lastCheckedSlug) return;
|
|
3742
|
+
lastCheckedSlug = slug;
|
|
3743
|
+
|
|
3744
|
+
try {
|
|
3745
|
+
// Show checking status
|
|
3746
|
+
statusDiv.innerHTML = '<span class="text-gray-400">\u23F3 Checking availability...</span>';
|
|
3747
|
+
|
|
3748
|
+
// Build URL
|
|
3749
|
+
let url = \`/api/content/check-slug?collectionId=\${encodeURIComponent(collectionId)}&slug=\${encodeURIComponent(slug)}\`;
|
|
3750
|
+
if (contentId) {
|
|
3751
|
+
url += \`&excludeId=\${encodeURIComponent(contentId)}\`;
|
|
3698
3752
|
}
|
|
3699
|
-
|
|
3753
|
+
|
|
3754
|
+
const response = await fetch(url);
|
|
3755
|
+
const data = await response.json();
|
|
3756
|
+
|
|
3757
|
+
if (data.available) {
|
|
3758
|
+
statusDiv.innerHTML = '<span class="text-green-500 dark:text-green-400">\u2713 Available</span>';
|
|
3759
|
+
slugField.setCustomValidity('');
|
|
3760
|
+
} else {
|
|
3761
|
+
statusDiv.innerHTML = \`<span class="text-red-500 dark:text-red-400">\u2717 \${data.message || 'Already in use'}</span>\`;
|
|
3762
|
+
slugField.setCustomValidity(data.message || 'This slug is already in use');
|
|
3763
|
+
}
|
|
3764
|
+
} catch (error) {
|
|
3765
|
+
console.error('Error checking slug:', error);
|
|
3766
|
+
statusDiv.innerHTML = '<span class="text-yellow-500 dark:text-yellow-400">\u26A0 Could not verify</span>';
|
|
3767
|
+
}
|
|
3700
3768
|
}
|
|
3701
|
-
|
|
3769
|
+
|
|
3770
|
+
// Format validation and duplicate checking
|
|
3771
|
+
slugField.addEventListener('input', function() {
|
|
3772
|
+
const value = this.value;
|
|
3773
|
+
|
|
3774
|
+
// Mark as manually edited if user types directly
|
|
3775
|
+
if (document.activeElement === this) {
|
|
3776
|
+
manuallyEdited = true;
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
// Clear status if empty
|
|
3780
|
+
if (!value) {
|
|
3781
|
+
statusDiv.innerHTML = '';
|
|
3782
|
+
this.setCustomValidity('');
|
|
3783
|
+
return;
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3786
|
+
// Pattern validation
|
|
3787
|
+
if (!pattern.test(value)) {
|
|
3788
|
+
this.setCustomValidity('Please use only lowercase letters, numbers, and hyphens.');
|
|
3789
|
+
statusDiv.innerHTML = '<span class="text-red-500 dark:text-red-400">\u2717 Invalid format</span>';
|
|
3790
|
+
return;
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
// Debounce the availability check
|
|
3794
|
+
clearTimeout(checkTimeout);
|
|
3795
|
+
checkTimeout = setTimeout(() => {
|
|
3796
|
+
checkSlugAvailability(value);
|
|
3797
|
+
}, 500); // Wait 500ms after user stops typing
|
|
3798
|
+
});
|
|
3799
|
+
|
|
3800
|
+
// Initial check if field has value
|
|
3801
|
+
if (slugField.value) {
|
|
3802
|
+
checkSlugAvailability(slugField.value);
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
// Auto-generate only in create mode
|
|
3806
|
+
// Wait for all fields to be rendered before attaching listeners
|
|
3807
|
+
if (!isEditMode) {
|
|
3808
|
+
// Use setTimeout to ensure all fields in the form are rendered
|
|
3809
|
+
setTimeout(() => {
|
|
3810
|
+
const titleField = document.querySelector('input[name="title"]');
|
|
3811
|
+
if (titleField) {
|
|
3812
|
+
titleField.addEventListener('input', function() {
|
|
3813
|
+
if (!manuallyEdited) {
|
|
3814
|
+
const slug = generateSlug(this.value);
|
|
3815
|
+
slugField.value = slug;
|
|
3816
|
+
|
|
3817
|
+
// Trigger validation and duplicate check
|
|
3818
|
+
slugField.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3819
|
+
}
|
|
3820
|
+
});
|
|
3821
|
+
}
|
|
3822
|
+
}, 0);
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
// Global function for regenerate button
|
|
3826
|
+
window.regenerateSlugFromTitle_${fieldId.replace(/-/g, "_")} = function() {
|
|
3827
|
+
const titleField = document.querySelector('input[name="title"]');
|
|
3828
|
+
if (titleField && slugField) {
|
|
3829
|
+
const slug = generateSlug(titleField.value);
|
|
3830
|
+
slugField.value = slug;
|
|
3831
|
+
manuallyEdited = false;
|
|
3832
|
+
|
|
3833
|
+
// Trigger validation and duplicate check
|
|
3834
|
+
slugField.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3835
|
+
}
|
|
3836
|
+
};
|
|
3837
|
+
})();
|
|
3702
3838
|
</script>
|
|
3703
3839
|
`;
|
|
3704
3840
|
break;
|
|
@@ -3831,210 +3967,9 @@ function escapeHtml2(text) {
|
|
|
3831
3967
|
"'": "'"
|
|
3832
3968
|
})[char] || char);
|
|
3833
3969
|
}
|
|
3834
|
-
var PluginBuilder = class _PluginBuilder {
|
|
3835
|
-
plugin;
|
|
3836
|
-
constructor(options) {
|
|
3837
|
-
this.plugin = {
|
|
3838
|
-
name: options.name,
|
|
3839
|
-
version: options.version,
|
|
3840
|
-
description: options.description,
|
|
3841
|
-
author: options.author,
|
|
3842
|
-
dependencies: options.dependencies,
|
|
3843
|
-
routes: [],
|
|
3844
|
-
middleware: [],
|
|
3845
|
-
models: [],
|
|
3846
|
-
services: [],
|
|
3847
|
-
adminPages: [],
|
|
3848
|
-
adminComponents: [],
|
|
3849
|
-
menuItems: [],
|
|
3850
|
-
hooks: []
|
|
3851
|
-
};
|
|
3852
|
-
}
|
|
3853
|
-
/**
|
|
3854
|
-
* Create a new plugin builder
|
|
3855
|
-
*/
|
|
3856
|
-
static create(options) {
|
|
3857
|
-
return new _PluginBuilder(options);
|
|
3858
|
-
}
|
|
3859
|
-
/**
|
|
3860
|
-
* Add metadata to the plugin
|
|
3861
|
-
*/
|
|
3862
|
-
metadata(metadata) {
|
|
3863
|
-
Object.assign(this.plugin, metadata);
|
|
3864
|
-
return this;
|
|
3865
|
-
}
|
|
3866
|
-
/**
|
|
3867
|
-
* Add routes to plugin
|
|
3868
|
-
*/
|
|
3869
|
-
addRoutes(routes) {
|
|
3870
|
-
this.plugin.routes = [...this.plugin.routes || [], ...routes];
|
|
3871
|
-
return this;
|
|
3872
|
-
}
|
|
3873
|
-
/**
|
|
3874
|
-
* Add a single route to plugin
|
|
3875
|
-
*/
|
|
3876
|
-
addRoute(path, handler, options) {
|
|
3877
|
-
const route = {
|
|
3878
|
-
path,
|
|
3879
|
-
handler,
|
|
3880
|
-
...options
|
|
3881
|
-
};
|
|
3882
|
-
this.plugin.routes = [...this.plugin.routes || [], route];
|
|
3883
|
-
return this;
|
|
3884
|
-
}
|
|
3885
|
-
/**
|
|
3886
|
-
* Add middleware to plugin
|
|
3887
|
-
*/
|
|
3888
|
-
addMiddleware(middleware) {
|
|
3889
|
-
this.plugin.middleware = [...this.plugin.middleware || [], ...middleware];
|
|
3890
|
-
return this;
|
|
3891
|
-
}
|
|
3892
|
-
/**
|
|
3893
|
-
* Add a single middleware to plugin
|
|
3894
|
-
*/
|
|
3895
|
-
addSingleMiddleware(name, handler, options) {
|
|
3896
|
-
const middleware = {
|
|
3897
|
-
name,
|
|
3898
|
-
handler,
|
|
3899
|
-
...options
|
|
3900
|
-
};
|
|
3901
|
-
this.plugin.middleware = [...this.plugin.middleware || [], middleware];
|
|
3902
|
-
return this;
|
|
3903
|
-
}
|
|
3904
|
-
/**
|
|
3905
|
-
* Add models to plugin
|
|
3906
|
-
*/
|
|
3907
|
-
addModels(models) {
|
|
3908
|
-
this.plugin.models = [...this.plugin.models || [], ...models];
|
|
3909
|
-
return this;
|
|
3910
|
-
}
|
|
3911
|
-
/**
|
|
3912
|
-
* Add a single model to plugin
|
|
3913
|
-
*/
|
|
3914
|
-
addModel(name, options) {
|
|
3915
|
-
const model = {
|
|
3916
|
-
name,
|
|
3917
|
-
...options
|
|
3918
|
-
};
|
|
3919
|
-
this.plugin.models = [...this.plugin.models || [], model];
|
|
3920
|
-
return this;
|
|
3921
|
-
}
|
|
3922
|
-
/**
|
|
3923
|
-
* Add services to plugin
|
|
3924
|
-
*/
|
|
3925
|
-
addServices(services) {
|
|
3926
|
-
this.plugin.services = [...this.plugin.services || [], ...services];
|
|
3927
|
-
return this;
|
|
3928
|
-
}
|
|
3929
|
-
/**
|
|
3930
|
-
* Add a single service to plugin
|
|
3931
|
-
*/
|
|
3932
|
-
addService(name, implementation, options) {
|
|
3933
|
-
const service = {
|
|
3934
|
-
name,
|
|
3935
|
-
implementation,
|
|
3936
|
-
...options
|
|
3937
|
-
};
|
|
3938
|
-
this.plugin.services = [...this.plugin.services || [], service];
|
|
3939
|
-
return this;
|
|
3940
|
-
}
|
|
3941
|
-
/**
|
|
3942
|
-
* Add admin pages to plugin
|
|
3943
|
-
*/
|
|
3944
|
-
addAdminPages(pages) {
|
|
3945
|
-
this.plugin.adminPages = [...this.plugin.adminPages || [], ...pages];
|
|
3946
|
-
return this;
|
|
3947
|
-
}
|
|
3948
|
-
/**
|
|
3949
|
-
* Add a single admin page to plugin
|
|
3950
|
-
*/
|
|
3951
|
-
addAdminPage(path, title, component, options) {
|
|
3952
|
-
const page = {
|
|
3953
|
-
path,
|
|
3954
|
-
title,
|
|
3955
|
-
component,
|
|
3956
|
-
...options
|
|
3957
|
-
};
|
|
3958
|
-
this.plugin.adminPages = [...this.plugin.adminPages || [], page];
|
|
3959
|
-
return this;
|
|
3960
|
-
}
|
|
3961
|
-
/**
|
|
3962
|
-
* Add admin components to plugin
|
|
3963
|
-
*/
|
|
3964
|
-
addComponents(components) {
|
|
3965
|
-
this.plugin.adminComponents = [...this.plugin.adminComponents || [], ...components];
|
|
3966
|
-
return this;
|
|
3967
|
-
}
|
|
3968
|
-
/**
|
|
3969
|
-
* Add a single admin component to plugin
|
|
3970
|
-
*/
|
|
3971
|
-
addComponent(name, template, options) {
|
|
3972
|
-
const component = {
|
|
3973
|
-
name,
|
|
3974
|
-
template,
|
|
3975
|
-
...options
|
|
3976
|
-
};
|
|
3977
|
-
this.plugin.adminComponents = [...this.plugin.adminComponents || [], component];
|
|
3978
|
-
return this;
|
|
3979
|
-
}
|
|
3980
|
-
/**
|
|
3981
|
-
* Add menu items to plugin
|
|
3982
|
-
*/
|
|
3983
|
-
addMenuItems(items) {
|
|
3984
|
-
this.plugin.menuItems = [...this.plugin.menuItems || [], ...items];
|
|
3985
|
-
return this;
|
|
3986
|
-
}
|
|
3987
|
-
/**
|
|
3988
|
-
* Add a single menu item to plugin
|
|
3989
|
-
*/
|
|
3990
|
-
addMenuItem(label, path, options) {
|
|
3991
|
-
const menuItem = {
|
|
3992
|
-
label,
|
|
3993
|
-
path,
|
|
3994
|
-
...options
|
|
3995
|
-
};
|
|
3996
|
-
this.plugin.menuItems = [...this.plugin.menuItems || [], menuItem];
|
|
3997
|
-
return this;
|
|
3998
|
-
}
|
|
3999
|
-
/**
|
|
4000
|
-
* Add hooks to plugin
|
|
4001
|
-
*/
|
|
4002
|
-
addHooks(hooks) {
|
|
4003
|
-
this.plugin.hooks = [...this.plugin.hooks || [], ...hooks];
|
|
4004
|
-
return this;
|
|
4005
|
-
}
|
|
4006
|
-
/**
|
|
4007
|
-
* Add a single hook to plugin
|
|
4008
|
-
*/
|
|
4009
|
-
addHook(name, handler, options) {
|
|
4010
|
-
const hook = {
|
|
4011
|
-
name,
|
|
4012
|
-
handler,
|
|
4013
|
-
...options
|
|
4014
|
-
};
|
|
4015
|
-
this.plugin.hooks = [...this.plugin.hooks || [], hook];
|
|
4016
|
-
return this;
|
|
4017
|
-
}
|
|
4018
|
-
/**
|
|
4019
|
-
* Add lifecycle hooks
|
|
4020
|
-
*/
|
|
4021
|
-
lifecycle(hooks) {
|
|
4022
|
-
Object.assign(this.plugin, hooks);
|
|
4023
|
-
return this;
|
|
4024
|
-
}
|
|
4025
|
-
/**
|
|
4026
|
-
* Build the plugin
|
|
4027
|
-
*/
|
|
4028
|
-
build() {
|
|
4029
|
-
if (!this.plugin.name || !this.plugin.version) {
|
|
4030
|
-
throw new Error("Plugin name and version are required");
|
|
4031
|
-
}
|
|
4032
|
-
return this.plugin;
|
|
4033
|
-
}
|
|
4034
|
-
};
|
|
4035
3970
|
|
|
4036
3971
|
// src/plugins/available/tinymce-plugin/index.ts
|
|
4037
|
-
var builder = PluginBuilder.create({
|
|
3972
|
+
var builder = chunkAI2JJIJX_cjs.PluginBuilder.create({
|
|
4038
3973
|
name: "tinymce-plugin",
|
|
4039
3974
|
version: "1.0.0",
|
|
4040
3975
|
description: "Powerful WYSIWYG rich text editor for content creation"
|
|
@@ -4317,7 +4252,7 @@ function getQuillCDN(version = "2.0.2") {
|
|
|
4317
4252
|
`;
|
|
4318
4253
|
}
|
|
4319
4254
|
function createQuillEditorPlugin() {
|
|
4320
|
-
const builder3 = PluginBuilder.create({
|
|
4255
|
+
const builder3 = chunkAI2JJIJX_cjs.PluginBuilder.create({
|
|
4321
4256
|
name: "quill-editor",
|
|
4322
4257
|
version: "1.0.0",
|
|
4323
4258
|
description: "Quill rich text editor integration for SonicJS"
|
|
@@ -4343,7 +4278,7 @@ function createQuillEditorPlugin() {
|
|
|
4343
4278
|
createQuillEditorPlugin();
|
|
4344
4279
|
|
|
4345
4280
|
// src/plugins/available/easy-mdx/index.ts
|
|
4346
|
-
var builder2 = PluginBuilder.create({
|
|
4281
|
+
var builder2 = chunkAI2JJIJX_cjs.PluginBuilder.create({
|
|
4347
4282
|
name: "easy-mdx",
|
|
4348
4283
|
version: "1.0.0",
|
|
4349
4284
|
description: "Lightweight markdown editor with live preview"
|
|
@@ -4575,17 +4510,24 @@ function renderContentFormPage(data) {
|
|
|
4575
4510
|
const coreFieldsHTML = coreFields.sort((a, b) => a.field_order - b.field_order).map((field) => renderDynamicField(field, {
|
|
4576
4511
|
value: getFieldValue(field.field_name),
|
|
4577
4512
|
errors: data.validationErrors?.[field.field_name] || [],
|
|
4578
|
-
pluginStatuses
|
|
4513
|
+
pluginStatuses,
|
|
4514
|
+
collectionId: data.collection.id,
|
|
4515
|
+
contentId: data.id
|
|
4516
|
+
// Pass content ID when editing
|
|
4579
4517
|
}));
|
|
4580
4518
|
const contentFieldsHTML = contentFields.sort((a, b) => a.field_order - b.field_order).map((field) => renderDynamicField(field, {
|
|
4581
4519
|
value: getFieldValue(field.field_name),
|
|
4582
4520
|
errors: data.validationErrors?.[field.field_name] || [],
|
|
4583
|
-
pluginStatuses
|
|
4521
|
+
pluginStatuses,
|
|
4522
|
+
collectionId: data.collection.id,
|
|
4523
|
+
contentId: data.id
|
|
4584
4524
|
}));
|
|
4585
4525
|
const metaFieldsHTML = metaFields.sort((a, b) => a.field_order - b.field_order).map((field) => renderDynamicField(field, {
|
|
4586
4526
|
value: getFieldValue(field.field_name),
|
|
4587
4527
|
errors: data.validationErrors?.[field.field_name] || [],
|
|
4588
|
-
pluginStatuses
|
|
4528
|
+
pluginStatuses,
|
|
4529
|
+
collectionId: data.collection.id,
|
|
4530
|
+
contentId: data.id
|
|
4589
4531
|
}));
|
|
4590
4532
|
const pageContent = `
|
|
4591
4533
|
<div class="space-y-6">
|
|
@@ -6025,7 +5967,7 @@ async function isPluginActive2(db, pluginId) {
|
|
|
6025
5967
|
|
|
6026
5968
|
// src/routes/admin-content.ts
|
|
6027
5969
|
var adminContentRoutes = new hono.Hono();
|
|
6028
|
-
adminContentRoutes.use("*",
|
|
5970
|
+
adminContentRoutes.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
6029
5971
|
async function getCollectionFields(db, collectionId) {
|
|
6030
5972
|
const cache = chunk7FOAMNTI_cjs.getCacheService(chunk7FOAMNTI_cjs.CACHE_CONFIGS.collection);
|
|
6031
5973
|
return cache.getOrSet(
|
|
@@ -6502,6 +6444,18 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
6502
6444
|
const errors = {};
|
|
6503
6445
|
for (const field of fields) {
|
|
6504
6446
|
const value = formData.get(field.field_name);
|
|
6447
|
+
const blocksConfig = chunkZWV3EBZ7_cjs.getBlocksFieldConfig(field.field_options);
|
|
6448
|
+
if (blocksConfig) {
|
|
6449
|
+
const parsed = chunkZWV3EBZ7_cjs.parseBlocksValue(value, blocksConfig);
|
|
6450
|
+
if (field.is_required && parsed.value.length === 0) {
|
|
6451
|
+
parsed.errors.push(`${field.field_label} is required`);
|
|
6452
|
+
}
|
|
6453
|
+
if (parsed.errors.length > 0) {
|
|
6454
|
+
errors[field.field_name] = parsed.errors;
|
|
6455
|
+
}
|
|
6456
|
+
data[field.field_name] = parsed.value;
|
|
6457
|
+
continue;
|
|
6458
|
+
}
|
|
6505
6459
|
if (field.is_required && (!value || value.toString().trim() === "")) {
|
|
6506
6460
|
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6507
6461
|
continue;
|
|
@@ -6524,6 +6478,67 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
6524
6478
|
data[field.field_name] = value;
|
|
6525
6479
|
}
|
|
6526
6480
|
break;
|
|
6481
|
+
case "array": {
|
|
6482
|
+
if (!value || value.toString().trim() === "") {
|
|
6483
|
+
data[field.field_name] = [];
|
|
6484
|
+
if (field.is_required) {
|
|
6485
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6486
|
+
}
|
|
6487
|
+
break;
|
|
6488
|
+
}
|
|
6489
|
+
try {
|
|
6490
|
+
const parsed = JSON.parse(value.toString());
|
|
6491
|
+
if (!Array.isArray(parsed)) {
|
|
6492
|
+
errors[field.field_name] = [`${field.field_label} must be a JSON array`];
|
|
6493
|
+
} else {
|
|
6494
|
+
if (field.is_required && parsed.length === 0) {
|
|
6495
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6496
|
+
}
|
|
6497
|
+
data[field.field_name] = parsed;
|
|
6498
|
+
}
|
|
6499
|
+
} catch {
|
|
6500
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6501
|
+
}
|
|
6502
|
+
break;
|
|
6503
|
+
}
|
|
6504
|
+
case "object": {
|
|
6505
|
+
if (!value || value.toString().trim() === "") {
|
|
6506
|
+
data[field.field_name] = {};
|
|
6507
|
+
if (field.is_required) {
|
|
6508
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6509
|
+
}
|
|
6510
|
+
break;
|
|
6511
|
+
}
|
|
6512
|
+
try {
|
|
6513
|
+
const parsed = JSON.parse(value.toString());
|
|
6514
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6515
|
+
errors[field.field_name] = [`${field.field_label} must be a JSON object`];
|
|
6516
|
+
} else {
|
|
6517
|
+
if (field.is_required && Object.keys(parsed).length === 0) {
|
|
6518
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6519
|
+
}
|
|
6520
|
+
data[field.field_name] = parsed;
|
|
6521
|
+
}
|
|
6522
|
+
} catch {
|
|
6523
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6524
|
+
}
|
|
6525
|
+
break;
|
|
6526
|
+
}
|
|
6527
|
+
case "json": {
|
|
6528
|
+
if (!value || value.toString().trim() === "") {
|
|
6529
|
+
data[field.field_name] = null;
|
|
6530
|
+
if (field.is_required) {
|
|
6531
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6532
|
+
}
|
|
6533
|
+
break;
|
|
6534
|
+
}
|
|
6535
|
+
try {
|
|
6536
|
+
data[field.field_name] = JSON.parse(value.toString());
|
|
6537
|
+
} catch {
|
|
6538
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6539
|
+
}
|
|
6540
|
+
break;
|
|
6541
|
+
}
|
|
6527
6542
|
default:
|
|
6528
6543
|
data[field.field_name] = value;
|
|
6529
6544
|
}
|
|
@@ -6648,6 +6663,18 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
6648
6663
|
const errors = {};
|
|
6649
6664
|
for (const field of fields) {
|
|
6650
6665
|
const value = formData.get(field.field_name);
|
|
6666
|
+
const blocksConfig = chunkZWV3EBZ7_cjs.getBlocksFieldConfig(field.field_options);
|
|
6667
|
+
if (blocksConfig) {
|
|
6668
|
+
const parsed = chunkZWV3EBZ7_cjs.parseBlocksValue(value, blocksConfig);
|
|
6669
|
+
if (field.is_required && parsed.value.length === 0) {
|
|
6670
|
+
parsed.errors.push(`${field.field_label} is required`);
|
|
6671
|
+
}
|
|
6672
|
+
if (parsed.errors.length > 0) {
|
|
6673
|
+
errors[field.field_name] = parsed.errors;
|
|
6674
|
+
}
|
|
6675
|
+
data[field.field_name] = parsed.value;
|
|
6676
|
+
continue;
|
|
6677
|
+
}
|
|
6651
6678
|
if (field.is_required && (!value || value.toString().trim() === "")) {
|
|
6652
6679
|
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6653
6680
|
continue;
|
|
@@ -6670,6 +6697,67 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
6670
6697
|
data[field.field_name] = value;
|
|
6671
6698
|
}
|
|
6672
6699
|
break;
|
|
6700
|
+
case "array": {
|
|
6701
|
+
if (!value || value.toString().trim() === "") {
|
|
6702
|
+
data[field.field_name] = [];
|
|
6703
|
+
if (field.is_required) {
|
|
6704
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6705
|
+
}
|
|
6706
|
+
break;
|
|
6707
|
+
}
|
|
6708
|
+
try {
|
|
6709
|
+
const parsed = JSON.parse(value.toString());
|
|
6710
|
+
if (!Array.isArray(parsed)) {
|
|
6711
|
+
errors[field.field_name] = [`${field.field_label} must be a JSON array`];
|
|
6712
|
+
} else {
|
|
6713
|
+
if (field.is_required && parsed.length === 0) {
|
|
6714
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6715
|
+
}
|
|
6716
|
+
data[field.field_name] = parsed;
|
|
6717
|
+
}
|
|
6718
|
+
} catch {
|
|
6719
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6720
|
+
}
|
|
6721
|
+
break;
|
|
6722
|
+
}
|
|
6723
|
+
case "object": {
|
|
6724
|
+
if (!value || value.toString().trim() === "") {
|
|
6725
|
+
data[field.field_name] = {};
|
|
6726
|
+
if (field.is_required) {
|
|
6727
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6728
|
+
}
|
|
6729
|
+
break;
|
|
6730
|
+
}
|
|
6731
|
+
try {
|
|
6732
|
+
const parsed = JSON.parse(value.toString());
|
|
6733
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6734
|
+
errors[field.field_name] = [`${field.field_label} must be a JSON object`];
|
|
6735
|
+
} else {
|
|
6736
|
+
if (field.is_required && Object.keys(parsed).length === 0) {
|
|
6737
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6738
|
+
}
|
|
6739
|
+
data[field.field_name] = parsed;
|
|
6740
|
+
}
|
|
6741
|
+
} catch {
|
|
6742
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6743
|
+
}
|
|
6744
|
+
break;
|
|
6745
|
+
}
|
|
6746
|
+
case "json": {
|
|
6747
|
+
if (!value || value.toString().trim() === "") {
|
|
6748
|
+
data[field.field_name] = null;
|
|
6749
|
+
if (field.is_required) {
|
|
6750
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6751
|
+
}
|
|
6752
|
+
break;
|
|
6753
|
+
}
|
|
6754
|
+
try {
|
|
6755
|
+
data[field.field_name] = JSON.parse(value.toString());
|
|
6756
|
+
} catch {
|
|
6757
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6758
|
+
}
|
|
6759
|
+
break;
|
|
6760
|
+
}
|
|
6673
6761
|
default:
|
|
6674
6762
|
data[field.field_name] = value;
|
|
6675
6763
|
}
|
|
@@ -6789,6 +6877,12 @@ adminContentRoutes.post("/preview", async (c) => {
|
|
|
6789
6877
|
const data = {};
|
|
6790
6878
|
for (const field of fields) {
|
|
6791
6879
|
const value = formData.get(field.field_name);
|
|
6880
|
+
const blocksConfig = chunkZWV3EBZ7_cjs.getBlocksFieldConfig(field.field_options);
|
|
6881
|
+
if (blocksConfig) {
|
|
6882
|
+
const parsed = chunkZWV3EBZ7_cjs.parseBlocksValue(value, blocksConfig);
|
|
6883
|
+
data[field.field_name] = parsed.value;
|
|
6884
|
+
continue;
|
|
6885
|
+
}
|
|
6792
6886
|
switch (field.field_type) {
|
|
6793
6887
|
case "number":
|
|
6794
6888
|
data[field.field_name] = value ? Number(value) : null;
|
|
@@ -8086,7 +8180,7 @@ function renderUserEditPage(data) {
|
|
|
8086
8180
|
<input
|
|
8087
8181
|
type="text"
|
|
8088
8182
|
name="first_name"
|
|
8089
|
-
value="${
|
|
8183
|
+
value="${chunkZWV3EBZ7_cjs.escapeHtml(data.userToEdit.firstName || "")}"
|
|
8090
8184
|
required
|
|
8091
8185
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8092
8186
|
/>
|
|
@@ -8097,7 +8191,7 @@ function renderUserEditPage(data) {
|
|
|
8097
8191
|
<input
|
|
8098
8192
|
type="text"
|
|
8099
8193
|
name="last_name"
|
|
8100
|
-
value="${
|
|
8194
|
+
value="${chunkZWV3EBZ7_cjs.escapeHtml(data.userToEdit.lastName || "")}"
|
|
8101
8195
|
required
|
|
8102
8196
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8103
8197
|
/>
|
|
@@ -8108,7 +8202,7 @@ function renderUserEditPage(data) {
|
|
|
8108
8202
|
<input
|
|
8109
8203
|
type="text"
|
|
8110
8204
|
name="username"
|
|
8111
|
-
value="${
|
|
8205
|
+
value="${chunkZWV3EBZ7_cjs.escapeHtml(data.userToEdit.username || "")}"
|
|
8112
8206
|
required
|
|
8113
8207
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8114
8208
|
/>
|
|
@@ -8119,7 +8213,7 @@ function renderUserEditPage(data) {
|
|
|
8119
8213
|
<input
|
|
8120
8214
|
type="email"
|
|
8121
8215
|
name="email"
|
|
8122
|
-
value="${
|
|
8216
|
+
value="${chunkZWV3EBZ7_cjs.escapeHtml(data.userToEdit.email || "")}"
|
|
8123
8217
|
required
|
|
8124
8218
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8125
8219
|
/>
|
|
@@ -8130,7 +8224,7 @@ function renderUserEditPage(data) {
|
|
|
8130
8224
|
<input
|
|
8131
8225
|
type="tel"
|
|
8132
8226
|
name="phone"
|
|
8133
|
-
value="${
|
|
8227
|
+
value="${chunkZWV3EBZ7_cjs.escapeHtml(data.userToEdit.phone || "")}"
|
|
8134
8228
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8135
8229
|
/>
|
|
8136
8230
|
</div>
|
|
@@ -8144,7 +8238,7 @@ function renderUserEditPage(data) {
|
|
|
8144
8238
|
class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white/5 dark:bg-white/5 py-1.5 pl-3 pr-8 text-base text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-zinc-500/30 dark:outline-zinc-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-zinc-500 dark:focus-visible:outline-zinc-400 sm:text-sm/6"
|
|
8145
8239
|
>
|
|
8146
8240
|
${data.roles.map((role) => `
|
|
8147
|
-
<option value="${
|
|
8241
|
+
<option value="${chunkZWV3EBZ7_cjs.escapeHtml(role.value)}" ${data.userToEdit.role === role.value ? "selected" : ""}>${chunkZWV3EBZ7_cjs.escapeHtml(role.label)}</option>
|
|
8148
8242
|
`).join("")}
|
|
8149
8243
|
</select>
|
|
8150
8244
|
<svg viewBox="0 0 16 16" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-zinc-600 dark:text-zinc-400 sm:size-4">
|
|
@@ -8160,7 +8254,7 @@ function renderUserEditPage(data) {
|
|
|
8160
8254
|
name="bio"
|
|
8161
8255
|
rows="3"
|
|
8162
8256
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8163
|
-
>${
|
|
8257
|
+
>${chunkZWV3EBZ7_cjs.escapeHtml(data.userToEdit.bio || "")}</textarea>
|
|
8164
8258
|
</div>
|
|
8165
8259
|
</div>
|
|
8166
8260
|
|
|
@@ -9060,7 +9154,7 @@ function renderUsersListPage(data) {
|
|
|
9060
9154
|
|
|
9061
9155
|
// src/routes/admin-users.ts
|
|
9062
9156
|
var userRoutes = new hono.Hono();
|
|
9063
|
-
userRoutes.use("*",
|
|
9157
|
+
userRoutes.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
9064
9158
|
userRoutes.get("/", (c) => {
|
|
9065
9159
|
return c.redirect("/admin/dashboard");
|
|
9066
9160
|
});
|
|
@@ -9159,12 +9253,12 @@ userRoutes.put("/profile", async (c) => {
|
|
|
9159
9253
|
const db = c.env.DB;
|
|
9160
9254
|
try {
|
|
9161
9255
|
const formData = await c.req.formData();
|
|
9162
|
-
const firstName =
|
|
9163
|
-
const lastName =
|
|
9164
|
-
const username =
|
|
9256
|
+
const firstName = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
9257
|
+
const lastName = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
9258
|
+
const username = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
9165
9259
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
9166
|
-
const phone =
|
|
9167
|
-
const bio =
|
|
9260
|
+
const phone = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
9261
|
+
const bio = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
9168
9262
|
const timezone = formData.get("timezone")?.toString() || "UTC";
|
|
9169
9263
|
const language = formData.get("language")?.toString() || "en";
|
|
9170
9264
|
const emailNotifications = formData.get("email_notifications") === "1";
|
|
@@ -9215,7 +9309,7 @@ userRoutes.put("/profile", async (c) => {
|
|
|
9215
9309
|
Date.now(),
|
|
9216
9310
|
user.userId
|
|
9217
9311
|
).run();
|
|
9218
|
-
await
|
|
9312
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9219
9313
|
db,
|
|
9220
9314
|
user.userId,
|
|
9221
9315
|
"profile.update",
|
|
@@ -9278,7 +9372,7 @@ userRoutes.post("/profile/avatar", async (c) => {
|
|
|
9278
9372
|
SELECT first_name, last_name FROM users WHERE id = ?
|
|
9279
9373
|
`);
|
|
9280
9374
|
const userData = await userStmt.bind(user.userId).first();
|
|
9281
|
-
await
|
|
9375
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9282
9376
|
db,
|
|
9283
9377
|
user.userId,
|
|
9284
9378
|
"profile.avatar_update",
|
|
@@ -9349,7 +9443,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
9349
9443
|
dismissible: true
|
|
9350
9444
|
}));
|
|
9351
9445
|
}
|
|
9352
|
-
const validPassword = await
|
|
9446
|
+
const validPassword = await chunkYYV3XQOQ_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
|
|
9353
9447
|
if (!validPassword) {
|
|
9354
9448
|
return c.html(renderAlert2({
|
|
9355
9449
|
type: "error",
|
|
@@ -9357,7 +9451,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
9357
9451
|
dismissible: true
|
|
9358
9452
|
}));
|
|
9359
9453
|
}
|
|
9360
|
-
const newPasswordHash = await
|
|
9454
|
+
const newPasswordHash = await chunkYYV3XQOQ_cjs.AuthManager.hashPassword(newPassword);
|
|
9361
9455
|
const historyStmt = db.prepare(`
|
|
9362
9456
|
INSERT INTO password_history (id, user_id, password_hash, created_at)
|
|
9363
9457
|
VALUES (?, ?, ?, ?)
|
|
@@ -9373,7 +9467,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
9373
9467
|
WHERE id = ?
|
|
9374
9468
|
`);
|
|
9375
9469
|
await updateStmt.bind(newPasswordHash, Date.now(), user.userId).run();
|
|
9376
|
-
await
|
|
9470
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9377
9471
|
db,
|
|
9378
9472
|
user.userId,
|
|
9379
9473
|
"profile.password_change",
|
|
@@ -9440,7 +9534,7 @@ userRoutes.get("/users", async (c) => {
|
|
|
9440
9534
|
`);
|
|
9441
9535
|
const countResult = await countStmt.bind(...params).first();
|
|
9442
9536
|
const totalUsers = countResult?.total || 0;
|
|
9443
|
-
await
|
|
9537
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9444
9538
|
db,
|
|
9445
9539
|
user.userId,
|
|
9446
9540
|
"users.list_view",
|
|
@@ -9542,12 +9636,12 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
9542
9636
|
const user = c.get("user");
|
|
9543
9637
|
try {
|
|
9544
9638
|
const formData = await c.req.formData();
|
|
9545
|
-
const firstName =
|
|
9546
|
-
const lastName =
|
|
9547
|
-
const username =
|
|
9639
|
+
const firstName = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
9640
|
+
const lastName = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
9641
|
+
const username = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
9548
9642
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
9549
|
-
const phone =
|
|
9550
|
-
const bio =
|
|
9643
|
+
const phone = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
9644
|
+
const bio = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
9551
9645
|
const role = formData.get("role")?.toString() || "viewer";
|
|
9552
9646
|
const password = formData.get("password")?.toString() || "";
|
|
9553
9647
|
const confirmPassword = formData.get("confirm_password")?.toString() || "";
|
|
@@ -9594,7 +9688,7 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
9594
9688
|
dismissible: true
|
|
9595
9689
|
}));
|
|
9596
9690
|
}
|
|
9597
|
-
const passwordHash = await
|
|
9691
|
+
const passwordHash = await chunkYYV3XQOQ_cjs.AuthManager.hashPassword(password);
|
|
9598
9692
|
const userId = crypto.randomUUID();
|
|
9599
9693
|
const createStmt = db.prepare(`
|
|
9600
9694
|
INSERT INTO users (
|
|
@@ -9617,7 +9711,7 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
9617
9711
|
Date.now(),
|
|
9618
9712
|
Date.now()
|
|
9619
9713
|
).run();
|
|
9620
|
-
await
|
|
9714
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9621
9715
|
db,
|
|
9622
9716
|
user.userId,
|
|
9623
9717
|
"user!.create",
|
|
@@ -9655,7 +9749,7 @@ userRoutes.get("/users/:id", async (c) => {
|
|
|
9655
9749
|
if (!userRecord) {
|
|
9656
9750
|
return c.json({ error: "User not found" }, 404);
|
|
9657
9751
|
}
|
|
9658
|
-
await
|
|
9752
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9659
9753
|
db,
|
|
9660
9754
|
user.userId,
|
|
9661
9755
|
"user!.view",
|
|
@@ -9748,12 +9842,12 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
9748
9842
|
const userId = c.req.param("id");
|
|
9749
9843
|
try {
|
|
9750
9844
|
const formData = await c.req.formData();
|
|
9751
|
-
const firstName =
|
|
9752
|
-
const lastName =
|
|
9753
|
-
const username =
|
|
9845
|
+
const firstName = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
9846
|
+
const lastName = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
9847
|
+
const username = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
9754
9848
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
9755
|
-
const phone =
|
|
9756
|
-
const bio =
|
|
9849
|
+
const phone = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
9850
|
+
const bio = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
9757
9851
|
const role = formData.get("role")?.toString() || "viewer";
|
|
9758
9852
|
const isActive = formData.get("is_active") === "1";
|
|
9759
9853
|
const emailVerified = formData.get("email_verified") === "1";
|
|
@@ -9804,7 +9898,7 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
9804
9898
|
Date.now(),
|
|
9805
9899
|
userId
|
|
9806
9900
|
).run();
|
|
9807
|
-
await
|
|
9901
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9808
9902
|
db,
|
|
9809
9903
|
user.userId,
|
|
9810
9904
|
"user!.update",
|
|
@@ -9849,7 +9943,7 @@ userRoutes.post("/users/:id/toggle", async (c) => {
|
|
|
9849
9943
|
UPDATE users SET is_active = ?, updated_at = ? WHERE id = ?
|
|
9850
9944
|
`);
|
|
9851
9945
|
await toggleStmt.bind(active ? 1 : 0, Date.now(), userId).run();
|
|
9852
|
-
await
|
|
9946
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9853
9947
|
db,
|
|
9854
9948
|
user.userId,
|
|
9855
9949
|
active ? "user.activate" : "user.deactivate",
|
|
@@ -9890,7 +9984,7 @@ userRoutes.delete("/users/:id", async (c) => {
|
|
|
9890
9984
|
DELETE FROM users WHERE id = ?
|
|
9891
9985
|
`);
|
|
9892
9986
|
await deleteStmt.bind(userId).run();
|
|
9893
|
-
await
|
|
9987
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9894
9988
|
db,
|
|
9895
9989
|
user.userId,
|
|
9896
9990
|
"user!.hard_delete",
|
|
@@ -9909,7 +10003,7 @@ userRoutes.delete("/users/:id", async (c) => {
|
|
|
9909
10003
|
UPDATE users SET is_active = 0, updated_at = ? WHERE id = ?
|
|
9910
10004
|
`);
|
|
9911
10005
|
await deleteStmt.bind(Date.now(), userId).run();
|
|
9912
|
-
await
|
|
10006
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9913
10007
|
db,
|
|
9914
10008
|
user.userId,
|
|
9915
10009
|
"user!.soft_delete",
|
|
@@ -9936,8 +10030,8 @@ userRoutes.post("/invite-user", async (c) => {
|
|
|
9936
10030
|
const formData = await c.req.formData();
|
|
9937
10031
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
9938
10032
|
const role = formData.get("role")?.toString()?.trim() || "viewer";
|
|
9939
|
-
const firstName =
|
|
9940
|
-
const lastName =
|
|
10033
|
+
const firstName = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
10034
|
+
const lastName = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
9941
10035
|
if (!email || !firstName || !lastName) {
|
|
9942
10036
|
return c.json({ error: "Email, first name, and last name are required" }, 400);
|
|
9943
10037
|
}
|
|
@@ -9975,7 +10069,7 @@ userRoutes.post("/invite-user", async (c) => {
|
|
|
9975
10069
|
Date.now(),
|
|
9976
10070
|
Date.now()
|
|
9977
10071
|
).run();
|
|
9978
|
-
await
|
|
10072
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
9979
10073
|
db,
|
|
9980
10074
|
user.userId,
|
|
9981
10075
|
"user!.invite_sent",
|
|
@@ -10032,7 +10126,7 @@ userRoutes.post("/resend-invitation/:id", async (c) => {
|
|
|
10032
10126
|
Date.now(),
|
|
10033
10127
|
userId
|
|
10034
10128
|
).run();
|
|
10035
|
-
await
|
|
10129
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
10036
10130
|
db,
|
|
10037
10131
|
user.userId,
|
|
10038
10132
|
"user!.invitation_resent",
|
|
@@ -10068,7 +10162,7 @@ userRoutes.delete("/cancel-invitation/:id", async (c) => {
|
|
|
10068
10162
|
}
|
|
10069
10163
|
const deleteStmt = db.prepare(`DELETE FROM users WHERE id = ?`);
|
|
10070
10164
|
await deleteStmt.bind(userId).run();
|
|
10071
|
-
await
|
|
10165
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
10072
10166
|
db,
|
|
10073
10167
|
user.userId,
|
|
10074
10168
|
"user!.invitation_cancelled",
|
|
@@ -10151,7 +10245,7 @@ userRoutes.get("/activity-logs", async (c) => {
|
|
|
10151
10245
|
...log,
|
|
10152
10246
|
details: log.details ? JSON.parse(log.details) : null
|
|
10153
10247
|
}));
|
|
10154
|
-
await
|
|
10248
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
10155
10249
|
db,
|
|
10156
10250
|
user.userId,
|
|
10157
10251
|
"activity.logs_viewed",
|
|
@@ -10258,7 +10352,7 @@ userRoutes.get("/activity-logs/export", async (c) => {
|
|
|
10258
10352
|
csvRows.push(row.join(","));
|
|
10259
10353
|
}
|
|
10260
10354
|
const csvContent = csvRows.join("\n");
|
|
10261
|
-
await
|
|
10355
|
+
await chunkYYV3XQOQ_cjs.logActivity(
|
|
10262
10356
|
db,
|
|
10263
10357
|
user.userId,
|
|
10264
10358
|
"activity.logs_exported",
|
|
@@ -11597,7 +11691,7 @@ var fileValidationSchema2 = zod.z.object({
|
|
|
11597
11691
|
// 50MB max
|
|
11598
11692
|
});
|
|
11599
11693
|
var adminMediaRoutes = new hono.Hono();
|
|
11600
|
-
adminMediaRoutes.use("*",
|
|
11694
|
+
adminMediaRoutes.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
11601
11695
|
adminMediaRoutes.get("/", async (c) => {
|
|
11602
11696
|
try {
|
|
11603
11697
|
const user = c.get("user");
|
|
@@ -12183,7 +12277,7 @@ adminMediaRoutes.put("/:id", async (c) => {
|
|
|
12183
12277
|
`);
|
|
12184
12278
|
}
|
|
12185
12279
|
});
|
|
12186
|
-
adminMediaRoutes.delete("/cleanup",
|
|
12280
|
+
adminMediaRoutes.delete("/cleanup", chunkYYV3XQOQ_cjs.requireRole("admin"), async (c) => {
|
|
12187
12281
|
try {
|
|
12188
12282
|
const db = c.env.DB;
|
|
12189
12283
|
const allMediaStmt = db.prepare("SELECT id, r2_key, filename FROM media WHERE deleted_at IS NULL");
|
|
@@ -13317,6 +13411,9 @@ function renderAuthSettingsForm(settings) {
|
|
|
13317
13411
|
}
|
|
13318
13412
|
|
|
13319
13413
|
// src/templates/pages/admin-plugin-settings.template.ts
|
|
13414
|
+
function escapeHtmlAttr(value) {
|
|
13415
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
13416
|
+
}
|
|
13320
13417
|
function renderPluginSettingsPage(data) {
|
|
13321
13418
|
const { plugin, activity = [], user } = data;
|
|
13322
13419
|
const pageContent = `
|
|
@@ -13594,6 +13691,7 @@ function renderSettingsTab(plugin) {
|
|
|
13594
13691
|
const settings = plugin.settings || {};
|
|
13595
13692
|
const isSeedDataPlugin = plugin.id === "seed-data" || plugin.name === "seed-data";
|
|
13596
13693
|
const isAuthPlugin = plugin.id === "core-auth" || plugin.name === "core-auth";
|
|
13694
|
+
const isTurnstilePlugin = plugin.id === "turnstile" || plugin.name === "turnstile";
|
|
13597
13695
|
return `
|
|
13598
13696
|
${isSeedDataPlugin ? `
|
|
13599
13697
|
<div class="backdrop-blur-md bg-black/20 rounded-xl border border-white/10 shadow-xl p-6 mb-6">
|
|
@@ -13620,12 +13718,15 @@ function renderSettingsTab(plugin) {
|
|
|
13620
13718
|
${isAuthPlugin ? `
|
|
13621
13719
|
<h2 class="text-xl font-semibold text-white mb-4">Authentication Settings</h2>
|
|
13622
13720
|
<p class="text-gray-400 mb-6">Configure user registration fields and validation rules.</p>
|
|
13721
|
+
` : isTurnstilePlugin ? `
|
|
13722
|
+
<h2 class="text-xl font-semibold text-white mb-4">Cloudflare Turnstile Settings</h2>
|
|
13723
|
+
<p class="text-gray-400 mb-6">Configure CAPTCHA-free bot protection for your forms.</p>
|
|
13623
13724
|
` : `
|
|
13624
13725
|
<h2 class="text-xl font-semibold text-white mb-4">Plugin Settings</h2>
|
|
13625
13726
|
`}
|
|
13626
13727
|
|
|
13627
13728
|
<form id="settings-form" class="space-y-6">
|
|
13628
|
-
${isAuthPlugin && Object.keys(settings).length > 0 ? renderAuthSettingsForm(settings) : Object.keys(settings).length > 0 ? renderSettingsFields(settings) : renderNoSettings(plugin)}
|
|
13729
|
+
${isAuthPlugin && Object.keys(settings).length > 0 ? renderAuthSettingsForm(settings) : isTurnstilePlugin && Object.keys(settings).length > 0 ? renderTurnstileSettingsForm(settings) : Object.keys(settings).length > 0 ? renderSettingsFields(settings) : renderNoSettings(plugin)}
|
|
13629
13730
|
|
|
13630
13731
|
${Object.keys(settings).length > 0 ? `
|
|
13631
13732
|
<div class="flex items-center justify-end pt-6 border-t border-white/10">
|
|
@@ -13689,6 +13790,80 @@ function renderSettingsFields(settings) {
|
|
|
13689
13790
|
}
|
|
13690
13791
|
}).join("");
|
|
13691
13792
|
}
|
|
13793
|
+
function renderTurnstileSettingsForm(settings) {
|
|
13794
|
+
const inputClass = "backdrop-blur-sm bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white placeholder-gray-300 focus:border-blue-400 focus:outline-none transition-colors w-full";
|
|
13795
|
+
const selectClass = "backdrop-blur-sm bg-zinc-800 border border-white/20 rounded-lg px-3 py-2 text-white focus:border-blue-400 focus:outline-none transition-colors w-full [&>option]:bg-zinc-800 [&>option]:text-white";
|
|
13796
|
+
return `
|
|
13797
|
+
<!-- Enable Toggle -->
|
|
13798
|
+
<div class="flex items-center justify-between">
|
|
13799
|
+
<div>
|
|
13800
|
+
<label for="setting_enabled" class="text-sm font-medium text-gray-300">Enable Turnstile</label>
|
|
13801
|
+
<p class="text-xs text-gray-400">Enable or disable Turnstile verification globally</p>
|
|
13802
|
+
</div>
|
|
13803
|
+
<label class="relative inline-flex items-center cursor-pointer">
|
|
13804
|
+
<input type="checkbox" name="setting_enabled" id="setting_enabled" ${settings.enabled ? "checked" : ""} class="sr-only peer">
|
|
13805
|
+
<div class="w-11 h-6 bg-gray-600 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-800 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
|
13806
|
+
</label>
|
|
13807
|
+
</div>
|
|
13808
|
+
|
|
13809
|
+
<!-- Site Key -->
|
|
13810
|
+
<div>
|
|
13811
|
+
<label for="setting_siteKey" class="block text-sm font-medium text-gray-300 mb-2">Site Key</label>
|
|
13812
|
+
<input type="text" name="setting_siteKey" id="setting_siteKey" value="${escapeHtmlAttr(settings.siteKey || "")}" placeholder="0x4AAAAAAAA..." class="${inputClass}">
|
|
13813
|
+
<p class="text-xs text-gray-400 mt-1">Your Cloudflare Turnstile site key (public)</p>
|
|
13814
|
+
</div>
|
|
13815
|
+
|
|
13816
|
+
<!-- Secret Key -->
|
|
13817
|
+
<div>
|
|
13818
|
+
<label for="setting_secretKey" class="block text-sm font-medium text-gray-300 mb-2">Secret Key</label>
|
|
13819
|
+
<input type="password" name="setting_secretKey" id="setting_secretKey" value="${escapeHtmlAttr(settings.secretKey || "")}" placeholder="0x4AAAAAAAA..." class="${inputClass}">
|
|
13820
|
+
<p class="text-xs text-gray-400 mt-1">Your Cloudflare Turnstile secret key (private)</p>
|
|
13821
|
+
</div>
|
|
13822
|
+
|
|
13823
|
+
<!-- Theme -->
|
|
13824
|
+
<div>
|
|
13825
|
+
<label for="setting_theme" class="block text-sm font-medium text-gray-300 mb-2">Widget Theme</label>
|
|
13826
|
+
<select name="setting_theme" id="setting_theme" class="${selectClass}" style="color: white; background-color: rgb(39, 39, 42);">
|
|
13827
|
+
<option value="auto" ${settings.theme === "auto" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Auto (matches page theme)</option>
|
|
13828
|
+
<option value="light" ${settings.theme === "light" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Light</option>
|
|
13829
|
+
<option value="dark" ${settings.theme === "dark" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Dark</option>
|
|
13830
|
+
</select>
|
|
13831
|
+
<p class="text-xs text-gray-400 mt-1">Visual appearance of the Turnstile widget</p>
|
|
13832
|
+
</div>
|
|
13833
|
+
|
|
13834
|
+
<!-- Size -->
|
|
13835
|
+
<div>
|
|
13836
|
+
<label for="setting_size" class="block text-sm font-medium text-gray-300 mb-2">Widget Size</label>
|
|
13837
|
+
<select name="setting_size" id="setting_size" class="${selectClass}" style="color: white; background-color: rgb(39, 39, 42);">
|
|
13838
|
+
<option value="normal" ${settings.size === "normal" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Normal (300x65px)</option>
|
|
13839
|
+
<option value="compact" ${settings.size === "compact" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Compact (130x120px)</option>
|
|
13840
|
+
</select>
|
|
13841
|
+
<p class="text-xs text-gray-400 mt-1">Size of the Turnstile challenge widget</p>
|
|
13842
|
+
</div>
|
|
13843
|
+
|
|
13844
|
+
<!-- Widget Mode -->
|
|
13845
|
+
<div>
|
|
13846
|
+
<label for="setting_mode" class="block text-sm font-medium text-gray-300 mb-2">Widget Mode</label>
|
|
13847
|
+
<select name="setting_mode" id="setting_mode" class="${selectClass}" style="color: white; background-color: rgb(39, 39, 42);">
|
|
13848
|
+
<option value="managed" ${!settings.mode || settings.mode === "managed" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Managed (Recommended) - Adaptive challenge</option>
|
|
13849
|
+
<option value="non-interactive" ${settings.mode === "non-interactive" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Non-Interactive - Always visible, minimal friction</option>
|
|
13850
|
+
<option value="invisible" ${settings.mode === "invisible" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Invisible - No visible widget</option>
|
|
13851
|
+
</select>
|
|
13852
|
+
<p class="text-xs text-gray-400 mt-1"><strong>Managed:</strong> Shows challenge only when needed. <strong>Non-Interactive:</strong> Always shows but doesn't require interaction. <strong>Invisible:</strong> Runs in background without UI.</p>
|
|
13853
|
+
</div>
|
|
13854
|
+
|
|
13855
|
+
<!-- Appearance (Pre-clearance) -->
|
|
13856
|
+
<div>
|
|
13857
|
+
<label for="setting_appearance" class="block text-sm font-medium text-gray-300 mb-2">Pre-clearance / Appearance</label>
|
|
13858
|
+
<select name="setting_appearance" id="setting_appearance" class="${selectClass}" style="color: white; background-color: rgb(39, 39, 42);">
|
|
13859
|
+
<option value="always" ${!settings.appearance || settings.appearance === "always" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Always - Pre-clearance enabled (verifies immediately)</option>
|
|
13860
|
+
<option value="execute" ${settings.appearance === "execute" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Execute - Challenge on form submit</option>
|
|
13861
|
+
<option value="interaction-only" ${settings.appearance === "interaction-only" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Interaction Only - Only after user interaction</option>
|
|
13862
|
+
</select>
|
|
13863
|
+
<p class="text-xs text-gray-400 mt-1">Controls when Turnstile verification occurs. <strong>Always:</strong> Verifies immediately (pre-clearance). <strong>Execute:</strong> Verifies on form submit. <strong>Interaction Only:</strong> Only after user interaction.</p>
|
|
13864
|
+
</div>
|
|
13865
|
+
`;
|
|
13866
|
+
}
|
|
13692
13867
|
function renderNoSettings(plugin) {
|
|
13693
13868
|
if (plugin.id === "seed-data" || plugin.name === "seed-data") {
|
|
13694
13869
|
return `
|
|
@@ -13828,7 +14003,7 @@ function formatTimestamp(timestamp) {
|
|
|
13828
14003
|
|
|
13829
14004
|
// src/routes/admin-plugins.ts
|
|
13830
14005
|
var adminPluginRoutes = new hono.Hono();
|
|
13831
|
-
adminPluginRoutes.use("*",
|
|
14006
|
+
adminPluginRoutes.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
13832
14007
|
var AVAILABLE_PLUGINS = [
|
|
13833
14008
|
{
|
|
13834
14009
|
id: "third-party-faq",
|
|
@@ -13920,6 +14095,19 @@ var AVAILABLE_PLUGINS = [
|
|
|
13920
14095
|
permissions: [],
|
|
13921
14096
|
dependencies: [],
|
|
13922
14097
|
is_core: false
|
|
14098
|
+
},
|
|
14099
|
+
{
|
|
14100
|
+
id: "turnstile",
|
|
14101
|
+
name: "turnstile-plugin",
|
|
14102
|
+
display_name: "Cloudflare Turnstile",
|
|
14103
|
+
description: "CAPTCHA-free bot protection for forms using Cloudflare Turnstile. Provides seamless spam prevention with configurable modes, themes, and pre-clearance options.",
|
|
14104
|
+
version: "1.0.0",
|
|
14105
|
+
author: "SonicJS Team",
|
|
14106
|
+
category: "security",
|
|
14107
|
+
icon: "\u{1F6E1}\uFE0F",
|
|
14108
|
+
permissions: [],
|
|
14109
|
+
dependencies: [],
|
|
14110
|
+
is_core: true
|
|
13923
14111
|
}
|
|
13924
14112
|
];
|
|
13925
14113
|
adminPluginRoutes.get("/", async (c) => {
|
|
@@ -14290,6 +14478,33 @@ adminPluginRoutes.post("/install", async (c) => {
|
|
|
14290
14478
|
});
|
|
14291
14479
|
return c.json({ success: true, plugin: easyMdxPlugin2 });
|
|
14292
14480
|
}
|
|
14481
|
+
if (body.name === "turnstile-plugin") {
|
|
14482
|
+
const turnstilePlugin = await pluginService.installPlugin({
|
|
14483
|
+
id: "turnstile",
|
|
14484
|
+
name: "turnstile-plugin",
|
|
14485
|
+
display_name: "Cloudflare Turnstile",
|
|
14486
|
+
description: "CAPTCHA-free bot protection for forms using Cloudflare Turnstile. Provides seamless spam prevention with configurable modes, themes, and pre-clearance options.",
|
|
14487
|
+
version: "1.0.0",
|
|
14488
|
+
author: "SonicJS Team",
|
|
14489
|
+
category: "security",
|
|
14490
|
+
icon: "\u{1F6E1}\uFE0F",
|
|
14491
|
+
permissions: [],
|
|
14492
|
+
dependencies: [],
|
|
14493
|
+
is_core: true,
|
|
14494
|
+
settings: {
|
|
14495
|
+
siteKey: "",
|
|
14496
|
+
secretKey: "",
|
|
14497
|
+
theme: "auto",
|
|
14498
|
+
size: "normal",
|
|
14499
|
+
mode: "managed",
|
|
14500
|
+
appearance: "always",
|
|
14501
|
+
preClearanceEnabled: false,
|
|
14502
|
+
preClearanceLevel: "managed",
|
|
14503
|
+
enabled: false
|
|
14504
|
+
}
|
|
14505
|
+
});
|
|
14506
|
+
return c.json({ success: true, plugin: turnstilePlugin });
|
|
14507
|
+
}
|
|
14293
14508
|
return c.json({ error: "Plugin not found in registry" }, 404);
|
|
14294
14509
|
} catch (error) {
|
|
14295
14510
|
console.error("Error installing plugin:", error);
|
|
@@ -15119,7 +15334,7 @@ function renderLogConfigPage(data) {
|
|
|
15119
15334
|
|
|
15120
15335
|
// src/routes/admin-logs.ts
|
|
15121
15336
|
var adminLogsRoutes = new hono.Hono();
|
|
15122
|
-
adminLogsRoutes.use("*",
|
|
15337
|
+
adminLogsRoutes.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
15123
15338
|
adminLogsRoutes.get("/", async (c) => {
|
|
15124
15339
|
try {
|
|
15125
15340
|
const user = c.get("user");
|
|
@@ -17447,9 +17662,9 @@ function renderStorageUsage(databaseSizeBytes, mediaSizeBytes) {
|
|
|
17447
17662
|
}
|
|
17448
17663
|
|
|
17449
17664
|
// src/routes/admin-dashboard.ts
|
|
17450
|
-
var VERSION =
|
|
17665
|
+
var VERSION = chunkZWV3EBZ7_cjs.getCoreVersion();
|
|
17451
17666
|
var router = new hono.Hono();
|
|
17452
|
-
router.use("*",
|
|
17667
|
+
router.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
17453
17668
|
router.get("/", async (c) => {
|
|
17454
17669
|
const user = c.get("user");
|
|
17455
17670
|
try {
|
|
@@ -19207,7 +19422,7 @@ function renderCollectionFormPage(data) {
|
|
|
19207
19422
|
|
|
19208
19423
|
// src/routes/admin-collections.ts
|
|
19209
19424
|
var adminCollectionsRoutes = new hono.Hono();
|
|
19210
|
-
adminCollectionsRoutes.use("*",
|
|
19425
|
+
adminCollectionsRoutes.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
19211
19426
|
adminCollectionsRoutes.get("/", async (c) => {
|
|
19212
19427
|
try {
|
|
19213
19428
|
const user = c.get("user");
|
|
@@ -19463,16 +19678,30 @@ adminCollectionsRoutes.get("/:id", async (c) => {
|
|
|
19463
19678
|
const schema = typeof collection.schema === "string" ? JSON.parse(collection.schema) : collection.schema;
|
|
19464
19679
|
if (schema && schema.properties) {
|
|
19465
19680
|
let fieldOrder = 0;
|
|
19466
|
-
fields = Object.entries(schema.properties).map(([fieldName, fieldConfig]) =>
|
|
19467
|
-
|
|
19468
|
-
|
|
19469
|
-
|
|
19470
|
-
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
19475
|
-
|
|
19681
|
+
fields = Object.entries(schema.properties).map(([fieldName, fieldConfig]) => {
|
|
19682
|
+
let fieldType = fieldConfig.type || "string";
|
|
19683
|
+
if (fieldConfig.enum) {
|
|
19684
|
+
fieldType = "select";
|
|
19685
|
+
} else if (fieldConfig.format === "richtext") {
|
|
19686
|
+
fieldType = "richtext";
|
|
19687
|
+
} else if (fieldConfig.format === "media") {
|
|
19688
|
+
fieldType = "media";
|
|
19689
|
+
} else if (fieldConfig.format === "date-time") {
|
|
19690
|
+
fieldType = "date";
|
|
19691
|
+
} else if (fieldConfig.type === "slug" || fieldConfig.format === "slug") {
|
|
19692
|
+
fieldType = "slug";
|
|
19693
|
+
}
|
|
19694
|
+
return {
|
|
19695
|
+
id: `schema-${fieldName}`,
|
|
19696
|
+
field_name: fieldName,
|
|
19697
|
+
field_type: fieldType,
|
|
19698
|
+
field_label: fieldConfig.title || fieldName,
|
|
19699
|
+
field_options: fieldConfig,
|
|
19700
|
+
field_order: fieldOrder++,
|
|
19701
|
+
is_required: fieldConfig.required === true || schema.required && schema.required.includes(fieldName),
|
|
19702
|
+
is_searchable: fieldConfig.searchable === true || false
|
|
19703
|
+
};
|
|
19704
|
+
});
|
|
19476
19705
|
}
|
|
19477
19706
|
} catch (e) {
|
|
19478
19707
|
console.error("Error parsing collection schema:", e);
|
|
@@ -19687,6 +19916,9 @@ adminCollectionsRoutes.post("/:id/fields", async (c) => {
|
|
|
19687
19916
|
fieldConfig.enum = parsedOptions.options || [];
|
|
19688
19917
|
} else if (fieldType === "media") {
|
|
19689
19918
|
fieldConfig.format = "media";
|
|
19919
|
+
} else if (fieldType === "slug") {
|
|
19920
|
+
fieldConfig.type = "slug";
|
|
19921
|
+
fieldConfig.format = "slug";
|
|
19690
19922
|
} else if (fieldType === "quill") {
|
|
19691
19923
|
fieldConfig.type = "quill";
|
|
19692
19924
|
} else if (fieldType === "mdxeditor") {
|
|
@@ -21370,7 +21602,7 @@ function renderDatabaseToolsSettings(settings) {
|
|
|
21370
21602
|
|
|
21371
21603
|
// src/routes/admin-settings.ts
|
|
21372
21604
|
var adminSettingsRoutes = new hono.Hono();
|
|
21373
|
-
adminSettingsRoutes.use("*",
|
|
21605
|
+
adminSettingsRoutes.use("*", chunkYYV3XQOQ_cjs.requireAuth());
|
|
21374
21606
|
function getMockSettings(user) {
|
|
21375
21607
|
return {
|
|
21376
21608
|
general: {
|
|
@@ -21538,7 +21770,7 @@ adminSettingsRoutes.get("/database-tools", (c) => {
|
|
|
21538
21770
|
adminSettingsRoutes.get("/api/migrations/status", async (c) => {
|
|
21539
21771
|
try {
|
|
21540
21772
|
const db = c.env.DB;
|
|
21541
|
-
const migrationService = new
|
|
21773
|
+
const migrationService = new chunkI4V3VZWF_cjs.MigrationService(db);
|
|
21542
21774
|
const status = await migrationService.getMigrationStatus();
|
|
21543
21775
|
return c.json({
|
|
21544
21776
|
success: true,
|
|
@@ -21562,7 +21794,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
|
|
|
21562
21794
|
}, 403);
|
|
21563
21795
|
}
|
|
21564
21796
|
const db = c.env.DB;
|
|
21565
|
-
const migrationService = new
|
|
21797
|
+
const migrationService = new chunkI4V3VZWF_cjs.MigrationService(db);
|
|
21566
21798
|
const result = await migrationService.runPendingMigrations();
|
|
21567
21799
|
return c.json({
|
|
21568
21800
|
success: result.success,
|
|
@@ -21580,7 +21812,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
|
|
|
21580
21812
|
adminSettingsRoutes.get("/api/migrations/validate", async (c) => {
|
|
21581
21813
|
try {
|
|
21582
21814
|
const db = c.env.DB;
|
|
21583
|
-
const migrationService = new
|
|
21815
|
+
const migrationService = new chunkI4V3VZWF_cjs.MigrationService(db);
|
|
21584
21816
|
const validation = await migrationService.validateSchema();
|
|
21585
21817
|
return c.json({
|
|
21586
21818
|
success: true,
|
|
@@ -21804,7 +22036,6 @@ var ROUTES_INFO = {
|
|
|
21804
22036
|
reference: "https://github.com/sonicjs/sonicjs"
|
|
21805
22037
|
};
|
|
21806
22038
|
|
|
21807
|
-
exports.PluginBuilder = PluginBuilder;
|
|
21808
22039
|
exports.ROUTES_INFO = ROUTES_INFO;
|
|
21809
22040
|
exports.adminCheckboxRoutes = adminCheckboxRoutes;
|
|
21810
22041
|
exports.adminCollectionsRoutes = adminCollectionsRoutes;
|
|
@@ -21826,5 +22057,5 @@ exports.checkAdminUserExists = checkAdminUserExists;
|
|
|
21826
22057
|
exports.router = router;
|
|
21827
22058
|
exports.test_cleanup_default = test_cleanup_default;
|
|
21828
22059
|
exports.userRoutes = userRoutes;
|
|
21829
|
-
//# sourceMappingURL=chunk-
|
|
21830
|
-
//# sourceMappingURL=chunk-
|
|
22060
|
+
//# sourceMappingURL=chunk-UAQL2VWX.cjs.map
|
|
22061
|
+
//# sourceMappingURL=chunk-UAQL2VWX.cjs.map
|