@sonicjs-cms/core 2.3.17 → 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-TMQOLXLY.js → chunk-BHNDALCA.js} +56 -4
- package/dist/chunk-BHNDALCA.js.map +1 -0
- package/dist/{chunk-IEWLOVP3.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-QBWD6FKH.js → chunk-OJZ45OJD.js} +559 -281
- 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-VYL6RIV6.js → chunk-TJTWRO4G.js} +5 -5
- package/dist/chunk-TJTWRO4G.js.map +1 -0
- package/dist/{chunk-74DP754U.cjs → chunk-UAQL2VWX.cjs} +644 -366
- package/dist/chunk-UAQL2VWX.cjs.map +1 -0
- package/dist/{chunk-B6YJRVFQ.js → chunk-VEL7QRYI.js} +46 -2
- package/dist/chunk-VEL7QRYI.js.map +1 -0
- package/dist/{chunk-YHJB26RJ.cjs → chunk-YYV3XQOQ.cjs} +6 -6
- package/dist/chunk-YYV3XQOQ.cjs.map +1 -0
- package/dist/{chunk-2XCJ3HT5.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 +114 -85
- 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 +37 -8
- 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-EOV7NJZ7.cjs.map → migrations-NIEUFG44.cjs.map} +1 -1
- package/dist/migrations-TGZKJKV4.js +4 -0
- package/dist/{migrations-6HKPNPTK.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-2XCJ3HT5.cjs.map +0 -1
- package/dist/chunk-74DP754U.cjs.map +0 -1
- package/dist/chunk-B6YJRVFQ.js.map +0 -1
- package/dist/chunk-CPXAVWCU.js.map +0 -1
- package/dist/chunk-DTLB6UIH.cjs.map +0 -1
- package/dist/chunk-IEWLOVP3.cjs.map +0 -1
- package/dist/chunk-QBWD6FKH.js.map +0 -1
- package/dist/chunk-TMQOLXLY.js.map +0 -1
- package/dist/chunk-VYL6RIV6.js.map +0 -1
- package/dist/chunk-YHJB26RJ.cjs.map +0 -1
- package/dist/migrations-6HKPNPTK.js +0 -4
- package/dist/migrations-EOV7NJZ7.cjs +0 -13
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { getCacheService, CACHE_CONFIGS, getLogger, SettingsService } from './chunk-3YNNVSMC.js';
|
|
2
|
-
import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity } from './chunk-
|
|
2
|
+
import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity } from './chunk-TJTWRO4G.js';
|
|
3
3
|
import { PluginService } from './chunk-SGAG6FD3.js';
|
|
4
|
-
import { MigrationService } from './chunk-
|
|
4
|
+
import { MigrationService } from './chunk-VEL7QRYI.js';
|
|
5
5
|
import { init_admin_layout_catalyst_template, renderDesignPage, renderCheckboxPage, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayoutCatalyst, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-V5LBQN3I.js';
|
|
6
|
-
import {
|
|
6
|
+
import { PluginBuilder } from './chunk-QDBNW7KQ.js';
|
|
7
|
+
import { QueryFilterBuilder, getBlocksFieldConfig, parseBlocksValue, sanitizeInput, getCoreVersion, escapeHtml } from './chunk-BHNDALCA.js';
|
|
7
8
|
import { metricsTracker } from './chunk-FICTAGD4.js';
|
|
8
9
|
import { Hono } from 'hono';
|
|
9
10
|
import { cors } from 'hono/cors';
|
|
@@ -14,6 +15,37 @@ import { html, raw } from 'hono/html';
|
|
|
14
15
|
// src/schemas/index.ts
|
|
15
16
|
var schemaDefinitions = [];
|
|
16
17
|
var apiContentCrudRoutes = new Hono();
|
|
18
|
+
apiContentCrudRoutes.get("/check-slug", async (c) => {
|
|
19
|
+
try {
|
|
20
|
+
const db = c.env.DB;
|
|
21
|
+
const collectionId = c.req.query("collectionId");
|
|
22
|
+
const slug = c.req.query("slug");
|
|
23
|
+
const excludeId = c.req.query("excludeId");
|
|
24
|
+
if (!collectionId || !slug) {
|
|
25
|
+
return c.json({ error: "collectionId and slug are required" }, 400);
|
|
26
|
+
}
|
|
27
|
+
let query = "SELECT id FROM content WHERE collection_id = ? AND slug = ?";
|
|
28
|
+
const params = [collectionId, slug];
|
|
29
|
+
if (excludeId) {
|
|
30
|
+
query += " AND id != ?";
|
|
31
|
+
params.push(excludeId);
|
|
32
|
+
}
|
|
33
|
+
const existing = await db.prepare(query).bind(...params).first();
|
|
34
|
+
if (existing) {
|
|
35
|
+
return c.json({
|
|
36
|
+
available: false,
|
|
37
|
+
message: "This URL slug is already in use in this collection"
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return c.json({ available: true });
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error("Error checking slug:", error);
|
|
43
|
+
return c.json({
|
|
44
|
+
error: "Failed to check slug availability",
|
|
45
|
+
details: error instanceof Error ? error.message : String(error)
|
|
46
|
+
}, 500);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
17
49
|
apiContentCrudRoutes.get("/:id", async (c) => {
|
|
18
50
|
try {
|
|
19
51
|
const id = c.req.param("id");
|
|
@@ -1720,7 +1752,7 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
|
|
|
1720
1752
|
});
|
|
1721
1753
|
adminApiRoutes.get("/migrations/status", async (c) => {
|
|
1722
1754
|
try {
|
|
1723
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1755
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-TGZKJKV4.js');
|
|
1724
1756
|
const db = c.env.DB;
|
|
1725
1757
|
const migrationService = new MigrationService2(db);
|
|
1726
1758
|
const status = await migrationService.getMigrationStatus();
|
|
@@ -1745,7 +1777,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
1745
1777
|
error: "Unauthorized. Admin access required."
|
|
1746
1778
|
}, 403);
|
|
1747
1779
|
}
|
|
1748
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1780
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-TGZKJKV4.js');
|
|
1749
1781
|
const db = c.env.DB;
|
|
1750
1782
|
const migrationService = new MigrationService2(db);
|
|
1751
1783
|
const result = await migrationService.runPendingMigrations();
|
|
@@ -1764,7 +1796,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
1764
1796
|
});
|
|
1765
1797
|
adminApiRoutes.get("/migrations/validate", async (c) => {
|
|
1766
1798
|
try {
|
|
1767
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1799
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-TGZKJKV4.js');
|
|
1768
1800
|
const db = c.env.DB;
|
|
1769
1801
|
const migrationService = new MigrationService2(db);
|
|
1770
1802
|
const validation = await migrationService.validateSchema();
|
|
@@ -1991,18 +2023,40 @@ function renderRegisterPage(data) {
|
|
|
1991
2023
|
<div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
|
1992
2024
|
<!-- Logo Section -->
|
|
1993
2025
|
<div class="sm:mx-auto sm:w-full sm:max-w-md text-center">
|
|
1994
|
-
<div class="mx-auto
|
|
1995
|
-
<svg class="
|
|
1996
|
-
<path
|
|
2026
|
+
<div class="mx-auto w-64 mb-8">
|
|
2027
|
+
<svg class="w-full h-auto" viewBox="380 1300 2250 400" aria-hidden="true">
|
|
2028
|
+
<path fill="#F1F2F2" d="M476.851,1404.673h168.536c4.714,0,8.695-1.618,11.944-4.866c3.241-3.241,4.866-7.222,4.866-11.943 c0-2.357-0.443-4.569-1.327-6.636c-0.885-2.06-2.067-3.829-3.539-5.308c-1.479-1.472-3.249-2.654-5.308-3.538 c-2.067-0.885-4.279-1.327-6.635-1.327H476.851c-20.057,0-37.158,7.154-51.313,21.454c-14.155,14.308-21.233,31.483-21.233,51.534 c0,20.058,7.078,37.234,21.233,51.534c14.155,14.308,31.255,21.454,51.313,21.454h112.357c10.907,0,20.196,3.837,27.868,11.502 c7.666,7.672,11.502,16.885,11.502,27.646c0,10.769-3.836,19.982-11.502,27.647c-7.672,7.673-16.961,11.502-27.868,11.502H421.115 c-4.721,0-8.702,1.624-11.944,4.865c-3.248,3.249-4.866,7.23-4.866,11.944c0,3.248,0.733,6.123,2.212,8.626 c1.472,2.509,3.462,4.499,5.971,5.972c2.502,1.472,5.378,2.212,8.626,2.212h168.094c20.052,0,37.227-7.078,51.534-21.234 c14.3-14.155,21.454-31.331,21.454-51.534c0-20.196-7.154-37.379-21.454-51.534c-14.308-14.156-31.483-21.234-51.534-21.234 H476.851c-10.616,0-19.76-3.905-27.426-11.721c-7.672-7.811-11.501-17.101-11.501-27.87c0-10.761,3.829-19.975,11.501-27.647 C457.091,1408.508,466.235,1404.673,476.851,1404.673z"></path>
|
|
2029
|
+
<path fill="#F1F2F2" d="M974.78,1398.211c-5.016,6.574-10.034,13.146-15.048,19.721c-1.828,2.398-3.657,4.796-5.487,7.194 c1.994,1.719,3.958,3.51,5.873,5.424c18.724,18.731,28.089,41.216,28.089,67.459c0,26.251-9.366,48.658-28.089,67.237 c-18.731,18.579-41.215,27.868-67.459,27.868c-9.848,0-19.156-1.308-27.923-3.923l-4.185,3.354 c-8.587,6.885-17.154,13.796-25.725,20.702c17.52,8.967,36.86,13.487,58.054,13.487c35.533,0,65.91-12.608,91.124-37.821 c25.214-25.215,37.821-55.584,37.821-91.125c0-35.534-12.607-65.911-37.821-91.126 C981.004,1403.663,977.926,1400.854,974.78,1398.211z"></path>
|
|
2030
|
+
<path fill="#F1F2F2" d="M1364.644,1439.619c-4.72,0-8.702,1.624-11.943,4.865c-3.249,3.249-4.866,7.23-4.866,11.944v138.014 l-167.651-211.003c-0.297-0.586-0.74-1.03-1.327-1.326c-4.721-4.714-10.249-7.742-16.588-9.069 c-6.346-1.326-12.608-0.732-18.801,1.77c-6.192,2.509-11.059,6.49-14.598,11.944c-3.539,5.46-5.308,11.577-5.308,18.357v208.348 c0,4.721,1.618,8.703,4.866,11.944c3.241,3.241,7.222,4.865,11.943,4.865c2.945,0,5.751-0.738,8.405-2.211 c2.654-1.472,4.713-3.463,6.193-5.971c1.473-2.503,2.212-5.378,2.212-8.627v-205.251l166.325,209.675 c2.06,2.654,4.423,4.865,7.078,6.635c5.308,3.829,11.349,5.75,18.137,5.75c5.308,0,10.464-1.182,15.482-3.538 c3.539-1.769,6.56-4.127,9.069-7.078c2.502-2.945,4.491-6.338,5.971-10.175c1.473-3.829,2.212-7.664,2.212-11.501v-141.552 c0-4.714-1.624-8.695-4.865-11.944C1373.339,1441.243,1369.359,1439.619,1364.644,1439.619z"></path>
|
|
2031
|
+
<path fill="#F1F2F2" d="M1508.406,1432.983c-2.654-1.472-5.46-2.212-8.404-2.212c-4.721,0-8.703,1.7-11.944,5.087 c-3.249,3.395-4.865,7.3-4.865,11.723v163.228c0,4.721,1.616,8.702,4.865,11.943c3.241,3.249,7.223,4.866,11.944,4.866 c2.944,0,5.751-0.732,8.404-2.212c2.655-1.472,4.714-3.539,6.193-6.194c1.473-2.654,2.213-5.453,2.213-8.404V1447.58 c0-2.945-0.74-5.75-2.213-8.405C1513.12,1436.522,1511.06,1434.462,1508.406,1432.983z"></path>
|
|
2032
|
+
<path fill="#F1F2F2" d="M1499.78,1367.957c-4.575,0-8.481,1.625-11.722,4.866c-3.249,3.249-4.865,7.23-4.865,11.943 c0,2.951,0.732,5.75,2.212,8.405c1.472,2.654,3.463,4.721,5.971,6.193c2.503,1.479,5.378,2.212,8.627,2.212 c4.423,0,8.328-1.618,11.721-4.865c3.387-3.243,5.088-7.224,5.088-11.944c0-4.713-1.701-8.694-5.088-11.943 C1508.33,1369.582,1504.349,1367.957,1499.78,1367.957z"></path>
|
|
2033
|
+
<path fill="#F1F2F2" d="M1859.627,1369.727H1747.27c-35.388,0-65.69,12.607-90.904,37.821 c-25.213,25.215-37.82,55.591-37.82,91.125c0,35.54,12.607,65.911,37.82,91.125c25.215,25.215,55.516,37.821,90.904,37.821h56.178 c4.714,0,8.695-1.618,11.944-4.866c3.241-3.241,4.865-7.222,4.865-11.943c0-4.714-1.624-8.695-4.865-11.943 c-3.249-3.243-7.23-4.866-11.944-4.866h-56.178c-26.251,0-48.659-9.359-67.237-28.09c-18.579-18.723-27.868-41.207-27.868-67.459 c0-26.243,9.29-48.659,27.868-67.237c18.579-18.579,40.987-27.868,67.237-27.868h112.357c4.714,0,8.696-1.693,11.944-5.087 c3.241-3.387,4.865-7.368,4.865-11.943c0-4.569-1.624-8.475-4.865-11.723C1868.322,1371.351,1864.341,1369.727,1859.627,1369.727z "></path>
|
|
2034
|
+
<path fill="#06b6d4" d="M2219.256,1371.054h-112.357c-4.423,0-8.336,1.624-11.723,4.865c-3.393,3.249-5.087,7.23-5.087,11.944 c0,4.721,1.694,8.702,5.087,11.943c3.387,3.249,7.3,4.866,11.723,4.866h95.547v95.105c0,26.251-9.365,48.659-28.088,67.237 c-18.731,18.579-41.215,27.868-67.459,27.868c-26.251,0-48.659-9.289-67.237-27.868c-18.579-18.579-27.868-40.987-27.868-67.237 c0-4.713-1.701-8.771-5.088-12.165c-3.393-3.387-7.374-5.087-11.943-5.087c-4.575,0-8.481,1.7-11.722,5.087 c-3.249,3.393-4.865,7.451-4.865,12.165c0,35.388,12.607,65.69,37.82,90.904c25.215,25.213,55.584,37.82,91.126,37.82 c35.532,0,65.91-12.607,91.125-37.82c25.214-25.215,37.82-55.516,37.82-90.904v-111.915c0-4.714-1.624-8.695-4.865-11.944 C2227.951,1372.678,2223.971,1371.054,2219.256,1371.054z"></path>
|
|
2035
|
+
<path fill="#06b6d4" d="M2574.24,1502.875c-14.306-14.156-31.483-21.234-51.533-21.234H2410.35 c-10.617,0-19.762-3.829-27.426-11.501c-7.672-7.664-11.501-16.954-11.501-27.868c0-10.907,3.829-20.196,11.501-27.868 c7.664-7.664,16.809-11.501,27.426-11.501h112.357c4.714,0,8.695-1.617,11.944-4.866c3.241-3.241,4.865-7.222,4.865-11.943 c0-4.714-1.624-8.695-4.865-11.944c-3.249-3.241-7.23-4.865-11.944-4.865H2410.35c-20.058,0-37.158,7.154-51.313,21.454 c-14.156,14.308-21.232,31.483-21.232,51.534c0,20.058,7.077,37.234,21.232,51.534c14.156,14.308,31.255,21.454,51.313,21.454 h112.357c7.078,0,13.637,1.77,19.684,5.308c6.042,3.539,10.838,8.336,14.377,14.377c3.538,6.047,5.307,12.607,5.307,19.685 c0,10.616-3.835,19.76-11.501,27.425c-7.672,7.673-16.961,11.502-27.868,11.502h-168.094c-4.721,0-8.703,1.7-11.944,5.087 c-3.249,3.393-4.865,7.374-4.865,11.943c0,4.576,1.616,8.481,4.865,11.723c3.241,3.249,7.223,4.866,11.944,4.866h168.094 c20.051,0,37.227-7.078,51.533-21.234c14.302-14.155,21.454-31.331,21.454-51.534 C2595.695,1534.213,2588.542,1517.03,2574.24,1502.875z"></path>
|
|
2036
|
+
<path fill="#06b6d4" d="M854.024,1585.195l20.001-16.028c16.616-13.507,33.04-27.265,50.086-40.251 c1.13-0.861,2.9-1.686,2.003-3.516c-0.843-1.716-2.481-2.302-4.484-2.123c-8.514,0.765-17.016-0.538-25.537-0.353 c-1.124,0.024-2.768,0.221-3.163-1.25c-0.371-1.369,1.088-2.063,1.919-2.894c6.26-6.242,12.574-12.43,18.816-18.691 c9.303-9.327,18.565-18.714,27.851-28.066c1.848-1.859,3.701-3.713,5.549-5.572c2.655-2.661,5.309-5.315,7.958-7.982 c0.574-0.579,1.259-1.141,1.246-1.94c-0.004-0.257-0.078-0.538-0.254-0.853c-0.556-0.981-1.441-1.1-2.469-0.957 c-0.658,0.096-1.315,0.185-1.973,0.275c-3.844,0.538-7.689,1.076-11.533,1.608c-3.641,0.505-7.281,1.02-10.922,1.529 c-4.162,0.582-8.324,1.158-12.486,1.748c-1.142,0.161-2.409,1.662-3.354,0.508c-0.419-0.508-0.431-1.028-0.251-1.531 c0.269-0.741,0.957-1.441,1.387-2.021c3.414-4.58,6.882-9.124,10.356-13.662c1.74-2.272,3.48-4.544,5.214-6.822 c4.682-6.141,9.369-12.281,14.051-18.422c0.09-0.119,0.181-0.237,0.271-0.355c6.848-8.98,13.7-17.958,20.553-26.936 c0.488-0.64,0.977-1.28,1.465-1.92c2.159-2.828,4.315-5.658,6.476-8.486c4.197-5.501,8.454-10.954,12.67-16.442 c0.263-0.347,0.538-0.718,0.717-1.106c0.269-0.586,0.299-1.196-0.335-1.776c-0.825-0.753-1.8-0.15-2.595,0.419 c-0.67,0.472-1.333,0.957-1.955,1.489c-2.206,1.889-4.401,3.797-6.595,5.698c-3.958,3.438-7.922,6.876-11.976,10.194 c-2.443,2.003-4.865,4.028-7.301,6.038c-18.689-10.581-39.53-15.906-62.549-15.906c-35.54,0-65.911,12.607-91.125,37.82 c-25.214,25.215-37.821,55.592-37.821,91.126c0,35.54,12.607,65.91,37.821,91.125c4.146,4.146,8.445,7.916,12.87,11.381 c-9.015,11.14-18.036,22.277-27.034,33.429c-1.208,1.489-3.755,3.151-2.745,4.891c0.078,0.144,0.173,0.281,0.305,0.425 c1.321,1.429,3.492-1.303,4.933-2.457c6.673-5.333,13.333-10.685,19.982-16.042c3.707-2.984,7.417-5.965,11.124-8.952 c1.474-1.188,2.951-2.373,4.425-3.561c6.41-5.164,12.816-10.333,19.238-15.481L854.024,1585.195z M797.552,1498.009 c0-26.243,9.29-48.728,27.868-67.459c18.579-18.723,40.987-28.089,67.238-28.089c12.273,0,23.712,2.075,34.34,6.171 c-3.37,2.905-6.734,5.816-10.069,8.762c-6.075,5.351-12.365,10.469-18.667,15.564c-4.179,3.378-8.371,6.744-12.514,10.164 c-7.54,6.23-15.037,12.52-22.529,18.804c-7.091,5.955-14.182,11.904-21.19,17.949c-1.136,0.974-3.055,1.907-2.135,3.94 c0.831,1.836,2.774,1.417,4.341,1.578l12.145-0.599l14.151-0.698c1.031-0.102,2.192-0.257,2.89,0.632 c0.034,0.044,0.073,0.078,0.106,0.127c1.017,1.561-0.67,2.105-1.387,2.942c-6.308,7.318-12.616,14.637-18.978,21.907 c-8.161,9.339-16.353,18.649-24.544,27.958c-2.146,2.433-4.275,4.879-6.422,7.312c-1.034,1.172-2.129,2.272-1.238,3.922 c0.933,1.728,2.685,1.752,4.323,1.602c4.134-0.367,8.263-0.489,12.396-0.492c0.242,0,0.485-0.005,0.728-0.004 c2.711,0.009,5.422,0.068,8.134,0.145c2.582,0.074,5.166,0.165,7.752,0.249c0.275,1.62-0.879,2.356-1.62,3.259 c-1.333,1.626-2.667,3.247-4,4.867c-4.315,5.252-8.62,10.514-12.928,15.772c-3.562-2.725-7.007-5.733-10.324-9.051 C806.842,1546.667,797.552,1524.26,797.552,1498.009z"></path>
|
|
1997
2037
|
</svg>
|
|
1998
2038
|
</div>
|
|
1999
|
-
<
|
|
2000
|
-
|
|
2039
|
+
<h2 class="mt-6 text-xl font-medium text-white">${data.isSetup ? "Welcome to SonicJS" : "Create Account"}</h2>
|
|
2040
|
+
${data.isSetup ? `<p class="mt-2 text-sm text-zinc-400">Create your admin account to get started.</p>` : `<p class="mt-2 text-sm text-zinc-400">Create your account and get started</p>`}
|
|
2001
2041
|
</div>
|
|
2002
2042
|
|
|
2003
2043
|
<!-- Form Container -->
|
|
2004
2044
|
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
|
2005
2045
|
<div class="bg-zinc-900 shadow-sm ring-1 ring-white/10 rounded-xl px-6 py-8 sm:px-10">
|
|
2046
|
+
<!-- Setup Banner -->
|
|
2047
|
+
${data.isSetup ? `
|
|
2048
|
+
<div class="mb-6 rounded-lg bg-blue-500/10 p-4 ring-1 ring-blue-500/20">
|
|
2049
|
+
<div class="flex items-start gap-x-3">
|
|
2050
|
+
<svg class="h-5 w-5 text-blue-400 shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
2051
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
2052
|
+
</svg>
|
|
2053
|
+
<div class="flex-1">
|
|
2054
|
+
<p class="text-sm font-medium text-blue-300">First-time Setup</p>
|
|
2055
|
+
<p class="text-sm text-blue-400/80 mt-1">This account will be the administrator with full access to manage your SonicJS installation.</p>
|
|
2056
|
+
</div>
|
|
2057
|
+
</div>
|
|
2058
|
+
</div>
|
|
2059
|
+
` : ""}
|
|
2006
2060
|
<!-- Alerts -->
|
|
2007
2061
|
${data.error ? `<div class="mb-6">${renderAlert({ type: "error", message: data.error })}</div>` : ""}
|
|
2008
2062
|
|
|
@@ -2117,6 +2171,7 @@ function renderRegisterPage(data) {
|
|
|
2117
2171
|
</html>
|
|
2118
2172
|
`;
|
|
2119
2173
|
}
|
|
2174
|
+
var adminExistsCache = null;
|
|
2120
2175
|
async function isRegistrationEnabled(db) {
|
|
2121
2176
|
try {
|
|
2122
2177
|
const plugin = await db.prepare("SELECT settings FROM plugins WHERE id = ?").bind("core-auth").first();
|
|
@@ -2138,6 +2193,21 @@ async function isFirstUserRegistration(db) {
|
|
|
2138
2193
|
return false;
|
|
2139
2194
|
}
|
|
2140
2195
|
}
|
|
2196
|
+
async function checkAdminUserExists(db) {
|
|
2197
|
+
if (adminExistsCache !== null) {
|
|
2198
|
+
return adminExistsCache;
|
|
2199
|
+
}
|
|
2200
|
+
try {
|
|
2201
|
+
const result = await db.prepare("SELECT id FROM users WHERE role = ?").bind("admin").first();
|
|
2202
|
+
adminExistsCache = !!result;
|
|
2203
|
+
return adminExistsCache;
|
|
2204
|
+
} catch {
|
|
2205
|
+
return false;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
function setAdminExists() {
|
|
2209
|
+
adminExistsCache = true;
|
|
2210
|
+
}
|
|
2141
2211
|
var baseRegistrationSchema = z.object({
|
|
2142
2212
|
email: z.string().email("Valid email is required"),
|
|
2143
2213
|
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
@@ -2199,8 +2269,11 @@ authRoutes.get("/register", async (c) => {
|
|
|
2199
2269
|
}
|
|
2200
2270
|
}
|
|
2201
2271
|
const error = c.req.query("error");
|
|
2272
|
+
const isSetup = c.req.query("setup") === "true";
|
|
2202
2273
|
const pageData = {
|
|
2203
|
-
error: error || void 0
|
|
2274
|
+
error: error || void 0,
|
|
2275
|
+
isSetup: isSetup && isFirstUser
|
|
2276
|
+
// Only show setup message if truly first user
|
|
2204
2277
|
};
|
|
2205
2278
|
return c.html(renderRegisterPage(pageData));
|
|
2206
2279
|
});
|
|
@@ -2475,6 +2548,9 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2475
2548
|
now.getTime(),
|
|
2476
2549
|
now.getTime()
|
|
2477
2550
|
).run();
|
|
2551
|
+
if (isFirstUser) {
|
|
2552
|
+
setAdminExists();
|
|
2553
|
+
}
|
|
2478
2554
|
const token = await AuthManager.generateToken(userId, normalizedEmail, role);
|
|
2479
2555
|
setCookie(c, "auth_token", token, {
|
|
2480
2556
|
httpOnly: true,
|
|
@@ -2596,6 +2672,7 @@ authRoutes.post("/seed-admin", async (c) => {
|
|
|
2596
2672
|
if (existingAdmin) {
|
|
2597
2673
|
const passwordHash2 = await AuthManager.hashPassword("sonicjs!");
|
|
2598
2674
|
await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(passwordHash2, Date.now(), existingAdmin.id).run();
|
|
2675
|
+
setAdminExists();
|
|
2599
2676
|
return c.json({
|
|
2600
2677
|
message: "Admin user already exists (password updated)",
|
|
2601
2678
|
user: {
|
|
@@ -2626,6 +2703,7 @@ authRoutes.post("/seed-admin", async (c) => {
|
|
|
2626
2703
|
now,
|
|
2627
2704
|
now
|
|
2628
2705
|
).run();
|
|
2706
|
+
setAdminExists();
|
|
2629
2707
|
return c.json({
|
|
2630
2708
|
message: "Admin user created successfully",
|
|
2631
2709
|
user: {
|
|
@@ -3337,7 +3415,7 @@ init_admin_layout_catalyst_template();
|
|
|
3337
3415
|
|
|
3338
3416
|
// src/templates/components/dynamic-field.template.ts
|
|
3339
3417
|
function renderDynamicField(field, options = {}) {
|
|
3340
|
-
const { value = "", errors = [], disabled = false, className = "", pluginStatuses = {} } = options;
|
|
3418
|
+
const { value = "", errors = [], disabled = false, className = "", pluginStatuses = {}, collectionId = "", contentId = "" } = options;
|
|
3341
3419
|
const opts = field.field_options || {};
|
|
3342
3420
|
const required = field.is_required ? "required" : "";
|
|
3343
3421
|
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}`;
|
|
@@ -3590,67 +3668,171 @@ function renderDynamicField(field, options = {}) {
|
|
|
3590
3668
|
`;
|
|
3591
3669
|
break;
|
|
3592
3670
|
case "slug":
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3671
|
+
const slugPattern = opts.pattern || "^[a-z0-9-]+$";
|
|
3672
|
+
const collectionIdValue = collectionId || opts.collectionId || "";
|
|
3673
|
+
const contentIdValue = contentId || opts.contentId || "";
|
|
3674
|
+
const isEditMode = !!value;
|
|
3596
3675
|
fieldHTML = `
|
|
3597
|
-
<
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3676
|
+
<div class="slug-field-container">
|
|
3677
|
+
<input
|
|
3678
|
+
type="text"
|
|
3679
|
+
id="${fieldId}"
|
|
3680
|
+
name="${fieldName}"
|
|
3681
|
+
value="${escapeHtml2(value)}"
|
|
3682
|
+
placeholder="${opts.placeholder || "url-friendly-slug"}"
|
|
3683
|
+
maxlength="${opts.maxLength || 100}"
|
|
3684
|
+
data-pattern="${slugPattern}"
|
|
3685
|
+
data-collection-id="${collectionIdValue}"
|
|
3686
|
+
data-content-id="${contentIdValue}"
|
|
3687
|
+
data-is-edit-mode="${isEditMode}"
|
|
3688
|
+
class="${baseClasses} ${errorClasses}"
|
|
3689
|
+
${required}
|
|
3690
|
+
${disabled ? "disabled" : ""}
|
|
3691
|
+
>
|
|
3692
|
+
<div id="${fieldId}-status" class="slug-status mt-1 text-sm min-h-[20px]"></div>
|
|
3693
|
+
<button
|
|
3694
|
+
type="button"
|
|
3695
|
+
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"
|
|
3696
|
+
onclick="window.regenerateSlugFromTitle_${fieldId.replace(/-/g, "_")}()"
|
|
3697
|
+
>
|
|
3698
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
3699
|
+
<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>
|
|
3700
|
+
</svg>
|
|
3701
|
+
Regenerate from title
|
|
3702
|
+
</button>
|
|
3703
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">Use lowercase letters, numbers, and hyphens only</p>
|
|
3704
|
+
</div>
|
|
3705
|
+
|
|
3610
3706
|
<script>
|
|
3611
3707
|
(function() {
|
|
3612
|
-
const
|
|
3708
|
+
const slugField = document.getElementById('${fieldId}');
|
|
3709
|
+
const statusDiv = document.getElementById('${fieldId}-status');
|
|
3710
|
+
const isEditMode = slugField.dataset.isEditMode === 'true';
|
|
3613
3711
|
const pattern = new RegExp('${slugPattern}');
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
})();
|
|
3627
|
-
|
|
3628
|
-
function generateSlugFromTitle(slugFieldId) {
|
|
3629
|
-
const titleField = document.querySelector('input[name="title"]');
|
|
3630
|
-
const slugField = document.getElementById(slugFieldId);
|
|
3631
|
-
if (titleField && slugField) {
|
|
3632
|
-
const slug = titleField.value
|
|
3712
|
+
const collectionId = slugField.dataset.collectionId;
|
|
3713
|
+
const contentId = slugField.dataset.contentId;
|
|
3714
|
+
|
|
3715
|
+
let checkTimeout;
|
|
3716
|
+
let lastCheckedSlug = '';
|
|
3717
|
+
let manuallyEdited = false;
|
|
3718
|
+
|
|
3719
|
+
// Shared slug generation function
|
|
3720
|
+
function generateSlug(text) {
|
|
3721
|
+
if (!text) return '';
|
|
3722
|
+
|
|
3723
|
+
return text
|
|
3633
3724
|
.toLowerCase()
|
|
3725
|
+
.normalize('NFD')
|
|
3726
|
+
.replace(/[\\u0300-\\u036f]/g, '')
|
|
3634
3727
|
.replace(/[^a-z0-9\\s_-]/g, '')
|
|
3635
3728
|
.replace(/\\s+/g, '-')
|
|
3636
3729
|
.replace(/[-_]+/g, '-')
|
|
3637
|
-
.replace(/^[-_]
|
|
3638
|
-
|
|
3730
|
+
.replace(/^[-_]+|[-_]+$/g, '')
|
|
3731
|
+
.substring(0, 100);
|
|
3639
3732
|
}
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3733
|
+
|
|
3734
|
+
// Check if slug is available
|
|
3735
|
+
async function checkSlugAvailability(slug) {
|
|
3736
|
+
if (!slug || !collectionId) return;
|
|
3737
|
+
|
|
3738
|
+
// Don't check if it's the same as last time
|
|
3739
|
+
if (slug === lastCheckedSlug) return;
|
|
3740
|
+
lastCheckedSlug = slug;
|
|
3741
|
+
|
|
3742
|
+
try {
|
|
3743
|
+
// Show checking status
|
|
3744
|
+
statusDiv.innerHTML = '<span class="text-gray-400">\u23F3 Checking availability...</span>';
|
|
3745
|
+
|
|
3746
|
+
// Build URL
|
|
3747
|
+
let url = \`/api/content/check-slug?collectionId=\${encodeURIComponent(collectionId)}&slug=\${encodeURIComponent(slug)}\`;
|
|
3748
|
+
if (contentId) {
|
|
3749
|
+
url += \`&excludeId=\${encodeURIComponent(contentId)}\`;
|
|
3650
3750
|
}
|
|
3651
|
-
|
|
3751
|
+
|
|
3752
|
+
const response = await fetch(url);
|
|
3753
|
+
const data = await response.json();
|
|
3754
|
+
|
|
3755
|
+
if (data.available) {
|
|
3756
|
+
statusDiv.innerHTML = '<span class="text-green-500 dark:text-green-400">\u2713 Available</span>';
|
|
3757
|
+
slugField.setCustomValidity('');
|
|
3758
|
+
} else {
|
|
3759
|
+
statusDiv.innerHTML = \`<span class="text-red-500 dark:text-red-400">\u2717 \${data.message || 'Already in use'}</span>\`;
|
|
3760
|
+
slugField.setCustomValidity(data.message || 'This slug is already in use');
|
|
3761
|
+
}
|
|
3762
|
+
} catch (error) {
|
|
3763
|
+
console.error('Error checking slug:', error);
|
|
3764
|
+
statusDiv.innerHTML = '<span class="text-yellow-500 dark:text-yellow-400">\u26A0 Could not verify</span>';
|
|
3765
|
+
}
|
|
3652
3766
|
}
|
|
3653
|
-
|
|
3767
|
+
|
|
3768
|
+
// Format validation and duplicate checking
|
|
3769
|
+
slugField.addEventListener('input', function() {
|
|
3770
|
+
const value = this.value;
|
|
3771
|
+
|
|
3772
|
+
// Mark as manually edited if user types directly
|
|
3773
|
+
if (document.activeElement === this) {
|
|
3774
|
+
manuallyEdited = true;
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3777
|
+
// Clear status if empty
|
|
3778
|
+
if (!value) {
|
|
3779
|
+
statusDiv.innerHTML = '';
|
|
3780
|
+
this.setCustomValidity('');
|
|
3781
|
+
return;
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3784
|
+
// Pattern validation
|
|
3785
|
+
if (!pattern.test(value)) {
|
|
3786
|
+
this.setCustomValidity('Please use only lowercase letters, numbers, and hyphens.');
|
|
3787
|
+
statusDiv.innerHTML = '<span class="text-red-500 dark:text-red-400">\u2717 Invalid format</span>';
|
|
3788
|
+
return;
|
|
3789
|
+
}
|
|
3790
|
+
|
|
3791
|
+
// Debounce the availability check
|
|
3792
|
+
clearTimeout(checkTimeout);
|
|
3793
|
+
checkTimeout = setTimeout(() => {
|
|
3794
|
+
checkSlugAvailability(value);
|
|
3795
|
+
}, 500); // Wait 500ms after user stops typing
|
|
3796
|
+
});
|
|
3797
|
+
|
|
3798
|
+
// Initial check if field has value
|
|
3799
|
+
if (slugField.value) {
|
|
3800
|
+
checkSlugAvailability(slugField.value);
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
// Auto-generate only in create mode
|
|
3804
|
+
// Wait for all fields to be rendered before attaching listeners
|
|
3805
|
+
if (!isEditMode) {
|
|
3806
|
+
// Use setTimeout to ensure all fields in the form are rendered
|
|
3807
|
+
setTimeout(() => {
|
|
3808
|
+
const titleField = document.querySelector('input[name="title"]');
|
|
3809
|
+
if (titleField) {
|
|
3810
|
+
titleField.addEventListener('input', function() {
|
|
3811
|
+
if (!manuallyEdited) {
|
|
3812
|
+
const slug = generateSlug(this.value);
|
|
3813
|
+
slugField.value = slug;
|
|
3814
|
+
|
|
3815
|
+
// Trigger validation and duplicate check
|
|
3816
|
+
slugField.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3817
|
+
}
|
|
3818
|
+
});
|
|
3819
|
+
}
|
|
3820
|
+
}, 0);
|
|
3821
|
+
}
|
|
3822
|
+
|
|
3823
|
+
// Global function for regenerate button
|
|
3824
|
+
window.regenerateSlugFromTitle_${fieldId.replace(/-/g, "_")} = function() {
|
|
3825
|
+
const titleField = document.querySelector('input[name="title"]');
|
|
3826
|
+
if (titleField && slugField) {
|
|
3827
|
+
const slug = generateSlug(titleField.value);
|
|
3828
|
+
slugField.value = slug;
|
|
3829
|
+
manuallyEdited = false;
|
|
3830
|
+
|
|
3831
|
+
// Trigger validation and duplicate check
|
|
3832
|
+
slugField.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3833
|
+
}
|
|
3834
|
+
};
|
|
3835
|
+
})();
|
|
3654
3836
|
</script>
|
|
3655
3837
|
`;
|
|
3656
3838
|
break;
|
|
@@ -3783,207 +3965,6 @@ function escapeHtml2(text) {
|
|
|
3783
3965
|
"'": "'"
|
|
3784
3966
|
})[char] || char);
|
|
3785
3967
|
}
|
|
3786
|
-
var PluginBuilder = class _PluginBuilder {
|
|
3787
|
-
plugin;
|
|
3788
|
-
constructor(options) {
|
|
3789
|
-
this.plugin = {
|
|
3790
|
-
name: options.name,
|
|
3791
|
-
version: options.version,
|
|
3792
|
-
description: options.description,
|
|
3793
|
-
author: options.author,
|
|
3794
|
-
dependencies: options.dependencies,
|
|
3795
|
-
routes: [],
|
|
3796
|
-
middleware: [],
|
|
3797
|
-
models: [],
|
|
3798
|
-
services: [],
|
|
3799
|
-
adminPages: [],
|
|
3800
|
-
adminComponents: [],
|
|
3801
|
-
menuItems: [],
|
|
3802
|
-
hooks: []
|
|
3803
|
-
};
|
|
3804
|
-
}
|
|
3805
|
-
/**
|
|
3806
|
-
* Create a new plugin builder
|
|
3807
|
-
*/
|
|
3808
|
-
static create(options) {
|
|
3809
|
-
return new _PluginBuilder(options);
|
|
3810
|
-
}
|
|
3811
|
-
/**
|
|
3812
|
-
* Add metadata to the plugin
|
|
3813
|
-
*/
|
|
3814
|
-
metadata(metadata) {
|
|
3815
|
-
Object.assign(this.plugin, metadata);
|
|
3816
|
-
return this;
|
|
3817
|
-
}
|
|
3818
|
-
/**
|
|
3819
|
-
* Add routes to plugin
|
|
3820
|
-
*/
|
|
3821
|
-
addRoutes(routes) {
|
|
3822
|
-
this.plugin.routes = [...this.plugin.routes || [], ...routes];
|
|
3823
|
-
return this;
|
|
3824
|
-
}
|
|
3825
|
-
/**
|
|
3826
|
-
* Add a single route to plugin
|
|
3827
|
-
*/
|
|
3828
|
-
addRoute(path, handler, options) {
|
|
3829
|
-
const route = {
|
|
3830
|
-
path,
|
|
3831
|
-
handler,
|
|
3832
|
-
...options
|
|
3833
|
-
};
|
|
3834
|
-
this.plugin.routes = [...this.plugin.routes || [], route];
|
|
3835
|
-
return this;
|
|
3836
|
-
}
|
|
3837
|
-
/**
|
|
3838
|
-
* Add middleware to plugin
|
|
3839
|
-
*/
|
|
3840
|
-
addMiddleware(middleware) {
|
|
3841
|
-
this.plugin.middleware = [...this.plugin.middleware || [], ...middleware];
|
|
3842
|
-
return this;
|
|
3843
|
-
}
|
|
3844
|
-
/**
|
|
3845
|
-
* Add a single middleware to plugin
|
|
3846
|
-
*/
|
|
3847
|
-
addSingleMiddleware(name, handler, options) {
|
|
3848
|
-
const middleware = {
|
|
3849
|
-
name,
|
|
3850
|
-
handler,
|
|
3851
|
-
...options
|
|
3852
|
-
};
|
|
3853
|
-
this.plugin.middleware = [...this.plugin.middleware || [], middleware];
|
|
3854
|
-
return this;
|
|
3855
|
-
}
|
|
3856
|
-
/**
|
|
3857
|
-
* Add models to plugin
|
|
3858
|
-
*/
|
|
3859
|
-
addModels(models) {
|
|
3860
|
-
this.plugin.models = [...this.plugin.models || [], ...models];
|
|
3861
|
-
return this;
|
|
3862
|
-
}
|
|
3863
|
-
/**
|
|
3864
|
-
* Add a single model to plugin
|
|
3865
|
-
*/
|
|
3866
|
-
addModel(name, options) {
|
|
3867
|
-
const model = {
|
|
3868
|
-
name,
|
|
3869
|
-
...options
|
|
3870
|
-
};
|
|
3871
|
-
this.plugin.models = [...this.plugin.models || [], model];
|
|
3872
|
-
return this;
|
|
3873
|
-
}
|
|
3874
|
-
/**
|
|
3875
|
-
* Add services to plugin
|
|
3876
|
-
*/
|
|
3877
|
-
addServices(services) {
|
|
3878
|
-
this.plugin.services = [...this.plugin.services || [], ...services];
|
|
3879
|
-
return this;
|
|
3880
|
-
}
|
|
3881
|
-
/**
|
|
3882
|
-
* Add a single service to plugin
|
|
3883
|
-
*/
|
|
3884
|
-
addService(name, implementation, options) {
|
|
3885
|
-
const service = {
|
|
3886
|
-
name,
|
|
3887
|
-
implementation,
|
|
3888
|
-
...options
|
|
3889
|
-
};
|
|
3890
|
-
this.plugin.services = [...this.plugin.services || [], service];
|
|
3891
|
-
return this;
|
|
3892
|
-
}
|
|
3893
|
-
/**
|
|
3894
|
-
* Add admin pages to plugin
|
|
3895
|
-
*/
|
|
3896
|
-
addAdminPages(pages) {
|
|
3897
|
-
this.plugin.adminPages = [...this.plugin.adminPages || [], ...pages];
|
|
3898
|
-
return this;
|
|
3899
|
-
}
|
|
3900
|
-
/**
|
|
3901
|
-
* Add a single admin page to plugin
|
|
3902
|
-
*/
|
|
3903
|
-
addAdminPage(path, title, component, options) {
|
|
3904
|
-
const page = {
|
|
3905
|
-
path,
|
|
3906
|
-
title,
|
|
3907
|
-
component,
|
|
3908
|
-
...options
|
|
3909
|
-
};
|
|
3910
|
-
this.plugin.adminPages = [...this.plugin.adminPages || [], page];
|
|
3911
|
-
return this;
|
|
3912
|
-
}
|
|
3913
|
-
/**
|
|
3914
|
-
* Add admin components to plugin
|
|
3915
|
-
*/
|
|
3916
|
-
addComponents(components) {
|
|
3917
|
-
this.plugin.adminComponents = [...this.plugin.adminComponents || [], ...components];
|
|
3918
|
-
return this;
|
|
3919
|
-
}
|
|
3920
|
-
/**
|
|
3921
|
-
* Add a single admin component to plugin
|
|
3922
|
-
*/
|
|
3923
|
-
addComponent(name, template, options) {
|
|
3924
|
-
const component = {
|
|
3925
|
-
name,
|
|
3926
|
-
template,
|
|
3927
|
-
...options
|
|
3928
|
-
};
|
|
3929
|
-
this.plugin.adminComponents = [...this.plugin.adminComponents || [], component];
|
|
3930
|
-
return this;
|
|
3931
|
-
}
|
|
3932
|
-
/**
|
|
3933
|
-
* Add menu items to plugin
|
|
3934
|
-
*/
|
|
3935
|
-
addMenuItems(items) {
|
|
3936
|
-
this.plugin.menuItems = [...this.plugin.menuItems || [], ...items];
|
|
3937
|
-
return this;
|
|
3938
|
-
}
|
|
3939
|
-
/**
|
|
3940
|
-
* Add a single menu item to plugin
|
|
3941
|
-
*/
|
|
3942
|
-
addMenuItem(label, path, options) {
|
|
3943
|
-
const menuItem = {
|
|
3944
|
-
label,
|
|
3945
|
-
path,
|
|
3946
|
-
...options
|
|
3947
|
-
};
|
|
3948
|
-
this.plugin.menuItems = [...this.plugin.menuItems || [], menuItem];
|
|
3949
|
-
return this;
|
|
3950
|
-
}
|
|
3951
|
-
/**
|
|
3952
|
-
* Add hooks to plugin
|
|
3953
|
-
*/
|
|
3954
|
-
addHooks(hooks) {
|
|
3955
|
-
this.plugin.hooks = [...this.plugin.hooks || [], ...hooks];
|
|
3956
|
-
return this;
|
|
3957
|
-
}
|
|
3958
|
-
/**
|
|
3959
|
-
* Add a single hook to plugin
|
|
3960
|
-
*/
|
|
3961
|
-
addHook(name, handler, options) {
|
|
3962
|
-
const hook = {
|
|
3963
|
-
name,
|
|
3964
|
-
handler,
|
|
3965
|
-
...options
|
|
3966
|
-
};
|
|
3967
|
-
this.plugin.hooks = [...this.plugin.hooks || [], hook];
|
|
3968
|
-
return this;
|
|
3969
|
-
}
|
|
3970
|
-
/**
|
|
3971
|
-
* Add lifecycle hooks
|
|
3972
|
-
*/
|
|
3973
|
-
lifecycle(hooks) {
|
|
3974
|
-
Object.assign(this.plugin, hooks);
|
|
3975
|
-
return this;
|
|
3976
|
-
}
|
|
3977
|
-
/**
|
|
3978
|
-
* Build the plugin
|
|
3979
|
-
*/
|
|
3980
|
-
build() {
|
|
3981
|
-
if (!this.plugin.name || !this.plugin.version) {
|
|
3982
|
-
throw new Error("Plugin name and version are required");
|
|
3983
|
-
}
|
|
3984
|
-
return this.plugin;
|
|
3985
|
-
}
|
|
3986
|
-
};
|
|
3987
3968
|
|
|
3988
3969
|
// src/plugins/available/tinymce-plugin/index.ts
|
|
3989
3970
|
var builder = PluginBuilder.create({
|
|
@@ -4527,17 +4508,24 @@ function renderContentFormPage(data) {
|
|
|
4527
4508
|
const coreFieldsHTML = coreFields.sort((a, b) => a.field_order - b.field_order).map((field) => renderDynamicField(field, {
|
|
4528
4509
|
value: getFieldValue(field.field_name),
|
|
4529
4510
|
errors: data.validationErrors?.[field.field_name] || [],
|
|
4530
|
-
pluginStatuses
|
|
4511
|
+
pluginStatuses,
|
|
4512
|
+
collectionId: data.collection.id,
|
|
4513
|
+
contentId: data.id
|
|
4514
|
+
// Pass content ID when editing
|
|
4531
4515
|
}));
|
|
4532
4516
|
const contentFieldsHTML = contentFields.sort((a, b) => a.field_order - b.field_order).map((field) => renderDynamicField(field, {
|
|
4533
4517
|
value: getFieldValue(field.field_name),
|
|
4534
4518
|
errors: data.validationErrors?.[field.field_name] || [],
|
|
4535
|
-
pluginStatuses
|
|
4519
|
+
pluginStatuses,
|
|
4520
|
+
collectionId: data.collection.id,
|
|
4521
|
+
contentId: data.id
|
|
4536
4522
|
}));
|
|
4537
4523
|
const metaFieldsHTML = metaFields.sort((a, b) => a.field_order - b.field_order).map((field) => renderDynamicField(field, {
|
|
4538
4524
|
value: getFieldValue(field.field_name),
|
|
4539
4525
|
errors: data.validationErrors?.[field.field_name] || [],
|
|
4540
|
-
pluginStatuses
|
|
4526
|
+
pluginStatuses,
|
|
4527
|
+
collectionId: data.collection.id,
|
|
4528
|
+
contentId: data.id
|
|
4541
4529
|
}));
|
|
4542
4530
|
const pageContent = `
|
|
4543
4531
|
<div class="space-y-6">
|
|
@@ -6454,6 +6442,18 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
6454
6442
|
const errors = {};
|
|
6455
6443
|
for (const field of fields) {
|
|
6456
6444
|
const value = formData.get(field.field_name);
|
|
6445
|
+
const blocksConfig = getBlocksFieldConfig(field.field_options);
|
|
6446
|
+
if (blocksConfig) {
|
|
6447
|
+
const parsed = parseBlocksValue(value, blocksConfig);
|
|
6448
|
+
if (field.is_required && parsed.value.length === 0) {
|
|
6449
|
+
parsed.errors.push(`${field.field_label} is required`);
|
|
6450
|
+
}
|
|
6451
|
+
if (parsed.errors.length > 0) {
|
|
6452
|
+
errors[field.field_name] = parsed.errors;
|
|
6453
|
+
}
|
|
6454
|
+
data[field.field_name] = parsed.value;
|
|
6455
|
+
continue;
|
|
6456
|
+
}
|
|
6457
6457
|
if (field.is_required && (!value || value.toString().trim() === "")) {
|
|
6458
6458
|
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6459
6459
|
continue;
|
|
@@ -6476,6 +6476,67 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
6476
6476
|
data[field.field_name] = value;
|
|
6477
6477
|
}
|
|
6478
6478
|
break;
|
|
6479
|
+
case "array": {
|
|
6480
|
+
if (!value || value.toString().trim() === "") {
|
|
6481
|
+
data[field.field_name] = [];
|
|
6482
|
+
if (field.is_required) {
|
|
6483
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6484
|
+
}
|
|
6485
|
+
break;
|
|
6486
|
+
}
|
|
6487
|
+
try {
|
|
6488
|
+
const parsed = JSON.parse(value.toString());
|
|
6489
|
+
if (!Array.isArray(parsed)) {
|
|
6490
|
+
errors[field.field_name] = [`${field.field_label} must be a JSON array`];
|
|
6491
|
+
} else {
|
|
6492
|
+
if (field.is_required && parsed.length === 0) {
|
|
6493
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6494
|
+
}
|
|
6495
|
+
data[field.field_name] = parsed;
|
|
6496
|
+
}
|
|
6497
|
+
} catch {
|
|
6498
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6499
|
+
}
|
|
6500
|
+
break;
|
|
6501
|
+
}
|
|
6502
|
+
case "object": {
|
|
6503
|
+
if (!value || value.toString().trim() === "") {
|
|
6504
|
+
data[field.field_name] = {};
|
|
6505
|
+
if (field.is_required) {
|
|
6506
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6507
|
+
}
|
|
6508
|
+
break;
|
|
6509
|
+
}
|
|
6510
|
+
try {
|
|
6511
|
+
const parsed = JSON.parse(value.toString());
|
|
6512
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6513
|
+
errors[field.field_name] = [`${field.field_label} must be a JSON object`];
|
|
6514
|
+
} else {
|
|
6515
|
+
if (field.is_required && Object.keys(parsed).length === 0) {
|
|
6516
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6517
|
+
}
|
|
6518
|
+
data[field.field_name] = parsed;
|
|
6519
|
+
}
|
|
6520
|
+
} catch {
|
|
6521
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6522
|
+
}
|
|
6523
|
+
break;
|
|
6524
|
+
}
|
|
6525
|
+
case "json": {
|
|
6526
|
+
if (!value || value.toString().trim() === "") {
|
|
6527
|
+
data[field.field_name] = null;
|
|
6528
|
+
if (field.is_required) {
|
|
6529
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6530
|
+
}
|
|
6531
|
+
break;
|
|
6532
|
+
}
|
|
6533
|
+
try {
|
|
6534
|
+
data[field.field_name] = JSON.parse(value.toString());
|
|
6535
|
+
} catch {
|
|
6536
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6537
|
+
}
|
|
6538
|
+
break;
|
|
6539
|
+
}
|
|
6479
6540
|
default:
|
|
6480
6541
|
data[field.field_name] = value;
|
|
6481
6542
|
}
|
|
@@ -6600,6 +6661,18 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
6600
6661
|
const errors = {};
|
|
6601
6662
|
for (const field of fields) {
|
|
6602
6663
|
const value = formData.get(field.field_name);
|
|
6664
|
+
const blocksConfig = getBlocksFieldConfig(field.field_options);
|
|
6665
|
+
if (blocksConfig) {
|
|
6666
|
+
const parsed = parseBlocksValue(value, blocksConfig);
|
|
6667
|
+
if (field.is_required && parsed.value.length === 0) {
|
|
6668
|
+
parsed.errors.push(`${field.field_label} is required`);
|
|
6669
|
+
}
|
|
6670
|
+
if (parsed.errors.length > 0) {
|
|
6671
|
+
errors[field.field_name] = parsed.errors;
|
|
6672
|
+
}
|
|
6673
|
+
data[field.field_name] = parsed.value;
|
|
6674
|
+
continue;
|
|
6675
|
+
}
|
|
6603
6676
|
if (field.is_required && (!value || value.toString().trim() === "")) {
|
|
6604
6677
|
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6605
6678
|
continue;
|
|
@@ -6622,6 +6695,67 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
6622
6695
|
data[field.field_name] = value;
|
|
6623
6696
|
}
|
|
6624
6697
|
break;
|
|
6698
|
+
case "array": {
|
|
6699
|
+
if (!value || value.toString().trim() === "") {
|
|
6700
|
+
data[field.field_name] = [];
|
|
6701
|
+
if (field.is_required) {
|
|
6702
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6703
|
+
}
|
|
6704
|
+
break;
|
|
6705
|
+
}
|
|
6706
|
+
try {
|
|
6707
|
+
const parsed = JSON.parse(value.toString());
|
|
6708
|
+
if (!Array.isArray(parsed)) {
|
|
6709
|
+
errors[field.field_name] = [`${field.field_label} must be a JSON array`];
|
|
6710
|
+
} else {
|
|
6711
|
+
if (field.is_required && parsed.length === 0) {
|
|
6712
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6713
|
+
}
|
|
6714
|
+
data[field.field_name] = parsed;
|
|
6715
|
+
}
|
|
6716
|
+
} catch {
|
|
6717
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6718
|
+
}
|
|
6719
|
+
break;
|
|
6720
|
+
}
|
|
6721
|
+
case "object": {
|
|
6722
|
+
if (!value || value.toString().trim() === "") {
|
|
6723
|
+
data[field.field_name] = {};
|
|
6724
|
+
if (field.is_required) {
|
|
6725
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6726
|
+
}
|
|
6727
|
+
break;
|
|
6728
|
+
}
|
|
6729
|
+
try {
|
|
6730
|
+
const parsed = JSON.parse(value.toString());
|
|
6731
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6732
|
+
errors[field.field_name] = [`${field.field_label} must be a JSON object`];
|
|
6733
|
+
} else {
|
|
6734
|
+
if (field.is_required && Object.keys(parsed).length === 0) {
|
|
6735
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6736
|
+
}
|
|
6737
|
+
data[field.field_name] = parsed;
|
|
6738
|
+
}
|
|
6739
|
+
} catch {
|
|
6740
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6741
|
+
}
|
|
6742
|
+
break;
|
|
6743
|
+
}
|
|
6744
|
+
case "json": {
|
|
6745
|
+
if (!value || value.toString().trim() === "") {
|
|
6746
|
+
data[field.field_name] = null;
|
|
6747
|
+
if (field.is_required) {
|
|
6748
|
+
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6749
|
+
}
|
|
6750
|
+
break;
|
|
6751
|
+
}
|
|
6752
|
+
try {
|
|
6753
|
+
data[field.field_name] = JSON.parse(value.toString());
|
|
6754
|
+
} catch {
|
|
6755
|
+
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6756
|
+
}
|
|
6757
|
+
break;
|
|
6758
|
+
}
|
|
6625
6759
|
default:
|
|
6626
6760
|
data[field.field_name] = value;
|
|
6627
6761
|
}
|
|
@@ -6741,6 +6875,12 @@ adminContentRoutes.post("/preview", async (c) => {
|
|
|
6741
6875
|
const data = {};
|
|
6742
6876
|
for (const field of fields) {
|
|
6743
6877
|
const value = formData.get(field.field_name);
|
|
6878
|
+
const blocksConfig = getBlocksFieldConfig(field.field_options);
|
|
6879
|
+
if (blocksConfig) {
|
|
6880
|
+
const parsed = parseBlocksValue(value, blocksConfig);
|
|
6881
|
+
data[field.field_name] = parsed.value;
|
|
6882
|
+
continue;
|
|
6883
|
+
}
|
|
6744
6884
|
switch (field.field_type) {
|
|
6745
6885
|
case "number":
|
|
6746
6886
|
data[field.field_name] = value ? Number(value) : null;
|
|
@@ -13269,6 +13409,9 @@ function renderAuthSettingsForm(settings) {
|
|
|
13269
13409
|
}
|
|
13270
13410
|
|
|
13271
13411
|
// src/templates/pages/admin-plugin-settings.template.ts
|
|
13412
|
+
function escapeHtmlAttr(value) {
|
|
13413
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
13414
|
+
}
|
|
13272
13415
|
function renderPluginSettingsPage(data) {
|
|
13273
13416
|
const { plugin, activity = [], user } = data;
|
|
13274
13417
|
const pageContent = `
|
|
@@ -13546,6 +13689,7 @@ function renderSettingsTab(plugin) {
|
|
|
13546
13689
|
const settings = plugin.settings || {};
|
|
13547
13690
|
const isSeedDataPlugin = plugin.id === "seed-data" || plugin.name === "seed-data";
|
|
13548
13691
|
const isAuthPlugin = plugin.id === "core-auth" || plugin.name === "core-auth";
|
|
13692
|
+
const isTurnstilePlugin = plugin.id === "turnstile" || plugin.name === "turnstile";
|
|
13549
13693
|
return `
|
|
13550
13694
|
${isSeedDataPlugin ? `
|
|
13551
13695
|
<div class="backdrop-blur-md bg-black/20 rounded-xl border border-white/10 shadow-xl p-6 mb-6">
|
|
@@ -13572,12 +13716,15 @@ function renderSettingsTab(plugin) {
|
|
|
13572
13716
|
${isAuthPlugin ? `
|
|
13573
13717
|
<h2 class="text-xl font-semibold text-white mb-4">Authentication Settings</h2>
|
|
13574
13718
|
<p class="text-gray-400 mb-6">Configure user registration fields and validation rules.</p>
|
|
13719
|
+
` : isTurnstilePlugin ? `
|
|
13720
|
+
<h2 class="text-xl font-semibold text-white mb-4">Cloudflare Turnstile Settings</h2>
|
|
13721
|
+
<p class="text-gray-400 mb-6">Configure CAPTCHA-free bot protection for your forms.</p>
|
|
13575
13722
|
` : `
|
|
13576
13723
|
<h2 class="text-xl font-semibold text-white mb-4">Plugin Settings</h2>
|
|
13577
13724
|
`}
|
|
13578
13725
|
|
|
13579
13726
|
<form id="settings-form" class="space-y-6">
|
|
13580
|
-
${isAuthPlugin && Object.keys(settings).length > 0 ? renderAuthSettingsForm(settings) : Object.keys(settings).length > 0 ? renderSettingsFields(settings) : renderNoSettings(plugin)}
|
|
13727
|
+
${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)}
|
|
13581
13728
|
|
|
13582
13729
|
${Object.keys(settings).length > 0 ? `
|
|
13583
13730
|
<div class="flex items-center justify-end pt-6 border-t border-white/10">
|
|
@@ -13641,6 +13788,80 @@ function renderSettingsFields(settings) {
|
|
|
13641
13788
|
}
|
|
13642
13789
|
}).join("");
|
|
13643
13790
|
}
|
|
13791
|
+
function renderTurnstileSettingsForm(settings) {
|
|
13792
|
+
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";
|
|
13793
|
+
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";
|
|
13794
|
+
return `
|
|
13795
|
+
<!-- Enable Toggle -->
|
|
13796
|
+
<div class="flex items-center justify-between">
|
|
13797
|
+
<div>
|
|
13798
|
+
<label for="setting_enabled" class="text-sm font-medium text-gray-300">Enable Turnstile</label>
|
|
13799
|
+
<p class="text-xs text-gray-400">Enable or disable Turnstile verification globally</p>
|
|
13800
|
+
</div>
|
|
13801
|
+
<label class="relative inline-flex items-center cursor-pointer">
|
|
13802
|
+
<input type="checkbox" name="setting_enabled" id="setting_enabled" ${settings.enabled ? "checked" : ""} class="sr-only peer">
|
|
13803
|
+
<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>
|
|
13804
|
+
</label>
|
|
13805
|
+
</div>
|
|
13806
|
+
|
|
13807
|
+
<!-- Site Key -->
|
|
13808
|
+
<div>
|
|
13809
|
+
<label for="setting_siteKey" class="block text-sm font-medium text-gray-300 mb-2">Site Key</label>
|
|
13810
|
+
<input type="text" name="setting_siteKey" id="setting_siteKey" value="${escapeHtmlAttr(settings.siteKey || "")}" placeholder="0x4AAAAAAAA..." class="${inputClass}">
|
|
13811
|
+
<p class="text-xs text-gray-400 mt-1">Your Cloudflare Turnstile site key (public)</p>
|
|
13812
|
+
</div>
|
|
13813
|
+
|
|
13814
|
+
<!-- Secret Key -->
|
|
13815
|
+
<div>
|
|
13816
|
+
<label for="setting_secretKey" class="block text-sm font-medium text-gray-300 mb-2">Secret Key</label>
|
|
13817
|
+
<input type="password" name="setting_secretKey" id="setting_secretKey" value="${escapeHtmlAttr(settings.secretKey || "")}" placeholder="0x4AAAAAAAA..." class="${inputClass}">
|
|
13818
|
+
<p class="text-xs text-gray-400 mt-1">Your Cloudflare Turnstile secret key (private)</p>
|
|
13819
|
+
</div>
|
|
13820
|
+
|
|
13821
|
+
<!-- Theme -->
|
|
13822
|
+
<div>
|
|
13823
|
+
<label for="setting_theme" class="block text-sm font-medium text-gray-300 mb-2">Widget Theme</label>
|
|
13824
|
+
<select name="setting_theme" id="setting_theme" class="${selectClass}" style="color: white; background-color: rgb(39, 39, 42);">
|
|
13825
|
+
<option value="auto" ${settings.theme === "auto" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Auto (matches page theme)</option>
|
|
13826
|
+
<option value="light" ${settings.theme === "light" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Light</option>
|
|
13827
|
+
<option value="dark" ${settings.theme === "dark" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Dark</option>
|
|
13828
|
+
</select>
|
|
13829
|
+
<p class="text-xs text-gray-400 mt-1">Visual appearance of the Turnstile widget</p>
|
|
13830
|
+
</div>
|
|
13831
|
+
|
|
13832
|
+
<!-- Size -->
|
|
13833
|
+
<div>
|
|
13834
|
+
<label for="setting_size" class="block text-sm font-medium text-gray-300 mb-2">Widget Size</label>
|
|
13835
|
+
<select name="setting_size" id="setting_size" class="${selectClass}" style="color: white; background-color: rgb(39, 39, 42);">
|
|
13836
|
+
<option value="normal" ${settings.size === "normal" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Normal (300x65px)</option>
|
|
13837
|
+
<option value="compact" ${settings.size === "compact" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Compact (130x120px)</option>
|
|
13838
|
+
</select>
|
|
13839
|
+
<p class="text-xs text-gray-400 mt-1">Size of the Turnstile challenge widget</p>
|
|
13840
|
+
</div>
|
|
13841
|
+
|
|
13842
|
+
<!-- Widget Mode -->
|
|
13843
|
+
<div>
|
|
13844
|
+
<label for="setting_mode" class="block text-sm font-medium text-gray-300 mb-2">Widget Mode</label>
|
|
13845
|
+
<select name="setting_mode" id="setting_mode" class="${selectClass}" style="color: white; background-color: rgb(39, 39, 42);">
|
|
13846
|
+
<option value="managed" ${!settings.mode || settings.mode === "managed" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Managed (Recommended) - Adaptive challenge</option>
|
|
13847
|
+
<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>
|
|
13848
|
+
<option value="invisible" ${settings.mode === "invisible" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Invisible - No visible widget</option>
|
|
13849
|
+
</select>
|
|
13850
|
+
<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>
|
|
13851
|
+
</div>
|
|
13852
|
+
|
|
13853
|
+
<!-- Appearance (Pre-clearance) -->
|
|
13854
|
+
<div>
|
|
13855
|
+
<label for="setting_appearance" class="block text-sm font-medium text-gray-300 mb-2">Pre-clearance / Appearance</label>
|
|
13856
|
+
<select name="setting_appearance" id="setting_appearance" class="${selectClass}" style="color: white; background-color: rgb(39, 39, 42);">
|
|
13857
|
+
<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>
|
|
13858
|
+
<option value="execute" ${settings.appearance === "execute" ? "selected" : ""} style="background-color: rgb(39, 39, 42); color: white;">Execute - Challenge on form submit</option>
|
|
13859
|
+
<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>
|
|
13860
|
+
</select>
|
|
13861
|
+
<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>
|
|
13862
|
+
</div>
|
|
13863
|
+
`;
|
|
13864
|
+
}
|
|
13644
13865
|
function renderNoSettings(plugin) {
|
|
13645
13866
|
if (plugin.id === "seed-data" || plugin.name === "seed-data") {
|
|
13646
13867
|
return `
|
|
@@ -13872,6 +14093,19 @@ var AVAILABLE_PLUGINS = [
|
|
|
13872
14093
|
permissions: [],
|
|
13873
14094
|
dependencies: [],
|
|
13874
14095
|
is_core: false
|
|
14096
|
+
},
|
|
14097
|
+
{
|
|
14098
|
+
id: "turnstile",
|
|
14099
|
+
name: "turnstile-plugin",
|
|
14100
|
+
display_name: "Cloudflare Turnstile",
|
|
14101
|
+
description: "CAPTCHA-free bot protection for forms using Cloudflare Turnstile. Provides seamless spam prevention with configurable modes, themes, and pre-clearance options.",
|
|
14102
|
+
version: "1.0.0",
|
|
14103
|
+
author: "SonicJS Team",
|
|
14104
|
+
category: "security",
|
|
14105
|
+
icon: "\u{1F6E1}\uFE0F",
|
|
14106
|
+
permissions: [],
|
|
14107
|
+
dependencies: [],
|
|
14108
|
+
is_core: true
|
|
13875
14109
|
}
|
|
13876
14110
|
];
|
|
13877
14111
|
adminPluginRoutes.get("/", async (c) => {
|
|
@@ -14242,6 +14476,33 @@ adminPluginRoutes.post("/install", async (c) => {
|
|
|
14242
14476
|
});
|
|
14243
14477
|
return c.json({ success: true, plugin: easyMdxPlugin2 });
|
|
14244
14478
|
}
|
|
14479
|
+
if (body.name === "turnstile-plugin") {
|
|
14480
|
+
const turnstilePlugin = await pluginService.installPlugin({
|
|
14481
|
+
id: "turnstile",
|
|
14482
|
+
name: "turnstile-plugin",
|
|
14483
|
+
display_name: "Cloudflare Turnstile",
|
|
14484
|
+
description: "CAPTCHA-free bot protection for forms using Cloudflare Turnstile. Provides seamless spam prevention with configurable modes, themes, and pre-clearance options.",
|
|
14485
|
+
version: "1.0.0",
|
|
14486
|
+
author: "SonicJS Team",
|
|
14487
|
+
category: "security",
|
|
14488
|
+
icon: "\u{1F6E1}\uFE0F",
|
|
14489
|
+
permissions: [],
|
|
14490
|
+
dependencies: [],
|
|
14491
|
+
is_core: true,
|
|
14492
|
+
settings: {
|
|
14493
|
+
siteKey: "",
|
|
14494
|
+
secretKey: "",
|
|
14495
|
+
theme: "auto",
|
|
14496
|
+
size: "normal",
|
|
14497
|
+
mode: "managed",
|
|
14498
|
+
appearance: "always",
|
|
14499
|
+
preClearanceEnabled: false,
|
|
14500
|
+
preClearanceLevel: "managed",
|
|
14501
|
+
enabled: false
|
|
14502
|
+
}
|
|
14503
|
+
});
|
|
14504
|
+
return c.json({ success: true, plugin: turnstilePlugin });
|
|
14505
|
+
}
|
|
14245
14506
|
return c.json({ error: "Plugin not found in registry" }, 404);
|
|
14246
14507
|
} catch (error) {
|
|
14247
14508
|
console.error("Error installing plugin:", error);
|
|
@@ -19415,16 +19676,30 @@ adminCollectionsRoutes.get("/:id", async (c) => {
|
|
|
19415
19676
|
const schema = typeof collection.schema === "string" ? JSON.parse(collection.schema) : collection.schema;
|
|
19416
19677
|
if (schema && schema.properties) {
|
|
19417
19678
|
let fieldOrder = 0;
|
|
19418
|
-
fields = Object.entries(schema.properties).map(([fieldName, fieldConfig]) =>
|
|
19419
|
-
|
|
19420
|
-
|
|
19421
|
-
|
|
19422
|
-
|
|
19423
|
-
|
|
19424
|
-
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19679
|
+
fields = Object.entries(schema.properties).map(([fieldName, fieldConfig]) => {
|
|
19680
|
+
let fieldType = fieldConfig.type || "string";
|
|
19681
|
+
if (fieldConfig.enum) {
|
|
19682
|
+
fieldType = "select";
|
|
19683
|
+
} else if (fieldConfig.format === "richtext") {
|
|
19684
|
+
fieldType = "richtext";
|
|
19685
|
+
} else if (fieldConfig.format === "media") {
|
|
19686
|
+
fieldType = "media";
|
|
19687
|
+
} else if (fieldConfig.format === "date-time") {
|
|
19688
|
+
fieldType = "date";
|
|
19689
|
+
} else if (fieldConfig.type === "slug" || fieldConfig.format === "slug") {
|
|
19690
|
+
fieldType = "slug";
|
|
19691
|
+
}
|
|
19692
|
+
return {
|
|
19693
|
+
id: `schema-${fieldName}`,
|
|
19694
|
+
field_name: fieldName,
|
|
19695
|
+
field_type: fieldType,
|
|
19696
|
+
field_label: fieldConfig.title || fieldName,
|
|
19697
|
+
field_options: fieldConfig,
|
|
19698
|
+
field_order: fieldOrder++,
|
|
19699
|
+
is_required: fieldConfig.required === true || schema.required && schema.required.includes(fieldName),
|
|
19700
|
+
is_searchable: fieldConfig.searchable === true || false
|
|
19701
|
+
};
|
|
19702
|
+
});
|
|
19428
19703
|
}
|
|
19429
19704
|
} catch (e) {
|
|
19430
19705
|
console.error("Error parsing collection schema:", e);
|
|
@@ -19639,6 +19914,9 @@ adminCollectionsRoutes.post("/:id/fields", async (c) => {
|
|
|
19639
19914
|
fieldConfig.enum = parsedOptions.options || [];
|
|
19640
19915
|
} else if (fieldType === "media") {
|
|
19641
19916
|
fieldConfig.format = "media";
|
|
19917
|
+
} else if (fieldType === "slug") {
|
|
19918
|
+
fieldConfig.type = "slug";
|
|
19919
|
+
fieldConfig.format = "slug";
|
|
19642
19920
|
} else if (fieldType === "quill") {
|
|
19643
19921
|
fieldConfig.type = "quill";
|
|
19644
19922
|
} else if (fieldType === "mdxeditor") {
|
|
@@ -21756,6 +22034,6 @@ var ROUTES_INFO = {
|
|
|
21756
22034
|
reference: "https://github.com/sonicjs/sonicjs"
|
|
21757
22035
|
};
|
|
21758
22036
|
|
|
21759
|
-
export {
|
|
21760
|
-
//# sourceMappingURL=chunk-
|
|
21761
|
-
//# sourceMappingURL=chunk-
|
|
22037
|
+
export { ROUTES_INFO, adminCheckboxRoutes, adminCollectionsRoutes, adminDesignRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, admin_api_default, admin_code_examples_default, admin_content_default, admin_testimonials_default, api_content_crud_default, api_default, api_media_default, api_system_default, auth_default, checkAdminUserExists, router, test_cleanup_default, userRoutes };
|
|
22038
|
+
//# sourceMappingURL=chunk-OJZ45OJD.js.map
|
|
22039
|
+
//# sourceMappingURL=chunk-OJZ45OJD.js.map
|