@sonicjs-cms/core 2.3.16 → 2.4.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.
Files changed (37) hide show
  1. package/dist/{chunk-4CLJPURJ.cjs → chunk-2MI3LZFH.cjs} +2 -2
  2. package/dist/{chunk-4CLJPURJ.cjs.map → chunk-2MI3LZFH.cjs.map} +1 -1
  3. package/dist/{chunk-AE27GPMY.cjs → chunk-7I5INVNR.cjs} +4 -4
  4. package/dist/{chunk-AE27GPMY.cjs.map → chunk-7I5INVNR.cjs.map} +1 -1
  5. package/dist/{chunk-ICDJ7KPP.cjs → chunk-A4SVOGG6.cjs} +143 -96
  6. package/dist/chunk-A4SVOGG6.cjs.map +1 -0
  7. package/dist/{chunk-33Q6ETMQ.js → chunk-D2NLCPO2.js} +2 -2
  8. package/dist/{chunk-33Q6ETMQ.js.map → chunk-D2NLCPO2.js.map} +1 -1
  9. package/dist/{chunk-ATNWDVAD.js → chunk-DXM575E2.js} +3 -3
  10. package/dist/{chunk-ATNWDVAD.js.map → chunk-DXM575E2.js.map} +1 -1
  11. package/dist/{chunk-QUBMTYRI.js → chunk-FT6NBHNX.js} +61 -15
  12. package/dist/chunk-FT6NBHNX.js.map +1 -0
  13. package/dist/{chunk-EUQDJQHJ.cjs → chunk-FYEDK7K7.cjs} +3 -3
  14. package/dist/{chunk-EUQDJQHJ.cjs.map → chunk-FYEDK7K7.cjs.map} +1 -1
  15. package/dist/{chunk-LRFDZHLM.js → chunk-VNCYCH3H.js} +3 -3
  16. package/dist/{chunk-LRFDZHLM.js.map → chunk-VNCYCH3H.js.map} +1 -1
  17. package/dist/index.cjs +672 -78
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.js +601 -7
  20. package/dist/index.js.map +1 -1
  21. package/dist/middleware.cjs +23 -23
  22. package/dist/middleware.js +2 -2
  23. package/dist/migrations-32QAYLWJ.cjs +13 -0
  24. package/dist/{migrations-QCBN4TLD.cjs.map → migrations-32QAYLWJ.cjs.map} +1 -1
  25. package/dist/migrations-57ZHBQ4X.js +4 -0
  26. package/dist/{migrations-7TWFJVNT.js.map → migrations-57ZHBQ4X.js.map} +1 -1
  27. package/dist/routes.cjs +24 -24
  28. package/dist/routes.js +4 -4
  29. package/dist/services.cjs +2 -2
  30. package/dist/services.js +1 -1
  31. package/dist/utils.cjs +11 -11
  32. package/dist/utils.js +1 -1
  33. package/package.json +1 -1
  34. package/dist/chunk-ICDJ7KPP.cjs.map +0 -1
  35. package/dist/chunk-QUBMTYRI.js.map +0 -1
  36. package/dist/migrations-7TWFJVNT.js +0 -4
  37. package/dist/migrations-QCBN4TLD.cjs +0 -13
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
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-QUBMTYRI.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-QUBMTYRI.js';
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, checkAdminUserExists } from './chunk-FT6NBHNX.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-FT6NBHNX.js';
3
3
  import { schema_exports } from './chunk-3YNNVSMC.js';
4
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-3YNNVSMC.js';
5
- import { AuthManager, metricsMiddleware, bootstrapMiddleware, requireAuth } from './chunk-ATNWDVAD.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-ATNWDVAD.js';
5
+ import { AuthManager, metricsMiddleware, bootstrapMiddleware, requireAuth } from './chunk-DXM575E2.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-DXM575E2.js';
7
7
  export { PluginBootstrapService, PluginService as PluginServiceClass, cleanupRemovedCollections, fullCollectionSync, getAvailableCollectionNames, getManagedCollections, isCollectionManaged, loadCollectionConfig, loadCollectionConfigs, registerCollections, syncCollection, syncCollections, validateCollectionConfig } from './chunk-SGAG6FD3.js';
8
- export { MigrationService } from './chunk-33Q6ETMQ.js';
8
+ export { MigrationService } from './chunk-D2NLCPO2.js';
9
9
  export { renderFilterBar } from './chunk-AVPUX57O.js';
