@sonicjs-cms/core 2.0.10 → 2.1.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-22EFGHAX.cjs +801 -0
- package/dist/chunk-22EFGHAX.cjs.map +1 -0
- package/dist/{chunk-3PHG75W4.cjs → chunk-2MBNRFS5.cjs} +7 -5
- package/dist/chunk-2MBNRFS5.cjs.map +1 -0
- package/dist/{chunk-LW33AOBF.js → chunk-5RKQB2JG.js} +6 -222
- package/dist/chunk-5RKQB2JG.js.map +1 -0
- package/dist/{chunk-HXA5QSI3.cjs → chunk-7EGKU7OO.cjs} +22 -6
- package/dist/chunk-7EGKU7OO.cjs.map +1 -0
- package/dist/{chunk-6FR25MPC.js → chunk-F4K5QAN6.js} +246 -3
- package/dist/chunk-F4K5QAN6.js.map +1 -0
- package/dist/{chunk-MXJJN4IA.js → chunk-K5JZ4JI3.js} +7 -5
- package/dist/chunk-K5JZ4JI3.js.map +1 -0
- package/dist/{chunk-Z4H6DBVF.js → chunk-KZ5XDGE6.js} +11991 -10731
- package/dist/chunk-KZ5XDGE6.js.map +1 -0
- package/dist/{chunk-Q7SL7U43.cjs → chunk-LPM3NPAX.cjs} +12001 -10740
- package/dist/chunk-LPM3NPAX.cjs.map +1 -0
- package/dist/chunk-LWMMMW43.js +787 -0
- package/dist/chunk-LWMMMW43.js.map +1 -0
- package/dist/chunk-NMVOTNSL.js +61 -0
- package/dist/chunk-NMVOTNSL.js.map +1 -0
- package/dist/{chunk-FTMKKKNH.js → chunk-QNWYQZ55.js} +3 -3
- package/dist/{chunk-FTMKKKNH.js.map → chunk-QNWYQZ55.js.map} +1 -1
- package/dist/{chunk-COBUPOMD.js → chunk-T7IYBGGO.cjs} +5 -770
- package/dist/chunk-T7IYBGGO.cjs.map +1 -0
- package/dist/{chunk-YHG45LMU.js → chunk-UJ4K4B23.js} +20 -4
- package/dist/chunk-UJ4K4B23.js.map +1 -0
- package/dist/chunk-WBX5YMTB.cjs +70 -0
- package/dist/chunk-WBX5YMTB.cjs.map +1 -0
- package/dist/{chunk-DOR2IU73.cjs → chunk-YP52USGX.cjs} +249 -2
- package/dist/chunk-YP52USGX.cjs.map +1 -0
- package/dist/{chunk-MU3MR2QR.cjs → chunk-YU6QFFI4.cjs} +5 -222
- package/dist/chunk-YU6QFFI4.cjs.map +1 -0
- package/dist/{chunk-CAP6QQR2.cjs → chunk-ZMSYKV62.cjs} +5 -5
- package/dist/{chunk-CAP6QQR2.cjs.map → chunk-ZMSYKV62.cjs.map} +1 -1
- package/dist/{chunk-NBDPIRQS.cjs → chunk-ZPMFT2JW.js} +4 -786
- package/dist/chunk-ZPMFT2JW.js.map +1 -0
- package/dist/index.cjs +520 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +389 -12
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +24 -23
- package/dist/middleware.js +3 -2
- package/dist/migrations-IHERIQVD.js +4 -0
- package/dist/migrations-IHERIQVD.js.map +1 -0
- package/dist/migrations-POFD5KNG.cjs +13 -0
- package/dist/migrations-POFD5KNG.cjs.map +1 -0
- package/dist/routes.cjs +31 -29
- package/dist/routes.js +8 -6
- package/dist/services.cjs +42 -24
- package/dist/services.js +4 -2
- package/dist/templates.cjs +17 -21
- package/dist/templates.js +2 -2
- package/dist/utils.cjs +44 -11
- package/dist/utils.js +2 -1
- package/migrations/001_initial_schema.sql +2 -2
- package/migrations/007_demo_login_plugin.sql +1 -1
- package/migrations/020_add_email_plugin.sql +22 -0
- package/migrations/021_add_magic_link_auth_plugin.sql +42 -0
- package/migrations/021_add_otp_login.sql +42 -0
- package/migrations/022_add_tinymce_plugin.sql +25 -0
- package/migrations/023_add_easy_mdx_plugin.sql +25 -0
- package/migrations/024_add_quill_editor_plugin.sql +25 -0
- package/migrations/025_rename_mdxeditor_to_easy_mdx.sql +22 -0
- package/package.json +5 -3
- package/dist/chunk-3PHG75W4.cjs.map +0 -1
- package/dist/chunk-6FR25MPC.js.map +0 -1
- package/dist/chunk-COBUPOMD.js.map +0 -1
- package/dist/chunk-DOR2IU73.cjs.map +0 -1
- package/dist/chunk-HXA5QSI3.cjs.map +0 -1
- package/dist/chunk-LW33AOBF.js.map +0 -1
- package/dist/chunk-MU3MR2QR.cjs.map +0 -1
- package/dist/chunk-MXJJN4IA.js.map +0 -1
- package/dist/chunk-NBDPIRQS.cjs.map +0 -1
- package/dist/chunk-Q7SL7U43.cjs.map +0 -1
- package/dist/chunk-YHG45LMU.js.map +0 -1
- package/dist/chunk-Z4H6DBVF.js.map +0 -1
- package/migrations/002_faq_plugin.sql +0 -86
package/dist/index.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import { api_default, api_media_default, api_system_default, admin_api_default, router, adminCollectionsRoutes, adminSettingsRoutes, admin_content_default, adminMediaRoutes, adminPluginRoutes, adminLogsRoutes, userRoutes, auth_default } from './chunk-
|
|
2
|
-
export { ROUTES_INFO, admin_api_default as adminApiRoutes, adminCheckboxRoutes, admin_code_examples_default as adminCodeExamplesRoutes, adminCollectionsRoutes, admin_content_default as adminContentRoutes, router as adminDashboardRoutes, adminDesignRoutes,
|
|
3
|
-
import { schema_exports } from './chunk-
|
|
4
|
-
export { Logger, apiTokens, collections, content, contentVersions, getLogger, initLogger, insertCollectionSchema, insertContentSchema, insertLogConfigSchema, insertMediaSchema, insertPluginActivityLogSchema, insertPluginAssetSchema, insertPluginHookSchema, insertPluginRouteSchema, insertPluginSchema, insertSystemLogSchema, insertUserSchema, insertWorkflowHistorySchema, logConfig, media, pluginActivityLog, pluginAssets, pluginHooks, pluginRoutes, plugins, selectCollectionSchema, selectContentSchema, selectLogConfigSchema, selectMediaSchema, selectPluginActivityLogSchema, selectPluginAssetSchema, selectPluginHookSchema, selectPluginRouteSchema, selectPluginSchema, selectSystemLogSchema, selectUserSchema, selectWorkflowHistorySchema, systemLogs, users, workflowHistory } from './chunk-
|
|
5
|
-
import { metricsMiddleware, bootstrapMiddleware, requireAuth } from './chunk-
|
|
6
|
-
export { AuthManager, PermissionManager, bootstrapMiddleware, cacheHeaders, compressionMiddleware, detailedLoggingMiddleware, getActivePlugins, isPluginActive, logActivity, loggingMiddleware, optionalAuth, performanceLoggingMiddleware, requireActivePlugin, requireActivePlugins, requireAnyPermission, requireAuth, requirePermission, requireRole, securityHeaders, securityLoggingMiddleware } from './chunk-
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { PluginBuilder, api_default, api_media_default, api_system_default, admin_api_default, router, adminCollectionsRoutes, adminSettingsRoutes, admin_content_default, adminMediaRoutes, adminPluginRoutes, adminLogsRoutes, userRoutes, auth_default, test_cleanup_default } from './chunk-KZ5XDGE6.js';
|
|
2
|
+
export { ROUTES_INFO, admin_api_default as adminApiRoutes, adminCheckboxRoutes, admin_code_examples_default as adminCodeExamplesRoutes, adminCollectionsRoutes, admin_content_default as adminContentRoutes, router as adminDashboardRoutes, adminDesignRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, admin_testimonials_default as adminTestimonialsRoutes, userRoutes as adminUsersRoutes, api_content_crud_default as apiContentCrudRoutes, api_media_default as apiMediaRoutes, api_default as apiRoutes, api_system_default as apiSystemRoutes, auth_default as authRoutes } from './chunk-KZ5XDGE6.js';
|
|
3
|
+
import { schema_exports } from './chunk-F4K5QAN6.js';
|
|
4
|
+
export { Logger, apiTokens, collections, content, contentVersions, getLogger, initLogger, insertCollectionSchema, insertContentSchema, insertLogConfigSchema, insertMediaSchema, insertPluginActivityLogSchema, insertPluginAssetSchema, insertPluginHookSchema, insertPluginRouteSchema, insertPluginSchema, insertSystemLogSchema, insertUserSchema, insertWorkflowHistorySchema, logConfig, media, pluginActivityLog, pluginAssets, pluginHooks, pluginRoutes, plugins, selectCollectionSchema, selectContentSchema, selectLogConfigSchema, selectMediaSchema, selectPluginActivityLogSchema, selectPluginAssetSchema, selectPluginHookSchema, selectPluginRouteSchema, selectPluginSchema, selectSystemLogSchema, selectUserSchema, selectWorkflowHistorySchema, systemLogs, users, workflowHistory } from './chunk-F4K5QAN6.js';
|
|
5
|
+
import { metricsMiddleware, bootstrapMiddleware, requireAuth } from './chunk-UJ4K4B23.js';
|
|
6
|
+
export { AuthManager, PermissionManager, bootstrapMiddleware, cacheHeaders, compressionMiddleware, detailedLoggingMiddleware, getActivePlugins, isPluginActive, logActivity, loggingMiddleware, optionalAuth, performanceLoggingMiddleware, requireActivePlugin, requireActivePlugins, requireAnyPermission, requireAuth, requirePermission, requireRole, securityHeaders, securityLoggingMiddleware } from './chunk-UJ4K4B23.js';
|
|
7
|
+
export { PluginBootstrapService, PluginService as PluginServiceClass, cleanupRemovedCollections, fullCollectionSync, getAvailableCollectionNames, getManagedCollections, isCollectionManaged, loadCollectionConfig, loadCollectionConfigs, registerCollections, syncCollection, syncCollections, validateCollectionConfig } from './chunk-LWMMMW43.js';
|
|
8
|
+
export { MigrationService } from './chunk-ZPMFT2JW.js';
|
|
9
|
+
export { renderFilterBar } from './chunk-QNWYQZ55.js';
|
|
10
|
+
import { init_admin_layout_catalyst_template, renderAdminLayout, renderAdminLayoutCatalyst } from './chunk-5RKQB2JG.js';
|
|
11
|
+
export { getConfirmationDialogScript, renderAlert, renderConfirmationDialog, renderForm, renderFormField, renderPagination, renderTable } from './chunk-5RKQB2JG.js';
|
|
11
12
|
export { HookSystemImpl, HookUtils, PluginManager as PluginManagerClass, PluginRegistryImpl, PluginValidator as PluginValidatorClass, ScopedHookSystem as ScopedHookSystemClass } from './chunk-HKEK7UNV.js';
|
|
12
|
-
import { package_default, getCoreVersion } from './chunk-
|
|
13
|
-
export { QueryFilterBuilder, SONICJS_VERSION, TemplateRenderer, buildQuery, escapeHtml, getCoreVersion, renderTemplate, sanitizeInput, sanitizeObject, templateRenderer } from './chunk-
|
|
13
|
+
import { package_default, getCoreVersion } from './chunk-K5JZ4JI3.js';
|
|
14
|
+
export { QueryFilterBuilder, SONICJS_VERSION, TemplateRenderer, buildQuery, escapeHtml, getCoreVersion, renderTemplate, sanitizeInput, sanitizeObject, templateRenderer } from './chunk-K5JZ4JI3.js';
|
|
15
|
+
import './chunk-NMVOTNSL.js';
|
|
14
16
|
export { metricsTracker } from './chunk-FICTAGD4.js';
|
|
15
17
|
export { HOOKS } from './chunk-LOUJRBXV.js';
|
|
16
18
|
import './chunk-V4OQ3NZ2.js';
|
|
17
19
|
import { Hono } from 'hono';
|
|
20
|
+
import { html } from 'hono/html';
|
|
18
21
|
import { drizzle } from 'drizzle-orm/d1';
|
|
19
22
|
|
|
20
23
|
// src/plugins/core-plugins/database-tools-plugin/services/database-service.ts
|
|
@@ -736,6 +739,374 @@ function createDatabaseToolsAdminRoutes() {
|
|
|
736
739
|
});
|
|
737
740
|
return router2;
|
|
738
741
|
}
|
|
742
|
+
function createEmailPlugin() {
|
|
743
|
+
const builder = PluginBuilder.create({
|
|
744
|
+
name: "email",
|
|
745
|
+
version: "1.0.0-beta.1",
|
|
746
|
+
description: "Send transactional emails using Resend"
|
|
747
|
+
});
|
|
748
|
+
builder.metadata({
|
|
749
|
+
author: {
|
|
750
|
+
name: "SonicJS Team",
|
|
751
|
+
email: "team@sonicjs.com"
|
|
752
|
+
},
|
|
753
|
+
license: "MIT",
|
|
754
|
+
compatibility: "^2.0.0"
|
|
755
|
+
});
|
|
756
|
+
const emailRoutes = new Hono();
|
|
757
|
+
emailRoutes.get("/settings", async (c) => {
|
|
758
|
+
const user = c.get("user");
|
|
759
|
+
const db = c.env.DB;
|
|
760
|
+
const plugin = await db.prepare(`
|
|
761
|
+
SELECT settings FROM plugins WHERE id = 'email'
|
|
762
|
+
`).first();
|
|
763
|
+
const settings = plugin?.settings ? JSON.parse(plugin.settings) : {};
|
|
764
|
+
const contentHTML = html`
|
|
765
|
+
<div class="p-8">
|
|
766
|
+
<!-- Header -->
|
|
767
|
+
<div class="mb-8">
|
|
768
|
+
<h1 class="text-3xl font-bold text-zinc-950 dark:text-white mb-2">Email Settings</h1>
|
|
769
|
+
<p class="text-zinc-600 dark:text-zinc-400">Configure Resend API for sending transactional emails</p>
|
|
770
|
+
</div>
|
|
771
|
+
|
|
772
|
+
<!-- Settings Form -->
|
|
773
|
+
<div class="max-w-3xl">
|
|
774
|
+
<!-- Main Settings Card -->
|
|
775
|
+
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 p-6 mb-6">
|
|
776
|
+
<h2 class="text-xl font-semibold text-zinc-950 dark:text-white mb-4">Resend Configuration</h2>
|
|
777
|
+
|
|
778
|
+
<form id="emailSettingsForm" class="space-y-6">
|
|
779
|
+
<!-- API Key -->
|
|
780
|
+
<div>
|
|
781
|
+
<label for="apiKey" class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">
|
|
782
|
+
Resend API Key <span class="text-red-500">*</span>
|
|
783
|
+
</label>
|
|
784
|
+
<input
|
|
785
|
+
type="password"
|
|
786
|
+
id="apiKey"
|
|
787
|
+
name="apiKey"
|
|
788
|
+
value="${settings.apiKey || ""}"
|
|
789
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-500 dark:placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-indigo-500 dark:focus:ring-indigo-400"
|
|
790
|
+
placeholder="re_..."
|
|
791
|
+
required
|
|
792
|
+
/>
|
|
793
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">
|
|
794
|
+
Get your API key from <a href="https://resend.com/api-keys" target="_blank" class="text-indigo-600 dark:text-indigo-400 hover:underline">resend.com/api-keys</a>
|
|
795
|
+
</p>
|
|
796
|
+
</div>
|
|
797
|
+
|
|
798
|
+
<!-- From Email -->
|
|
799
|
+
<div>
|
|
800
|
+
<label for="fromEmail" class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">
|
|
801
|
+
From Email <span class="text-red-500">*</span>
|
|
802
|
+
</label>
|
|
803
|
+
<input
|
|
804
|
+
type="email"
|
|
805
|
+
id="fromEmail"
|
|
806
|
+
name="fromEmail"
|
|
807
|
+
value="${settings.fromEmail || ""}"
|
|
808
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-500 dark:placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-indigo-500 dark:focus:ring-indigo-400"
|
|
809
|
+
placeholder="noreply@yourdomain.com"
|
|
810
|
+
required
|
|
811
|
+
/>
|
|
812
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">
|
|
813
|
+
Must be a verified domain in Resend
|
|
814
|
+
</p>
|
|
815
|
+
</div>
|
|
816
|
+
|
|
817
|
+
<!-- From Name -->
|
|
818
|
+
<div>
|
|
819
|
+
<label for="fromName" class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">
|
|
820
|
+
From Name <span class="text-red-500">*</span>
|
|
821
|
+
</label>
|
|
822
|
+
<input
|
|
823
|
+
type="text"
|
|
824
|
+
id="fromName"
|
|
825
|
+
name="fromName"
|
|
826
|
+
value="${settings.fromName || ""}"
|
|
827
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-500 dark:placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-indigo-500 dark:focus:ring-indigo-400"
|
|
828
|
+
placeholder="Your App Name"
|
|
829
|
+
required
|
|
830
|
+
/>
|
|
831
|
+
</div>
|
|
832
|
+
|
|
833
|
+
<!-- Reply To -->
|
|
834
|
+
<div>
|
|
835
|
+
<label for="replyTo" class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">
|
|
836
|
+
Reply-To Email
|
|
837
|
+
</label>
|
|
838
|
+
<input
|
|
839
|
+
type="email"
|
|
840
|
+
id="replyTo"
|
|
841
|
+
name="replyTo"
|
|
842
|
+
value="${settings.replyTo || ""}"
|
|
843
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-500 dark:placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-indigo-500 dark:focus:ring-indigo-400"
|
|
844
|
+
placeholder="support@yourdomain.com"
|
|
845
|
+
/>
|
|
846
|
+
</div>
|
|
847
|
+
|
|
848
|
+
<!-- Logo URL -->
|
|
849
|
+
<div>
|
|
850
|
+
<label for="logoUrl" class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">
|
|
851
|
+
Logo URL
|
|
852
|
+
</label>
|
|
853
|
+
<input
|
|
854
|
+
type="url"
|
|
855
|
+
id="logoUrl"
|
|
856
|
+
name="logoUrl"
|
|
857
|
+
value="${settings.logoUrl || ""}"
|
|
858
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-500 dark:placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-indigo-500 dark:focus:ring-indigo-400"
|
|
859
|
+
placeholder="https://yourdomain.com/logo.png"
|
|
860
|
+
/>
|
|
861
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">
|
|
862
|
+
Logo to display in email templates
|
|
863
|
+
</p>
|
|
864
|
+
</div>
|
|
865
|
+
|
|
866
|
+
<!-- Action Buttons -->
|
|
867
|
+
<div class="flex gap-3 pt-4">
|
|
868
|
+
<button
|
|
869
|
+
type="submit"
|
|
870
|
+
class="inline-flex items-center justify-center rounded-lg bg-zinc-950 dark:bg-white px-3.5 py-2.5 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 transition-colors shadow-sm"
|
|
871
|
+
>
|
|
872
|
+
Save Settings
|
|
873
|
+
</button>
|
|
874
|
+
<button
|
|
875
|
+
type="button"
|
|
876
|
+
id="testEmailBtn"
|
|
877
|
+
class="inline-flex items-center justify-center rounded-lg bg-white dark:bg-zinc-800 px-3.5 py-2.5 text-sm font-semibold text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors shadow-sm"
|
|
878
|
+
>
|
|
879
|
+
Send Test Email
|
|
880
|
+
</button>
|
|
881
|
+
<button
|
|
882
|
+
type="button"
|
|
883
|
+
id="resetBtn"
|
|
884
|
+
class="inline-flex items-center justify-center rounded-lg bg-white dark:bg-zinc-800 px-3.5 py-2.5 text-sm font-semibold text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors shadow-sm"
|
|
885
|
+
>
|
|
886
|
+
Reset
|
|
887
|
+
</button>
|
|
888
|
+
</div>
|
|
889
|
+
</form>
|
|
890
|
+
</div>
|
|
891
|
+
|
|
892
|
+
<!-- Status Message -->
|
|
893
|
+
<div id="statusMessage" class="hidden rounded-xl p-4 mb-6"></div>
|
|
894
|
+
|
|
895
|
+
<!-- Info Card -->
|
|
896
|
+
<div class="rounded-xl bg-indigo-50 dark:bg-indigo-950/30 ring-1 ring-indigo-100 dark:ring-indigo-900/50 p-6">
|
|
897
|
+
<h3 class="font-semibold text-indigo-900 dark:text-indigo-300 mb-3">
|
|
898
|
+
📧 Email Templates Included
|
|
899
|
+
</h3>
|
|
900
|
+
<ul class="text-sm text-indigo-800 dark:text-indigo-200 space-y-2">
|
|
901
|
+
<li>✓ Registration confirmation</li>
|
|
902
|
+
<li>✓ Email verification</li>
|
|
903
|
+
<li>✓ Password reset</li>
|
|
904
|
+
<li>✓ One-time code (2FA)</li>
|
|
905
|
+
</ul>
|
|
906
|
+
<p class="text-xs text-indigo-700 dark:text-indigo-300 mt-4">
|
|
907
|
+
Templates are code-based and can be customized by editing the plugin files.
|
|
908
|
+
</p>
|
|
909
|
+
</div>
|
|
910
|
+
</div>
|
|
911
|
+
</div>
|
|
912
|
+
|
|
913
|
+
<script>
|
|
914
|
+
// Form submission handler
|
|
915
|
+
document.getElementById('emailSettingsForm').addEventListener('submit', async (e) => {
|
|
916
|
+
e.preventDefault()
|
|
917
|
+
const formData = new FormData(e.target)
|
|
918
|
+
const data = Object.fromEntries(formData.entries())
|
|
919
|
+
|
|
920
|
+
const statusEl = document.getElementById('statusMessage')
|
|
921
|
+
|
|
922
|
+
try {
|
|
923
|
+
const response = await fetch('/admin/plugins/email/settings', {
|
|
924
|
+
method: 'POST',
|
|
925
|
+
headers: { 'Content-Type': 'application/json' },
|
|
926
|
+
body: JSON.stringify(data)
|
|
927
|
+
})
|
|
928
|
+
|
|
929
|
+
if (response.ok) {
|
|
930
|
+
statusEl.className = 'rounded-xl bg-green-50 dark:bg-green-950/30 ring-1 ring-green-100 dark:ring-green-900/50 p-4 mb-6 text-green-900 dark:text-green-200'
|
|
931
|
+
statusEl.innerHTML = '✅ Settings saved successfully!'
|
|
932
|
+
statusEl.classList.remove('hidden')
|
|
933
|
+
setTimeout(() => statusEl.classList.add('hidden'), 3000)
|
|
934
|
+
} else {
|
|
935
|
+
throw new Error('Failed to save settings')
|
|
936
|
+
}
|
|
937
|
+
} catch (error) {
|
|
938
|
+
statusEl.className = 'rounded-xl bg-red-50 dark:bg-red-950/30 ring-1 ring-red-100 dark:ring-red-900/50 p-4 mb-6 text-red-900 dark:text-red-200'
|
|
939
|
+
statusEl.innerHTML = '❌ Failed to save settings. Please try again.'
|
|
940
|
+
statusEl.classList.remove('hidden')
|
|
941
|
+
}
|
|
942
|
+
})
|
|
943
|
+
|
|
944
|
+
// Test email handler
|
|
945
|
+
document.getElementById('testEmailBtn').addEventListener('click', async () => {
|
|
946
|
+
// Prompt for destination email
|
|
947
|
+
const toEmail = prompt('Enter destination email address for test:')
|
|
948
|
+
if (!toEmail) return
|
|
949
|
+
|
|
950
|
+
// Basic email validation
|
|
951
|
+
if (!toEmail.match(/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/)) {
|
|
952
|
+
alert('Please enter a valid email address')
|
|
953
|
+
return
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
const statusEl = document.getElementById('statusMessage')
|
|
957
|
+
|
|
958
|
+
statusEl.className = 'rounded-xl bg-indigo-50 dark:bg-indigo-950/30 ring-1 ring-indigo-100 dark:ring-indigo-900/50 p-4 mb-6 text-indigo-900 dark:text-indigo-200'
|
|
959
|
+
statusEl.innerHTML = \`📧 Sending test email to \${toEmail}...\`
|
|
960
|
+
statusEl.classList.remove('hidden')
|
|
961
|
+
|
|
962
|
+
try {
|
|
963
|
+
const response = await fetch('/admin/plugins/email/test', {
|
|
964
|
+
method: 'POST',
|
|
965
|
+
headers: { 'Content-Type': 'application/json' },
|
|
966
|
+
body: JSON.stringify({ toEmail })
|
|
967
|
+
})
|
|
968
|
+
|
|
969
|
+
const data = await response.json()
|
|
970
|
+
|
|
971
|
+
if (response.ok) {
|
|
972
|
+
statusEl.className = 'rounded-xl bg-green-50 dark:bg-green-950/30 ring-1 ring-green-100 dark:ring-green-900/50 p-4 mb-6 text-green-900 dark:text-green-200'
|
|
973
|
+
statusEl.innerHTML = \`✅ \${data.message || 'Test email sent! Check your inbox.'}\`
|
|
974
|
+
} else {
|
|
975
|
+
statusEl.className = 'rounded-xl bg-red-50 dark:bg-red-950/30 ring-1 ring-red-100 dark:ring-red-900/50 p-4 mb-6 text-red-900 dark:text-red-200'
|
|
976
|
+
statusEl.innerHTML = \`❌ \${data.error || 'Failed to send test email. Check your settings.'}\`
|
|
977
|
+
}
|
|
978
|
+
} catch (error) {
|
|
979
|
+
statusEl.className = 'rounded-xl bg-red-50 dark:bg-red-950/30 ring-1 ring-red-100 dark:ring-red-900/50 p-4 mb-6 text-red-900 dark:text-red-200'
|
|
980
|
+
statusEl.innerHTML = '❌ Network error. Please try again.'
|
|
981
|
+
}
|
|
982
|
+
})
|
|
983
|
+
|
|
984
|
+
// Reset button handler
|
|
985
|
+
document.getElementById('resetBtn').addEventListener('click', () => {
|
|
986
|
+
document.getElementById('emailSettingsForm').reset()
|
|
987
|
+
})
|
|
988
|
+
</script>
|
|
989
|
+
`;
|
|
990
|
+
return c.html(
|
|
991
|
+
renderAdminLayout({
|
|
992
|
+
title: "Email Settings",
|
|
993
|
+
content: contentHTML,
|
|
994
|
+
user,
|
|
995
|
+
currentPath: "/admin/plugins/email/settings"
|
|
996
|
+
})
|
|
997
|
+
);
|
|
998
|
+
});
|
|
999
|
+
emailRoutes.post("/settings", async (c) => {
|
|
1000
|
+
try {
|
|
1001
|
+
const body = await c.req.json();
|
|
1002
|
+
const db = c.env.DB;
|
|
1003
|
+
await db.prepare(`
|
|
1004
|
+
UPDATE plugins
|
|
1005
|
+
SET settings = ?,
|
|
1006
|
+
updated_at = unixepoch()
|
|
1007
|
+
WHERE id = 'email'
|
|
1008
|
+
`).bind(JSON.stringify(body)).run();
|
|
1009
|
+
return c.json({ success: true });
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
console.error("Error saving email settings:", error);
|
|
1012
|
+
return c.json({ success: false, error: "Failed to save settings" }, 500);
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
emailRoutes.post("/test", async (c) => {
|
|
1016
|
+
try {
|
|
1017
|
+
const db = c.env.DB;
|
|
1018
|
+
const body = await c.req.json();
|
|
1019
|
+
const plugin = await db.prepare(`
|
|
1020
|
+
SELECT settings FROM plugins WHERE id = 'email'
|
|
1021
|
+
`).first();
|
|
1022
|
+
if (!plugin?.settings) {
|
|
1023
|
+
return c.json({
|
|
1024
|
+
success: false,
|
|
1025
|
+
error: "Email settings not configured. Please save your settings first."
|
|
1026
|
+
}, 400);
|
|
1027
|
+
}
|
|
1028
|
+
const settings = JSON.parse(plugin.settings);
|
|
1029
|
+
if (!settings.apiKey || !settings.fromEmail || !settings.fromName) {
|
|
1030
|
+
return c.json({
|
|
1031
|
+
success: false,
|
|
1032
|
+
error: "Missing required settings. Please configure API Key, From Email, and From Name."
|
|
1033
|
+
}, 400);
|
|
1034
|
+
}
|
|
1035
|
+
const toEmail = body.toEmail || settings.fromEmail;
|
|
1036
|
+
if (!toEmail.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
|
|
1037
|
+
return c.json({
|
|
1038
|
+
success: false,
|
|
1039
|
+
error: "Invalid email address format"
|
|
1040
|
+
}, 400);
|
|
1041
|
+
}
|
|
1042
|
+
const response = await fetch("https://api.resend.com/emails", {
|
|
1043
|
+
method: "POST",
|
|
1044
|
+
headers: {
|
|
1045
|
+
"Authorization": `Bearer ${settings.apiKey}`,
|
|
1046
|
+
"Content-Type": "application/json"
|
|
1047
|
+
},
|
|
1048
|
+
body: JSON.stringify({
|
|
1049
|
+
from: `${settings.fromName} <${settings.fromEmail}>`,
|
|
1050
|
+
to: [toEmail],
|
|
1051
|
+
subject: "Test Email from SonicJS",
|
|
1052
|
+
html: `
|
|
1053
|
+
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
|
1054
|
+
<h1 style="color: #667eea;">Test Email Successful! \u{1F389}</h1>
|
|
1055
|
+
<p>This is a test email from your SonicJS Email plugin.</p>
|
|
1056
|
+
<p><strong>Configuration:</strong></p>
|
|
1057
|
+
<ul>
|
|
1058
|
+
<li>From: ${settings.fromName} <${settings.fromEmail}></li>
|
|
1059
|
+
<li>Reply-To: ${settings.replyTo || "Not set"}</li>
|
|
1060
|
+
<li>Sent at: ${(/* @__PURE__ */ new Date()).toISOString()}</li>
|
|
1061
|
+
</ul>
|
|
1062
|
+
<p>Your email settings are working correctly!</p>
|
|
1063
|
+
</div>
|
|
1064
|
+
`,
|
|
1065
|
+
reply_to: settings.replyTo || settings.fromEmail
|
|
1066
|
+
})
|
|
1067
|
+
});
|
|
1068
|
+
const data = await response.json();
|
|
1069
|
+
if (!response.ok) {
|
|
1070
|
+
console.error("Resend API error:", data);
|
|
1071
|
+
return c.json({
|
|
1072
|
+
success: false,
|
|
1073
|
+
error: data.message || "Failed to send test email. Check your API key and domain verification."
|
|
1074
|
+
}, response.status);
|
|
1075
|
+
}
|
|
1076
|
+
return c.json({
|
|
1077
|
+
success: true,
|
|
1078
|
+
message: `Test email sent successfully to ${toEmail}`,
|
|
1079
|
+
emailId: data.id
|
|
1080
|
+
});
|
|
1081
|
+
} catch (error) {
|
|
1082
|
+
console.error("Test email error:", error);
|
|
1083
|
+
return c.json({
|
|
1084
|
+
success: false,
|
|
1085
|
+
error: error.message || "An error occurred while sending test email"
|
|
1086
|
+
}, 500);
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
builder.addRoute("/admin/plugins/email", emailRoutes, {
|
|
1090
|
+
description: "Email plugin settings",
|
|
1091
|
+
requiresAuth: true,
|
|
1092
|
+
priority: 80
|
|
1093
|
+
});
|
|
1094
|
+
builder.addMenuItem("Email", "/admin/plugins/email/settings", {
|
|
1095
|
+
icon: "envelope",
|
|
1096
|
+
order: 80,
|
|
1097
|
+
permissions: ["email:manage"]
|
|
1098
|
+
});
|
|
1099
|
+
builder.lifecycle({
|
|
1100
|
+
activate: async () => {
|
|
1101
|
+
console.info("\u2705 Email plugin activated");
|
|
1102
|
+
},
|
|
1103
|
+
deactivate: async () => {
|
|
1104
|
+
console.info("\u274C Email plugin deactivated");
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
return builder.build();
|
|
1108
|
+
}
|
|
1109
|
+
var emailPlugin = createEmailPlugin();
|
|
739
1110
|
|
|
740
1111
|
// src/app.ts
|
|
741
1112
|
function createSonicJSApp(config = {}) {
|
|
@@ -778,6 +1149,12 @@ function createSonicJSApp(config = {}) {
|
|
|
778
1149
|
app.route("/admin/logs", adminLogsRoutes);
|
|
779
1150
|
app.route("/admin", userRoutes);
|
|
780
1151
|
app.route("/auth", auth_default);
|
|
1152
|
+
app.route("/", test_cleanup_default);
|
|
1153
|
+
if (emailPlugin.routes && emailPlugin.routes.length > 0) {
|
|
1154
|
+
for (const route of emailPlugin.routes) {
|
|
1155
|
+
app.route(route.path, route.handler);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
781
1158
|
app.get("/files/*", async (c) => {
|
|
782
1159
|
try {
|
|
783
1160
|
const url = new URL(c.req.url);
|