@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,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();
|
|
@@ -3383,7 +3415,7 @@ init_admin_layout_catalyst_template();
|
|
|
3383
3415
|
|
|
3384
3416
|
// src/templates/components/dynamic-field.template.ts
|
|
3385
3417
|
function renderDynamicField(field, options = {}) {
|
|
3386
|
-
const { value = "", errors = [], disabled = false, className = "", pluginStatuses = {} } = options;
|
|
3418
|
+
const { value = "", errors = [], disabled = false, className = "", pluginStatuses = {}, collectionId = "", contentId = "" } = options;
|
|
3387
3419
|
const opts = field.field_options || {};
|
|
3388
3420
|
const required = field.is_required ? "required" : "";
|
|
3389
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}`;
|
|
@@ -3636,67 +3668,171 @@ function renderDynamicField(field, options = {}) {
|
|
|
3636
3668
|
`;
|
|
3637
3669
|
break;
|
|
3638
3670
|
case "slug":
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3671
|
+
const slugPattern = opts.pattern || "^[a-z0-9-]+$";
|
|
3672
|
+
const collectionIdValue = collectionId || opts.collectionId || "";
|
|
3673
|
+
const contentIdValue = contentId || opts.contentId || "";
|
|
3674
|
+
const isEditMode = !!value;
|
|
3642
3675
|
fieldHTML = `
|
|
3643
|
-
<
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
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
|
+
|
|
3656
3706
|
<script>
|
|
3657
3707
|
(function() {
|
|
3658
|
-
const
|
|
3708
|
+
const slugField = document.getElementById('${fieldId}');
|
|
3709
|
+
const statusDiv = document.getElementById('${fieldId}-status');
|
|
3710
|
+
const isEditMode = slugField.dataset.isEditMode === 'true';
|
|
3659
3711
|
const pattern = new RegExp('${slugPattern}');
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
})();
|
|
3673
|
-
|
|
3674
|
-
function generateSlugFromTitle(slugFieldId) {
|
|
3675
|
-
const titleField = document.querySelector('input[name="title"]');
|
|
3676
|
-
const slugField = document.getElementById(slugFieldId);
|
|
3677
|
-
if (titleField && slugField) {
|
|
3678
|
-
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
|
|
3679
3724
|
.toLowerCase()
|
|
3725
|
+
.normalize('NFD')
|
|
3726
|
+
.replace(/[\\u0300-\\u036f]/g, '')
|
|
3680
3727
|
.replace(/[^a-z0-9\\s_-]/g, '')
|
|
3681
3728
|
.replace(/\\s+/g, '-')
|
|
3682
3729
|
.replace(/[-_]+/g, '-')
|
|
3683
|
-
.replace(/^[-_]
|
|
3684
|
-
|
|
3730
|
+
.replace(/^[-_]+|[-_]+$/g, '')
|
|
3731
|
+
.substring(0, 100);
|
|
3685
3732
|
}
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
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)}\`;
|
|
3696
3750
|
}
|
|
3697
|
-
|
|
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
|
+
}
|
|
3698
3766
|
}
|
|
3699
|
-
|
|
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
|
+
})();
|
|
3700
3836
|
</script>
|
|
3701
3837
|
`;
|
|
3702
3838
|
break;
|
|
@@ -3829,207 +3965,6 @@ function escapeHtml2(text) {
|
|
|
3829
3965
|
"'": "'"
|
|
3830
3966
|
})[char] || char);
|
|
3831
3967
|
}
|
|
3832
|
-
var PluginBuilder = class _PluginBuilder {
|
|
3833
|
-
plugin;
|
|
3834
|
-
constructor(options) {
|
|
3835
|
-
this.plugin = {
|
|
3836
|
-
name: options.name,
|
|
3837
|
-
version: options.version,
|
|
3838
|
-
description: options.description,
|
|
3839
|
-
author: options.author,
|
|
3840
|
-
dependencies: options.dependencies,
|
|
3841
|
-
routes: [],
|
|
3842
|
-
middleware: [],
|
|
3843
|
-
models: [],
|
|
3844
|
-
services: [],
|
|
3845
|
-
adminPages: [],
|
|
3846
|
-
adminComponents: [],
|
|
3847
|
-
menuItems: [],
|
|
3848
|
-
hooks: []
|
|
3849
|
-
};
|
|
3850
|
-
}
|
|
3851
|
-
/**
|
|
3852
|
-
* Create a new plugin builder
|
|
3853
|
-
*/
|
|
3854
|
-
static create(options) {
|
|
3855
|
-
return new _PluginBuilder(options);
|
|
3856
|
-
}
|
|
3857
|
-
/**
|
|
3858
|
-
* Add metadata to the plugin
|
|
3859
|
-
*/
|
|
3860
|
-
metadata(metadata) {
|
|
3861
|
-
Object.assign(this.plugin, metadata);
|
|
3862
|
-
return this;
|
|
3863
|
-
}
|
|
3864
|
-
/**
|
|
3865
|
-
* Add routes to plugin
|
|
3866
|
-
*/
|
|
3867
|
-
addRoutes(routes) {
|
|
3868
|
-
this.plugin.routes = [...this.plugin.routes || [], ...routes];
|
|
3869
|
-
return this;
|
|
3870
|
-
}
|
|
3871
|
-
/**
|
|
3872
|
-
* Add a single route to plugin
|
|
3873
|
-
*/
|
|
3874
|
-
addRoute(path, handler, options) {
|
|
3875
|
-
const route = {
|
|
3876
|
-
path,
|
|
3877
|
-
handler,
|
|
3878
|
-
...options
|
|
3879
|
-
};
|
|
3880
|
-
this.plugin.routes = [...this.plugin.routes || [], route];
|
|
3881
|
-
return this;
|
|
3882
|
-
}
|
|
3883
|
-
/**
|
|
3884
|
-
* Add middleware to plugin
|
|
3885
|
-
*/
|
|
3886
|
-
addMiddleware(middleware) {
|
|
3887
|
-
this.plugin.middleware = [...this.plugin.middleware || [], ...middleware];
|
|
3888
|
-
return this;
|
|
3889
|
-
}
|
|
3890
|
-
/**
|
|
3891
|
-
* Add a single middleware to plugin
|
|
3892
|
-
*/
|
|
3893
|
-
addSingleMiddleware(name, handler, options) {
|
|
3894
|
-
const middleware = {
|
|
3895
|
-
name,
|
|
3896
|
-
handler,
|
|
3897
|
-
...options
|
|
3898
|
-
};
|
|
3899
|
-
this.plugin.middleware = [...this.plugin.middleware || [], middleware];
|
|
3900
|
-
return this;
|
|
3901
|
-
}
|
|
3902
|
-
/**
|
|
3903
|
-
* Add models to plugin
|
|
3904
|
-
*/
|
|
3905
|
-
addModels(models) {
|
|
3906
|
-
this.plugin.models = [...this.plugin.models || [], ...models];
|
|
3907
|
-
return this;
|
|
3908
|
-
}
|
|
3909
|
-
/**
|
|
3910
|
-
* Add a single model to plugin
|
|
3911
|
-
*/
|
|
3912
|
-
addModel(name, options) {
|
|
3913
|
-
const model = {
|
|
3914
|
-
name,
|
|
3915
|
-
...options
|
|
3916
|
-
};
|
|
3917
|
-
this.plugin.models = [...this.plugin.models || [], model];
|
|
3918
|
-
return this;
|
|
3919
|
-
}
|
|
3920
|
-
/**
|
|
3921
|
-
* Add services to plugin
|
|
3922
|
-
*/
|
|
3923
|
-
addServices(services) {
|
|
3924
|
-
this.plugin.services = [...this.plugin.services || [], ...services];
|
|
3925
|
-
return this;
|
|
3926
|
-
}
|
|
3927
|
-
/**
|
|
3928
|
-
* Add a single service to plugin
|
|
3929
|
-
*/
|
|
3930
|
-
addService(name, implementation, options) {
|
|
3931
|
-
const service = {
|
|
3932
|
-
name,
|
|
3933
|
-
implementation,
|
|
3934
|
-
...options
|
|
3935
|
-
};
|
|
3936
|
-
this.plugin.services = [...this.plugin.services || [], service];
|
|
3937
|
-
return this;
|
|
3938
|
-
}
|
|
3939
|
-
/**
|
|
3940
|
-
* Add admin pages to plugin
|
|
3941
|
-
*/
|
|
3942
|
-
addAdminPages(pages) {
|
|
3943
|
-
this.plugin.adminPages = [...this.plugin.adminPages || [], ...pages];
|
|
3944
|
-
return this;
|
|
3945
|
-
}
|
|
3946
|
-
/**
|
|
3947
|
-
* Add a single admin page to plugin
|
|
3948
|
-
*/
|
|
3949
|
-
addAdminPage(path, title, component, options) {
|
|
3950
|
-
const page = {
|
|
3951
|
-
path,
|
|
3952
|
-
title,
|
|
3953
|
-
component,
|
|
3954
|
-
...options
|
|
3955
|
-
};
|
|
3956
|
-
this.plugin.adminPages = [...this.plugin.adminPages || [], page];
|
|
3957
|
-
return this;
|
|
3958
|
-
}
|
|
3959
|
-
/**
|
|
3960
|
-
* Add admin components to plugin
|
|
3961
|
-
*/
|
|
3962
|
-
addComponents(components) {
|
|
3963
|
-
this.plugin.adminComponents = [...this.plugin.adminComponents || [], ...components];
|
|
3964
|
-
return this;
|
|
3965
|
-
}
|
|
3966
|
-
/**
|
|
3967
|
-
* Add a single admin component to plugin
|
|
3968
|
-
*/
|
|
3969
|
-
addComponent(name, template, options) {
|
|
3970
|
-
const component = {
|
|
3971
|
-
name,
|
|
3972
|
-
template,
|
|
3973
|
-
...options
|
|
3974
|
-
};
|
|
3975
|
-
this.plugin.adminComponents = [...this.plugin.adminComponents || [], component];
|
|
3976
|
-
return this;
|
|
3977
|
-
}
|
|
3978
|
-
/**
|
|
3979
|
-
* Add menu items to plugin
|
|
3980
|
-
*/
|
|
3981
|
-
addMenuItems(items) {
|
|
3982
|
-
this.plugin.menuItems = [...this.plugin.menuItems || [], ...items];
|
|
3983
|
-
return this;
|
|
3984
|
-
}
|
|
3985
|
-
/**
|
|
3986
|
-
* Add a single menu item to plugin
|
|
3987
|
-
*/
|
|
3988
|
-
addMenuItem(label, path, options) {
|
|
3989
|
-
const menuItem = {
|
|
3990
|
-
label,
|
|
3991
|
-
path,
|
|
3992
|
-
...options
|
|
3993
|
-
};
|
|
3994
|
-
this.plugin.menuItems = [...this.plugin.menuItems || [], menuItem];
|
|
3995
|
-
return this;
|
|
3996
|
-
}
|
|
3997
|
-
/**
|
|
3998
|
-
* Add hooks to plugin
|
|
3999
|
-
*/
|
|
4000
|
-
addHooks(hooks) {
|
|
4001
|
-
this.plugin.hooks = [...this.plugin.hooks || [], ...hooks];
|
|
4002
|
-
return this;
|
|
4003
|
-
}
|
|
4004
|
-
/**
|
|
4005
|
-
* Add a single hook to plugin
|
|
4006
|
-
*/
|
|
4007
|
-
addHook(name, handler, options) {
|
|
4008
|
-
const hook = {
|
|
4009
|
-
name,
|
|
4010
|
-
handler,
|
|
4011
|
-
...options
|
|
4012
|
-
};
|
|
4013
|
-
this.plugin.hooks = [...this.plugin.hooks || [], hook];
|
|
4014
|
-
return this;
|
|
4015
|
-
}
|
|
4016
|
-
/**
|
|
4017
|
-
* Add lifecycle hooks
|
|
4018
|
-
*/
|
|
4019
|
-
lifecycle(hooks) {
|
|
4020
|
-
Object.assign(this.plugin, hooks);
|
|
4021
|
-
return this;
|
|
4022
|
-
}
|
|
4023
|
-
/**
|
|
4024
|
-
* Build the plugin
|
|
4025
|
-
*/
|
|
4026
|
-
build() {
|
|
4027
|
-
if (!this.plugin.name || !this.plugin.version) {
|
|
4028
|
-
throw new Error("Plugin name and version are required");
|
|
4029
|
-
}
|
|
4030
|
-
return this.plugin;
|
|
4031
|
-
}
|
|
4032
|
-
};
|
|
4033
3968
|
|
|
4034
3969
|
// src/plugins/available/tinymce-plugin/index.ts
|
|
4035
3970
|
var builder = PluginBuilder.create({
|
|
@@ -4573,17 +4508,24 @@ function renderContentFormPage(data) {
|
|
|
4573
4508
|
const coreFieldsHTML = coreFields.sort((a, b) => a.field_order - b.field_order).map((field) => renderDynamicField(field, {
|
|
4574
4509
|
value: getFieldValue(field.field_name),
|
|
4575
4510
|
errors: data.validationErrors?.[field.field_name] || [],
|
|
4576
|
-
pluginStatuses
|
|
4511
|
+
pluginStatuses,
|
|
4512
|
+
collectionId: data.collection.id,
|
|
4513
|
+
contentId: data.id
|
|
4514
|
+
// Pass content ID when editing
|
|
4577
4515
|
}));
|
|
4578
4516
|
const contentFieldsHTML = contentFields.sort((a, b) => a.field_order - b.field_order).map((field) => renderDynamicField(field, {
|
|
4579
4517
|
value: getFieldValue(field.field_name),
|
|
4580
4518
|
errors: data.validationErrors?.[field.field_name] || [],
|
|
4581
|
-
pluginStatuses
|
|
4519
|
+
pluginStatuses,
|
|
4520
|
+
collectionId: data.collection.id,
|
|
4521
|
+
contentId: data.id
|
|
4582
4522
|
}));
|
|
4583
4523
|
const metaFieldsHTML = metaFields.sort((a, b) => a.field_order - b.field_order).map((field) => renderDynamicField(field, {
|
|
4584
4524
|
value: getFieldValue(field.field_name),
|
|
4585
4525
|
errors: data.validationErrors?.[field.field_name] || [],
|
|
4586
|
-
pluginStatuses
|
|
4526
|
+
pluginStatuses,
|
|
4527
|
+
collectionId: data.collection.id,
|
|
4528
|
+
contentId: data.id
|
|
4587
4529
|
}));
|
|
4588
4530
|
const pageContent = `
|
|
4589
4531
|
<div class="space-y-6">
|
|
@@ -6500,6 +6442,18 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
6500
6442
|
const errors = {};
|
|
6501
6443
|
for (const field of fields) {
|
|
6502
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
|
+
}
|
|
6503
6457
|
if (field.is_required && (!value || value.toString().trim() === "")) {
|
|
6504
6458
|
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6505
6459
|
continue;
|
|
@@ -6522,6 +6476,67 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
6522
6476
|
data[field.field_name] = value;
|
|
6523
6477
|
}
|
|
6524
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
|
+
}
|
|
6525
6540
|
default:
|
|
6526
6541
|
data[field.field_name] = value;
|
|
6527
6542
|
}
|
|
@@ -6646,6 +6661,18 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
6646
6661
|
const errors = {};
|
|
6647
6662
|
for (const field of fields) {
|
|
6648
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
|
+
}
|
|
6649
6676
|
if (field.is_required && (!value || value.toString().trim() === "")) {
|
|
6650
6677
|
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6651
6678
|
continue;
|
|
@@ -6668,6 +6695,67 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
6668
6695
|
data[field.field_name] = value;
|
|
6669
6696
|
}
|
|
6670
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
|
+
}
|
|
6671
6759
|
default:
|
|
6672
6760
|
data[field.field_name] = value;
|
|
6673
6761
|
}
|
|
@@ -6787,6 +6875,12 @@ adminContentRoutes.post("/preview", async (c) => {
|
|
|
6787
6875
|
const data = {};
|
|
6788
6876
|
for (const field of fields) {
|
|
6789
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
|
+
}
|
|
6790
6884
|
switch (field.field_type) {
|
|
6791
6885
|
case "number":
|
|
6792
6886
|
data[field.field_name] = value ? Number(value) : null;
|
|
@@ -13315,6 +13409,9 @@ function renderAuthSettingsForm(settings) {
|
|
|
13315
13409
|
}
|
|
13316
13410
|
|
|
13317
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
|
+
}
|
|
13318
13415
|
function renderPluginSettingsPage(data) {
|
|
13319
13416
|
const { plugin, activity = [], user } = data;
|
|
13320
13417
|
const pageContent = `
|
|
@@ -13592,6 +13689,7 @@ function renderSettingsTab(plugin) {
|
|
|
13592
13689
|
const settings = plugin.settings || {};
|
|
13593
13690
|
const isSeedDataPlugin = plugin.id === "seed-data" || plugin.name === "seed-data";
|
|
13594
13691
|
const isAuthPlugin = plugin.id === "core-auth" || plugin.name === "core-auth";
|
|
13692
|
+
const isTurnstilePlugin = plugin.id === "turnstile" || plugin.name === "turnstile";
|
|
13595
13693
|
return `
|
|
13596
13694
|
${isSeedDataPlugin ? `
|
|
13597
13695
|
<div class="backdrop-blur-md bg-black/20 rounded-xl border border-white/10 shadow-xl p-6 mb-6">
|
|
@@ -13618,12 +13716,15 @@ function renderSettingsTab(plugin) {
|
|
|
13618
13716
|
${isAuthPlugin ? `
|
|
13619
13717
|
<h2 class="text-xl font-semibold text-white mb-4">Authentication Settings</h2>
|
|
13620
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>
|
|
13621
13722
|
` : `
|
|
13622
13723
|
<h2 class="text-xl font-semibold text-white mb-4">Plugin Settings</h2>
|
|
13623
13724
|
`}
|
|
13624
13725
|
|
|
13625
13726
|
<form id="settings-form" class="space-y-6">
|
|
13626
|
-
${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)}
|
|
13627
13728
|
|
|
13628
13729
|
${Object.keys(settings).length > 0 ? `
|
|
13629
13730
|
<div class="flex items-center justify-end pt-6 border-t border-white/10">
|
|
@@ -13687,6 +13788,80 @@ function renderSettingsFields(settings) {
|
|
|
13687
13788
|
}
|
|
13688
13789
|
}).join("");
|
|
13689
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
|
+
}
|
|
13690
13865
|
function renderNoSettings(plugin) {
|
|
13691
13866
|
if (plugin.id === "seed-data" || plugin.name === "seed-data") {
|
|
13692
13867
|
return `
|
|
@@ -13918,6 +14093,19 @@ var AVAILABLE_PLUGINS = [
|
|
|
13918
14093
|
permissions: [],
|
|
13919
14094
|
dependencies: [],
|
|
13920
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
|
|
13921
14109
|
}
|
|
13922
14110
|
];
|
|
13923
14111
|
adminPluginRoutes.get("/", async (c) => {
|
|
@@ -14288,6 +14476,33 @@ adminPluginRoutes.post("/install", async (c) => {
|
|
|
14288
14476
|
});
|
|
14289
14477
|
return c.json({ success: true, plugin: easyMdxPlugin2 });
|
|
14290
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
|
+
}
|
|
14291
14506
|
return c.json({ error: "Plugin not found in registry" }, 404);
|
|
14292
14507
|
} catch (error) {
|
|
14293
14508
|
console.error("Error installing plugin:", error);
|
|
@@ -19461,16 +19676,30 @@ adminCollectionsRoutes.get("/:id", async (c) => {
|
|
|
19461
19676
|
const schema = typeof collection.schema === "string" ? JSON.parse(collection.schema) : collection.schema;
|
|
19462
19677
|
if (schema && schema.properties) {
|
|
19463
19678
|
let fieldOrder = 0;
|
|
19464
|
-
fields = Object.entries(schema.properties).map(([fieldName, fieldConfig]) =>
|
|
19465
|
-
|
|
19466
|
-
|
|
19467
|
-
|
|
19468
|
-
|
|
19469
|
-
|
|
19470
|
-
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
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
|
+
});
|
|
19474
19703
|
}
|
|
19475
19704
|
} catch (e) {
|
|
19476
19705
|
console.error("Error parsing collection schema:", e);
|
|
@@ -19685,6 +19914,9 @@ adminCollectionsRoutes.post("/:id/fields", async (c) => {
|
|
|
19685
19914
|
fieldConfig.enum = parsedOptions.options || [];
|
|
19686
19915
|
} else if (fieldType === "media") {
|
|
19687
19916
|
fieldConfig.format = "media";
|
|
19917
|
+
} else if (fieldType === "slug") {
|
|
19918
|
+
fieldConfig.type = "slug";
|
|
19919
|
+
fieldConfig.format = "slug";
|
|
19688
19920
|
} else if (fieldType === "quill") {
|
|
19689
19921
|
fieldConfig.type = "quill";
|
|
19690
19922
|
} else if (fieldType === "mdxeditor") {
|
|
@@ -21802,6 +22034,6 @@ var ROUTES_INFO = {
|
|
|
21802
22034
|
reference: "https://github.com/sonicjs/sonicjs"
|
|
21803
22035
|
};
|
|
21804
22036
|
|
|
21805
|
-
export {
|
|
21806
|
-
//# sourceMappingURL=chunk-
|
|
21807
|
-
//# 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
|