10
10
  import { init_admin_layout_catalyst_template, renderAdminLayout, adminLayoutV2, renderAdminLayoutCatalyst } from './chunk-V5LBQN3I.js';
11
11
  export { getConfirmationDialogScript, renderAlert, renderConfirmationDialog, renderForm, renderFormField, renderPagination, renderTable } from './chunk-V5LBQN3I.js';
12
12
  export { HookSystemImpl, HookUtils, PluginManager as PluginManagerClass, PluginRegistryImpl, PluginValidator as PluginValidatorClass, ScopedHookSystem as ScopedHookSystemClass } from './chunk-CPXAVWCU.js';
13
- import { package_default, getCoreVersion } from './chunk-LRFDZHLM.js';
14
- export { QueryFilterBuilder, SONICJS_VERSION, TemplateRenderer, buildQuery, escapeHtml, getCoreVersion, renderTemplate, sanitizeInput, sanitizeObject, templateRenderer } from './chunk-LRFDZHLM.js';
13
+ import { package_default, getCoreVersion } from './chunk-VNCYCH3H.js';
14
+ export { QueryFilterBuilder, SONICJS_VERSION, TemplateRenderer, buildQuery, escapeHtml, getCoreVersion, renderTemplate, sanitizeInput, sanitizeObject, templateRenderer } from './chunk-VNCYCH3H.js';
15
15
  import './chunk-X7ZAEI5S.js';
16
16
  export { metricsTracker } from './chunk-FICTAGD4.js';
17
17
  export { HOOKS } from './chunk-LOUJRBXV.js';
@@ -22,6 +22,31 @@ import { setCookie } from 'hono/cookie';
22
22
  import { z } from 'zod';
23
23
  import { drizzle } from 'drizzle-orm/d1';
24
24
 
25
+ // src/middleware/admin-setup.ts
26
+ function adminSetupMiddleware() {
27
+ return async (c, next) => {
28
+ const path = new URL(c.req.url).pathname;
29
+ if (path.startsWith("/auth/")) {
30
+ return next();
31
+ }
32
+ if (path.match(/\.(js|css|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/)) {
33
+ return next();
34
+ }
35
+ if (path === "/health") {
36
+ return next();
37
+ }
38
+ if (path.startsWith("/api/")) {
39
+ return next();
40
+ }
41
+ const db = c.env.DB;
42
+ const adminExists = await checkAdminUserExists(db);
43
+ if (!adminExists) {
44
+ return c.redirect("/auth/register?setup=true");
45
+ }
46
+ return next();
47
+ };
48
+ }
49
+
25
50
  // src/plugins/core-plugins/database-tools-plugin/services/database-service.ts
26
51
  var DatabaseToolsService = class {
27
52
  constructor(db) {
@@ -741,6 +766,573 @@ function createDatabaseToolsAdminRoutes() {
741
766
  });
742
767
  return router2;
743
768
  }
769
+
770
+ // src/plugins/core-plugins/seed-data-plugin/services/seed-data-service.ts
771
+ var SeedDataService = class {
772
+ constructor(db) {
773
+ this.db = db;
774
+ }
775
+ // First names for generating realistic users
776
+ firstNames = [
777
+ "Emma",
778
+ "Liam",
779
+ "Olivia",
780
+ "Noah",
781
+ "Ava",
782
+ "Ethan",
783
+ "Sophia",
784
+ "Mason",
785
+ "Isabella",
786
+ "William",
787
+ "Mia",
788
+ "James",
789
+ "Charlotte",
790
+ "Benjamin",
791
+ "Amelia",
792
+ "Lucas",
793
+ "Harper",
794
+ "Henry",
795
+ "Evelyn",
796
+ "Alexander"
797
+ ];
798
+ // Last names for generating realistic users
799
+ lastNames = [
800
+ "Smith",
801
+ "Johnson",
802
+ "Williams",
803
+ "Brown",
804
+ "Jones",
805
+ "Garcia",
806
+ "Miller",
807
+ "Davis",
808
+ "Rodriguez",
809
+ "Martinez",
810
+ "Hernandez",
811
+ "Lopez",
812
+ "Gonzalez",
813
+ "Wilson",
814
+ "Anderson",
815
+ "Thomas",
816
+ "Taylor",
817
+ "Moore",
818
+ "Jackson",
819
+ "Martin"
820
+ ];
821
+ // Content titles for blog posts
822
+ blogTitles = [
823
+ "Getting Started with Modern Web Development",
824
+ "The Future of JavaScript Frameworks",
825
+ "Building Scalable Applications with Microservices",
826
+ "Understanding TypeScript: A Complete Guide",
827
+ "Best Practices for API Design",
828
+ "Introduction to Cloud Computing",
829
+ "Mastering Git and Version Control",
830
+ "The Art of Code Review",
831
+ "Performance Optimization Techniques",
832
+ "Security Best Practices for Web Apps",
833
+ "Exploring Serverless Architecture",
834
+ "Database Design Fundamentals",
835
+ "Testing Strategies for Modern Apps",
836
+ "CI/CD Pipeline Implementation",
837
+ "Mobile-First Development Approach"
838
+ ];
839
+ // Content titles for pages
840
+ pageTitles = [
841
+ "About Us",
842
+ "Contact",
843
+ "Privacy Policy",
844
+ "Terms of Service",
845
+ "FAQ",
846
+ "Our Team",
847
+ "Careers",
848
+ "Press Kit",
849
+ "Support",
850
+ "Documentation",
851
+ "Pricing",
852
+ "Features"
853
+ ];
854
+ // Content titles for products
855
+ productTitles = [
856
+ "Premium Wireless Headphones",
857
+ "Smart Watch Pro",
858
+ "Laptop Stand Adjustable",
859
+ "Mechanical Keyboard RGB",
860
+ "HD Webcam 4K",
861
+ "USB-C Hub 7-in-1",
862
+ "Portable SSD 1TB",
863
+ "Wireless Mouse Ergonomic",
864
+ 'Monitor 27" 4K',
865
+ "Desk Lamp LED",
866
+ "Phone Case Premium",
867
+ "Tablet Stand Aluminum",
868
+ "Cable Management Kit",
869
+ "Power Bank 20000mAh",
870
+ "Bluetooth Speaker Portable"
871
+ ];
872
+ // Content for generating blog posts
873
+ blogContent = [
874
+ "This comprehensive guide covers everything you need to know about modern development practices and tools.",
875
+ "Learn the fundamentals and advanced concepts that will help you build better applications.",
876
+ "Discover the latest trends and best practices used by industry professionals.",
877
+ "A deep dive into the technologies and methodologies shaping the future of software development.",
878
+ "Practical tips and real-world examples to improve your development workflow.",
879
+ "Explore cutting-edge techniques and proven strategies for building robust applications.",
880
+ "Master the essential skills needed to excel in modern software development.",
881
+ "An in-depth look at the tools and frameworks that power today's web applications.",
882
+ "Step-by-step instructions and expert insights for developers of all levels.",
883
+ "Understanding the core principles that drive successful software projects."
884
+ ];
885
+ // Generate a random ID
886
+ generateId() {
887
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
888
+ }
889
+ // Generate a slug from a title
890
+ generateSlug(title) {
891
+ return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
892
+ }
893
+ // Generate random date within the last year
894
+ randomDate() {
895
+ const now = /* @__PURE__ */ new Date();
896
+ const yearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
897
+ const randomTime = yearAgo.getTime() + Math.random() * (now.getTime() - yearAgo.getTime());
898
+ return new Date(randomTime);
899
+ }
900
+ // Create 20 example users
901
+ async createUsers() {
902
+ const roles = ["admin", "editor", "author", "viewer"];
903
+ const hashedPassword = "password123";
904
+ let count = 0;
905
+ for (let i = 0; i < 20; i++) {
906
+ const firstName = this.firstNames[Math.floor(Math.random() * this.firstNames.length)] || "John";
907
+ const lastName = this.lastNames[Math.floor(Math.random() * this.lastNames.length)] || "Doe";
908
+ const username = `${firstName.toLowerCase()}${lastName.toLowerCase()}${i}`;
909
+ const email = `${username}@example.com`;
910
+ const createdAt = this.randomDate();
911
+ const createdAtTimestamp = Math.floor(createdAt.getTime() / 1e3);
912
+ const stmt = this.db.prepare(`
913
+ INSERT INTO users (id, email, username, first_name, last_name, password_hash, role, is_active, last_login_at, created_at, updated_at)
914
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
915
+ `);
916
+ await stmt.bind(
917
+ this.generateId(),
918
+ email,
919
+ username,
920
+ firstName,
921
+ lastName,
922
+ hashedPassword,
923
+ roles[Math.floor(Math.random() * roles.length)],
924
+ Math.random() > 0.1 ? 1 : 0,
925
+ // 90% active
926
+ Math.random() > 0.3 ? createdAtTimestamp : null,
927
+ createdAtTimestamp,
928
+ createdAtTimestamp
929
+ ).run();
930
+ count++;
931
+ }
932
+ return count;
933
+ }
934
+ // Create 200 content items across different types
935
+ async createContent() {
936
+ const usersStmt = this.db.prepare("SELECT * FROM users");
937
+ const { results: allUsers } = await usersStmt.all();
938
+ const collectionsStmt = this.db.prepare("SELECT * FROM collections");
939
+ const { results: allCollections } = await collectionsStmt.all();
940
+ if (!allUsers || allUsers.length === 0) {
941
+ throw new Error("No users found. Please create users first.");
942
+ }
943
+ if (!allCollections || allCollections.length === 0) {
944
+ throw new Error("No collections found. Please create collections first.");
945
+ }
946
+ const statuses = ["draft", "published", "archived"];
947
+ let count = 0;
948
+ for (let i = 0; i < 200; i++) {
949
+ const collection = allCollections[Math.floor(Math.random() * allCollections.length)];
950
+ const author = allUsers[Math.floor(Math.random() * allUsers.length)];
951
+ const status = statuses[Math.floor(Math.random() * statuses.length)];
952
+ let title;
953
+ let contentData;
954
+ if (collection.name === "blog_posts" || collection.name.includes("blog")) {
955
+ title = this.blogTitles[Math.floor(Math.random() * this.blogTitles.length)] || "Untitled Blog Post";
956
+ contentData = {
957
+ body: this.blogContent[Math.floor(Math.random() * this.blogContent.length)] || "Blog content here",
958
+ excerpt: "A brief introduction to this article that provides an overview of the main topics covered.",
959
+ tags: this.generateTags(),
960
+ featured: Math.random() > 0.7
961
+ };
962
+ } else if (collection.name === "pages" || collection.name.includes("page")) {
963
+ title = this.pageTitles[Math.floor(Math.random() * this.pageTitles.length)] || "Untitled Page";
964
+ contentData = {
965
+ body: "This is a standard page with important information about our services and policies.",
966
+ template: "default",
967
+ showInMenu: Math.random() > 0.5
968
+ };
969
+ } else if (collection.name === "products" || collection.name.includes("product")) {
970
+ title = this.productTitles[Math.floor(Math.random() * this.productTitles.length)] || "Untitled Product";
971
+ contentData = {
972
+ description: "High-quality product with excellent features and great value for money.",
973
+ price: (Math.random() * 500 + 10).toFixed(2),
974
+ sku: `SKU-${Math.random().toString(36).substr(2, 9).toUpperCase()}`,
975
+ inStock: Math.random() > 0.2,
976
+ rating: (Math.random() * 2 + 3).toFixed(1)
977
+ // 3.0 - 5.0
978
+ };
979
+ } else {
980
+ title = `${collection.display_name || collection.name} Item ${i + 1}`;
981
+ contentData = {
982
+ description: "This is a sample content item with generic data.",
983
+ value: Math.floor(Math.random() * 1e3)
984
+ };
985
+ }
986
+ const slug = `${this.generateSlug(title)}-${i}`;
987
+ const createdAt = this.randomDate();
988
+ const createdAtTimestamp = Math.floor(createdAt.getTime() / 1e3);
989
+ const publishedAtTimestamp = status === "published" ? createdAtTimestamp : null;
990
+ const stmt = this.db.prepare(`
991
+ INSERT INTO content (id, collection_id, slug, title, data, status, published_at, author_id, created_at, updated_at)
992
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
993
+ `);
994
+ await stmt.bind(
995
+ this.generateId(),
996
+ collection.id,
997
+ slug,
998
+ `${title} ${i}`,
999
+ JSON.stringify(contentData),
1000
+ status,
1001
+ publishedAtTimestamp,
1002
+ author.id,
1003
+ createdAtTimestamp,
1004
+ createdAtTimestamp
1005
+ ).run();
1006
+ count++;
1007
+ }
1008
+ return count;
1009
+ }
1010
+ // Generate random tags for blog posts
1011
+ generateTags() {
1012
+ const allTags = [
1013
+ "tutorial",
1014
+ "guide",
1015
+ "javascript",
1016
+ "typescript",
1017
+ "web-dev",
1018
+ "backend",
1019
+ "frontend",
1020
+ "best-practices",
1021
+ "security",
1022
+ "performance",
1023
+ "testing",
1024
+ "deployment",
1025
+ "cloud",
1026
+ "database",
1027
+ "api"
1028
+ ];
1029
+ const numTags = Math.floor(Math.random() * 4) + 1;
1030
+ const shuffled = allTags.sort(() => 0.5 - Math.random());
1031
+ return shuffled.slice(0, numTags);
1032
+ }
1033
+ // Seed all data
1034
+ async seedAll() {
1035
+ const userCount = await this.createUsers();
1036
+ const contentCount = await this.createContent();
1037
+ return {
1038
+ users: userCount,
1039
+ content: contentCount
1040
+ };
1041
+ }
1042
+ // Clear all seed data (optional cleanup method)
1043
+ async clearSeedData() {
1044
+ const deleteContentStmt = this.db.prepare("DELETE FROM content");
1045
+ await deleteContentStmt.run();
1046
+ const deleteUsersStmt = this.db.prepare(
1047
+ "DELETE FROM users WHERE role != 'admin'"
1048
+ );
1049
+ await deleteUsersStmt.run();
1050
+ }
1051
+ };
1052
+
1053
+ // src/plugins/core-plugins/seed-data-plugin/admin-routes.ts
1054
+ function createSeedDataAdminRoutes() {
1055
+ const routes = new Hono();
1056
+ routes.get("/", async (c) => {
1057
+ const html3 = `
1058
+ <!DOCTYPE html>
1059
+ <html>
1060
+ <head>
1061
+ <title>Seed Data - SonicJS Admin</title>
1062
+ <meta charset="utf-8">
1063
+ <meta name="viewport" content="width=device-width, initial-scale=1">
1064
+ <style>
1065
+ * { margin: 0; padding: 0; box-sizing: border-box; }
1066
+ body {
1067
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1068
+ background: #f5f5f5;
1069
+ padding: 2rem;
1070
+ }
1071
+ .container {
1072
+ max-width: 800px;
1073
+ margin: 0 auto;
1074
+ background: white;
1075
+ border-radius: 8px;
1076
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
1077
+ padding: 2rem;
1078
+ }
1079
+ h1 {
1080
+ color: #333;
1081
+ margin-bottom: 1rem;
1082
+ font-size: 2rem;
1083
+ }
1084
+ .description {
1085
+ color: #666;
1086
+ margin-bottom: 2rem;
1087
+ line-height: 1.6;
1088
+ }
1089
+ .card {
1090
+ background: #f9f9f9;
1091
+ border-radius: 6px;
1092
+ padding: 1.5rem;
1093
+ margin-bottom: 1.5rem;
1094
+ }
1095
+ .card h2 {
1096
+ color: #333;
1097
+ font-size: 1.25rem;
1098
+ margin-bottom: 0.75rem;
1099
+ }
1100
+ .card p {
1101
+ color: #666;
1102
+ line-height: 1.6;
1103
+ margin-bottom: 1rem;
1104
+ }
1105
+ .card ul {
1106
+ color: #666;
1107
+ margin-left: 1.5rem;
1108
+ margin-bottom: 1rem;
1109
+ }
1110
+ .card li {
1111
+ margin-bottom: 0.5rem;
1112
+ }
1113
+ button {
1114
+ background: #3b82f6;
1115
+ color: white;
1116
+ border: none;
1117
+ padding: 0.75rem 1.5rem;
1118
+ border-radius: 6px;
1119
+ font-size: 1rem;
1120
+ cursor: pointer;
1121
+ transition: background 0.2s;
1122
+ }
1123
+ button:hover {
1124
+ background: #2563eb;
1125
+ }
1126
+ button:disabled {
1127
+ background: #94a3b8;
1128
+ cursor: not-allowed;
1129
+ }
1130
+ .danger {
1131
+ background: #ef4444;
1132
+ }
1133
+ .danger:hover {
1134
+ background: #dc2626;
1135
+ }
1136
+ .warning {
1137
+ background: #f59e0b;
1138
+ color: #fff;
1139
+ padding: 1rem;
1140
+ border-radius: 6px;
1141
+ margin-bottom: 1.5rem;
1142
+ }
1143
+ .success {
1144
+ background: #10b981;
1145
+ color: #fff;
1146
+ padding: 1rem;
1147
+ border-radius: 6px;
1148
+ margin-bottom: 1.5rem;
1149
+ display: none;
1150
+ }
1151
+ .error {
1152
+ background: #ef4444;
1153
+ color: #fff;
1154
+ padding: 1rem;
1155
+ border-radius: 6px;
1156
+ margin-bottom: 1.5rem;
1157
+ display: none;
1158
+ }
1159
+ .loading {
1160
+ display: none;
1161
+ margin-left: 1rem;
1162
+ }
1163
+ .back-link {
1164
+ display: inline-block;
1165
+ margin-bottom: 1rem;
1166
+ color: #3b82f6;
1167
+ text-decoration: none;
1168
+ font-size: 0.9rem;
1169
+ }
1170
+ .back-link:hover {
1171
+ text-decoration: underline;
1172
+ }
1173
+ </style>
1174
+ </head>
1175
+ <body>
1176
+ <div class="container">
1177
+ <a href="/admin/plugins/seed-data" class="back-link">\u2190 Back to Plugin Settings</a>
1178
+ <h1>\u{1F331} Seed Data Generator</h1>
1179
+ <p class="description">
1180
+ Generate realistic example data for testing and development. This will create 20 users and 200 content items with realistic data.
1181
+ </p>
1182
+
1183
+ <div class="warning">
1184
+ <strong>\u26A0\uFE0F Warning:</strong> This will create new data in your database. Make sure you're not running this in production!
1185
+ </div>
1186
+
1187
+ <div class="success" id="successMessage"></div>
1188
+ <div class="error" id="errorMessage"></div>
1189
+
1190
+ <div class="card">
1191
+ <h2>What will be created?</h2>
1192
+ <ul>
1193
+ <li><strong>20 Users:</strong> With realistic names, emails, and various roles (admin, editor, author, viewer)</li>
1194
+ <li><strong>200 Content Items:</strong> Including blog posts, pages, and products with realistic titles and data</li>
1195
+ <li><strong>All passwords:</strong> Set to "password123" for testing</li>
1196
+ <li><strong>Random dates:</strong> Created within the last year</li>
1197
+ <li><strong>Various statuses:</strong> Draft, published, and archived content</li>
1198
+ </ul>
1199
+ </div>
1200
+
1201
+ <div class="card">
1202
+ <h2>Generate Seed Data</h2>
1203
+ <p>Click the button below to generate example data. This may take a few moments.</p>
1204
+ <button id="seedButton" onclick="generateSeedData()">
1205
+ Generate Data
1206
+ <span class="loading" id="loading">...</span>
1207
+ </button>
1208
+ </div>
1209
+
1210
+ <div class="card">
1211
+ <h2>Clear Seed Data</h2>
1212
+ <p>Remove all users and content from the database (except admin users).</p>
1213
+ <button class="danger" id="clearButton" onclick="clearSeedData()">
1214
+ Clear All Data
1215
+ <span class="loading" id="clearLoading">...</span>
1216
+ </button>
1217
+ </div>
1218
+ </div>
1219
+
1220
+ <script>
1221
+ async function generateSeedData() {
1222
+ const button = document.getElementById('seedButton');
1223
+ const loading = document.getElementById('loading');
1224
+ const success = document.getElementById('successMessage');
1225
+ const error = document.getElementById('errorMessage');
1226
+
1227
+ button.disabled = true;
1228
+ loading.style.display = 'inline';
1229
+ success.style.display = 'none';
1230
+ error.style.display = 'none';
1231
+
1232
+ try {
1233
+ const response = await fetch('/admin/seed-data/generate', {
1234
+ method: 'POST',
1235
+ headers: {
1236
+ 'Content-Type': 'application/json'
1237
+ }
1238
+ });
1239
+
1240
+ const data = await response.json();
1241
+
1242
+ if (response.ok) {
1243
+ success.textContent = \`\u2705 Successfully created \${data.users} users and \${data.content} content items!\`;
1244
+ success.style.display = 'block';
1245
+ } else {
1246
+ throw new Error(data.error || 'Failed to generate seed data');
1247
+ }
1248
+ } catch (err) {
1249
+ error.textContent = \`\u274C Error: \${err.message}\`;
1250
+ error.style.display = 'block';
1251
+ } finally {
1252
+ button.disabled = false;
1253
+ loading.style.display = 'none';
1254
+ }
1255
+ }
1256
+
1257
+ async function clearSeedData() {
1258
+ if (!confirm('Are you sure you want to clear all data? This cannot be undone!')) {
1259
+ return;
1260
+ }
1261
+
1262
+ const button = document.getElementById('clearButton');
1263
+ const loading = document.getElementById('clearLoading');
1264
+ const success = document.getElementById('successMessage');
1265
+ const error = document.getElementById('errorMessage');
1266
+
1267
+ button.disabled = true;
1268
+ loading.style.display = 'inline';
1269
+ success.style.display = 'none';
1270
+ error.style.display = 'none';
1271
+
1272
+ try {
1273
+ const response = await fetch('/admin/seed-data/clear', {
1274
+ method: 'POST',
1275
+ headers: {
1276
+ 'Content-Type': 'application/json'
1277
+ }
1278
+ });
1279
+
1280
+ const data = await response.json();
1281
+
1282
+ if (response.ok) {
1283
+ success.textContent = '\u2705 Successfully cleared all seed data!';
1284
+ success.style.display = 'block';
1285
+ } else {
1286
+ throw new Error(data.error || 'Failed to clear seed data');
1287
+ }
1288
+ } catch (err) {
1289
+ error.textContent = \`\u274C Error: \${err.message}\`;
1290
+ error.style.display = 'block';
1291
+ } finally {
1292
+ button.disabled = false;
1293
+ loading.style.display = 'none';
1294
+ }
1295
+ }
1296
+ </script>
1297
+ </body>
1298
+ </html>
1299
+ `;
1300
+ return c.html(html3);
1301
+ });
1302
+ routes.post("/generate", async (c) => {
1303
+ try {
1304
+ const db = c.env.DB;
1305
+ const seedService = new SeedDataService(db);
1306
+ const result = await seedService.seedAll();
1307
+ return c.json({
1308
+ success: true,
1309
+ users: result.users,
1310
+ content: result.content
1311
+ });
1312
+ } catch (error) {
1313
+ return c.json({
1314
+ success: false,
1315
+ error: error.message
1316
+ }, 500);
1317
+ }
1318
+ });
1319
+ routes.post("/clear", async (c) => {
1320
+ try {
1321
+ const db = c.env.DB;
1322
+ const seedService = new SeedDataService(db);
1323
+ await seedService.clearSeedData();
1324
+ return c.json({
1325
+ success: true
1326
+ });
1327
+ } catch (error) {
1328
+ return c.json({
1329
+ success: false,
1330
+ error: error.message
1331
+ }, 500);
1332
+ }
1333
+ });
1334
+ return routes;
1335
+ }
744
1336
  function createEmailPlugin() {
745
1337
  const builder = PluginBuilder.create({
746
1338
  name: "email",
@@ -2147,6 +2739,7 @@ function createSonicJSApp(config = {}) {
2147
2739
  });
2148
2740
  app.use("*", metricsMiddleware());
2149
2741
  app.use("*", bootstrapMiddleware(config));
2742
+ app.use("*", adminSetupMiddleware());
2150
2743
  if (config.middleware?.beforeAuth) {
2151
2744
  for (const middleware of config.middleware.beforeAuth) {
2152
2745
  app.use("*", middleware);
@@ -2171,6 +2764,7 @@ function createSonicJSApp(config = {}) {
2171
2764
  app.route("/admin/collections", adminCollectionsRoutes);
2172
2765
  app.route("/admin/settings", adminSettingsRoutes);
2173
2766
  app.route("/admin/database-tools", createDatabaseToolsAdminRoutes());
2767
+ app.route("/admin/seed-data", createSeedDataAdminRoutes());
2174
2768
  app.route("/admin/content", admin_content_default);
2175
2769
  app.route("/admin/media", adminMediaRoutes);
2176
2770
  app.route("/admin/plugins", adminPluginRoutes);