@sonicjs-cms/core 2.10.0 → 2.11.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 (82) hide show
  1. package/dist/{chunk-CJYFSKH7.js → chunk-2MXF4RYZ.js} +3 -3
  2. package/dist/{chunk-CJYFSKH7.js.map → chunk-2MXF4RYZ.js.map} +1 -1
  3. package/dist/{chunk-MNFY6DWY.cjs → chunk-56GUBLJE.cjs} +7 -7
  4. package/dist/{chunk-MNFY6DWY.cjs.map → chunk-56GUBLJE.cjs.map} +1 -1
  5. package/dist/{chunk-IIBRG5S5.cjs → chunk-6BVLPACH.cjs} +408 -2
  6. package/dist/chunk-6BVLPACH.cjs.map +1 -0
  7. package/dist/{chunk-RCA6R6VE.cjs → chunk-ASAEJ4B7.cjs} +315 -162
  8. package/dist/chunk-ASAEJ4B7.cjs.map +1 -0
  9. package/dist/{chunk-IT2TC4ZD.cjs → chunk-B2ASV5RD.cjs} +13 -7
  10. package/dist/chunk-B2ASV5RD.cjs.map +1 -0
  11. package/dist/{chunk-IZWNIUJI.js → chunk-BUU2US2Z.js} +3 -3
  12. package/dist/{chunk-IZWNIUJI.js.map → chunk-BUU2US2Z.js.map} +1 -1
  13. package/dist/{chunk-ZMVWMJ3S.cjs → chunk-DE5YTNCD.cjs} +9 -2
  14. package/dist/chunk-DE5YTNCD.cjs.map +1 -0
  15. package/dist/{chunk-4TTMQQC7.js → chunk-GKRGDJGG.js} +10 -4
  16. package/dist/chunk-GKRGDJGG.js.map +1 -0
  17. package/dist/{chunk-6O3RJV3C.js → chunk-H55AYIRI.js} +9 -2
  18. package/dist/chunk-H55AYIRI.js.map +1 -0
  19. package/dist/{chunk-JTNUM7JE.js → chunk-JTQBNSZX.js} +187 -34
  20. package/dist/chunk-JTQBNSZX.js.map +1 -0
  21. package/dist/{chunk-64APW3DW.cjs → chunk-LFAQUR7P.cjs} +9 -2
  22. package/dist/chunk-LFAQUR7P.cjs.map +1 -0
  23. package/dist/{chunk-27AOVQTR.js → chunk-NMLFKXWW.js} +402 -3
  24. package/dist/chunk-NMLFKXWW.js.map +1 -0
  25. package/dist/{chunk-EKPLKUZT.cjs → chunk-QLPFENZ2.cjs} +3 -3
  26. package/dist/{chunk-EKPLKUZT.cjs.map → chunk-QLPFENZ2.cjs.map} +1 -1
  27. package/dist/{chunk-KYGRJCZM.cjs → chunk-QTFKZBLC.cjs} +3 -2
  28. package/dist/chunk-QTFKZBLC.cjs.map +1 -0
  29. package/dist/{chunk-LOUJRBXV.js → chunk-QXOZI5Q2.js} +3 -2
  30. package/dist/chunk-QXOZI5Q2.js.map +1 -0
  31. package/dist/{chunk-7JMMLHPQ.js → chunk-VJCLJH3X.js} +9 -2
  32. package/dist/chunk-VJCLJH3X.js.map +1 -0
  33. package/dist/index.cjs +751 -152
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +125 -5
  36. package/dist/index.d.ts +125 -5
  37. package/dist/index.js +582 -15
  38. package/dist/index.js.map +1 -1
  39. package/dist/middleware.cjs +29 -29
  40. package/dist/middleware.js +3 -3
  41. package/dist/migrations-UFVJTPVT.js +4 -0
  42. package/dist/{migrations-N2C2VPJU.js.map → migrations-UFVJTPVT.js.map} +1 -1
  43. package/dist/migrations-VNYOSUNE.cjs +13 -0
  44. package/dist/{migrations-ONIAY6GK.cjs.map → migrations-VNYOSUNE.cjs.map} +1 -1
  45. package/dist/{plugin-0Xogrln-.d.cts → plugin-DDYetMF-.d.cts} +1 -0
  46. package/dist/{plugin-0Xogrln-.d.ts → plugin-DDYetMF-.d.ts} +1 -0
  47. package/dist/{plugin-bootstrap-fpG98Otb.d.cts → plugin-bootstrap-DCXpeQVb.d.cts} +229 -1
  48. package/dist/{plugin-bootstrap-WmpvYM5w.d.ts → plugin-bootstrap-DXBAYaqM.d.ts} +229 -1
  49. package/dist/{plugin-manager-GcIeb226.d.cts → plugin-manager-BoM3Q7o7.d.cts} +1 -1
  50. package/dist/{plugin-manager-Clf2gXwj.d.ts → plugin-manager-Efx9RyDX.d.ts} +1 -1
  51. package/dist/plugins.cjs +10 -10
  52. package/dist/plugins.d.cts +2 -2
  53. package/dist/plugins.d.ts +2 -2
  54. package/dist/plugins.js +2 -2
  55. package/dist/routes.cjs +29 -29
  56. package/dist/routes.js +6 -6
  57. package/dist/services.cjs +60 -32
  58. package/dist/services.d.cts +1 -1
  59. package/dist/services.d.ts +1 -1
  60. package/dist/services.js +3 -3
  61. package/dist/types.cjs +2 -2
  62. package/dist/types.d.cts +1 -1
  63. package/dist/types.d.ts +1 -1
  64. package/dist/types.js +1 -1
  65. package/dist/utils.cjs +11 -11
  66. package/dist/utils.js +1 -1
  67. package/migrations/033_form_content_integration.sql +19 -0
  68. package/package.json +1 -1
  69. package/dist/chunk-27AOVQTR.js.map +0 -1
  70. package/dist/chunk-4TTMQQC7.js.map +0 -1
  71. package/dist/chunk-64APW3DW.cjs.map +0 -1
  72. package/dist/chunk-6O3RJV3C.js.map +0 -1
  73. package/dist/chunk-7JMMLHPQ.js.map +0 -1
  74. package/dist/chunk-IIBRG5S5.cjs.map +0 -1
  75. package/dist/chunk-IT2TC4ZD.cjs.map +0 -1
  76. package/dist/chunk-JTNUM7JE.js.map +0 -1
  77. package/dist/chunk-KYGRJCZM.cjs.map +0 -1
  78. package/dist/chunk-LOUJRBXV.js.map +0 -1
  79. package/dist/chunk-RCA6R6VE.cjs.map +0 -1
  80. package/dist/chunk-ZMVWMJ3S.cjs.map +0 -1
  81. package/dist/migrations-N2C2VPJU.js +0 -4
  82. package/dist/migrations-ONIAY6GK.cjs +0 -13
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db/migrations-bundle.ts","../src/services/migrations.ts"],"names":[],"mappings":"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","file":"chunk-DE5YTNCD.cjs","sourcesContent":["/**\n * AUTO-GENERATED FILE - DO NOT EDIT\n * Generated by: scripts/generate-migrations.ts\n * Generated at: 2026-04-07T00:31:27.272Z\n *\n * This file contains all migration SQL bundled for use in Cloudflare Workers\n * where filesystem access is not available at runtime.\n */\n\nexport interface BundledMigration {\n id: string\n name: string\n filename: string\n description: string\n sql: string\n}\n\nexport const bundledMigrations: BundledMigration[] = [\n {\n id: '001',\n name: 'Initial Schema',\n filename: '001_initial_schema.sql',\n description: 'Migration 001: Initial Schema',\n sql: \"-- Initial schema for SonicJS AI\\n-- Create users table for authentication\\nCREATE TABLE IF NOT EXISTS users (\\n id TEXT PRIMARY KEY,\\n email TEXT NOT NULL UNIQUE,\\n username TEXT NOT NULL UNIQUE,\\n first_name TEXT NOT NULL,\\n last_name TEXT NOT NULL,\\n password_hash TEXT,\\n role TEXT NOT NULL DEFAULT 'viewer',\\n avatar TEXT,\\n is_active INTEGER NOT NULL DEFAULT 1,\\n last_login_at INTEGER,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\n\\n-- Create collections table for content schema definitions\\nCREATE TABLE IF NOT EXISTS collections (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL UNIQUE,\\n display_name TEXT NOT NULL,\\n description TEXT,\\n schema TEXT NOT NULL, -- JSON schema definition\\n is_active INTEGER NOT NULL DEFAULT 1,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\n\\n-- Create content table for actual content data\\nCREATE TABLE IF NOT EXISTS content (\\n id TEXT PRIMARY KEY,\\n collection_id TEXT NOT NULL REFERENCES collections(id),\\n slug TEXT NOT NULL,\\n title TEXT NOT NULL,\\n data TEXT NOT NULL, -- JSON content data\\n status TEXT NOT NULL DEFAULT 'draft',\\n published_at INTEGER,\\n author_id TEXT NOT NULL REFERENCES users(id),\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\n\\n-- Create content_versions table for versioning\\nCREATE TABLE IF NOT EXISTS content_versions (\\n id TEXT PRIMARY KEY,\\n content_id TEXT NOT NULL REFERENCES content(id),\\n version INTEGER NOT NULL,\\n data TEXT NOT NULL, -- JSON data\\n author_id TEXT NOT NULL REFERENCES users(id),\\n created_at INTEGER NOT NULL\\n);\\n\\n-- Create media/files table with comprehensive R2 integration\\nCREATE TABLE IF NOT EXISTS media (\\n id TEXT PRIMARY KEY,\\n filename TEXT NOT NULL,\\n original_name TEXT NOT NULL,\\n mime_type TEXT NOT NULL,\\n size INTEGER NOT NULL,\\n width INTEGER,\\n height INTEGER,\\n folder TEXT NOT NULL DEFAULT 'uploads',\\n r2_key TEXT NOT NULL, -- R2 storage key\\n public_url TEXT NOT NULL, -- CDN URL\\n thumbnail_url TEXT, -- Cloudflare Images URL\\n alt TEXT,\\n caption TEXT,\\n tags TEXT, -- JSON array of tags\\n uploaded_by TEXT NOT NULL REFERENCES users(id),\\n uploaded_at INTEGER NOT NULL,\\n updated_at INTEGER,\\n published_at INTEGER,\\n scheduled_at INTEGER,\\n archived_at INTEGER,\\n deleted_at INTEGER\\n);\\n\\n-- Create API tokens table for programmatic access\\nCREATE TABLE IF NOT EXISTS api_tokens (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL,\\n token TEXT NOT NULL UNIQUE,\\n user_id TEXT NOT NULL REFERENCES users(id),\\n permissions TEXT NOT NULL, -- JSON array of permissions\\n expires_at INTEGER,\\n last_used_at INTEGER,\\n created_at INTEGER NOT NULL\\n);\\n\\n-- Create workflow history table for content workflow tracking\\nCREATE TABLE IF NOT EXISTS workflow_history (\\n id TEXT PRIMARY KEY,\\n content_id TEXT NOT NULL REFERENCES content(id),\\n action TEXT NOT NULL,\\n from_status TEXT NOT NULL,\\n to_status TEXT NOT NULL,\\n user_id TEXT NOT NULL REFERENCES users(id),\\n comment TEXT,\\n created_at INTEGER NOT NULL\\n);\\n\\n-- Create indexes for better performance\\nCREATE INDEX IF NOT EXISTS idx_users_email ON users(email);\\nCREATE INDEX IF NOT EXISTS idx_users_username ON users(username);\\nCREATE INDEX IF NOT EXISTS idx_users_role ON users(role);\\n\\nCREATE INDEX IF NOT EXISTS idx_collections_name ON collections(name);\\nCREATE INDEX IF NOT EXISTS idx_collections_active ON collections(is_active);\\n\\nCREATE INDEX IF NOT EXISTS idx_content_collection ON content(collection_id);\\nCREATE INDEX IF NOT EXISTS idx_content_author ON content(author_id);\\nCREATE INDEX IF NOT EXISTS idx_content_status ON content(status);\\nCREATE INDEX IF NOT EXISTS idx_content_published ON content(published_at);\\nCREATE INDEX IF NOT EXISTS idx_content_slug ON content(slug);\\n\\nCREATE INDEX IF NOT EXISTS idx_content_versions_content ON content_versions(content_id);\\nCREATE INDEX IF NOT EXISTS idx_content_versions_version ON content_versions(version);\\n\\nCREATE INDEX IF NOT EXISTS idx_media_folder ON media(folder);\\nCREATE INDEX IF NOT EXISTS idx_media_type ON media(mime_type);\\nCREATE INDEX IF NOT EXISTS idx_media_uploaded_by ON media(uploaded_by);\\nCREATE INDEX IF NOT EXISTS idx_media_uploaded_at ON media(uploaded_at);\\nCREATE INDEX IF NOT EXISTS idx_media_deleted ON media(deleted_at);\\n\\nCREATE INDEX IF NOT EXISTS idx_api_tokens_user ON api_tokens(user_id);\\nCREATE INDEX IF NOT EXISTS idx_api_tokens_token ON api_tokens(token);\\n\\nCREATE INDEX IF NOT EXISTS idx_workflow_history_content ON workflow_history(content_id);\\nCREATE INDEX IF NOT EXISTS idx_workflow_history_user ON workflow_history(user_id);\\n\\n-- Note: Admin user is created via the seed script with user-provided credentials\\n-- Run 'npm run seed' after migrations to create the admin user\\n\\n-- Insert sample collections\\nINSERT OR IGNORE INTO collections (\\n id, name, display_name, description, schema, \\n is_active, created_at, updated_at\\n) VALUES (\\n 'blog-posts-collection',\\n 'blog_posts',\\n 'Blog Posts',\\n 'Blog post content collection',\\n '{\\\"type\\\":\\\"object\\\",\\\"properties\\\":{\\\"title\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Title\\\",\\\"required\\\":true},\\\"content\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Content\\\",\\\"format\\\":\\\"richtext\\\"},\\\"excerpt\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Excerpt\\\"},\\\"featured_image\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Featured Image\\\",\\\"format\\\":\\\"media\\\"},\\\"tags\\\":{\\\"type\\\":\\\"array\\\",\\\"title\\\":\\\"Tags\\\",\\\"items\\\":{\\\"type\\\":\\\"string\\\"}},\\\"status\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Status\\\",\\\"enum\\\":[\\\"draft\\\",\\\"published\\\",\\\"archived\\\"],\\\"default\\\":\\\"draft\\\"}},\\\"required\\\":[\\\"title\\\"]}',\\n 1,\\n strftime('%s', 'now') * 1000,\\n strftime('%s', 'now') * 1000\\n),\\n(\\n 'pages-collection',\\n 'pages',\\n 'Pages',\\n 'Static page content collection',\\n '{\\\"type\\\":\\\"object\\\",\\\"properties\\\":{\\\"title\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Title\\\",\\\"required\\\":true},\\\"content\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Content\\\",\\\"format\\\":\\\"richtext\\\"},\\\"slug\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Slug\\\"},\\\"meta_description\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Meta Description\\\"},\\\"featured_image\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Featured Image\\\",\\\"format\\\":\\\"media\\\"}},\\\"required\\\":[\\\"title\\\"]}',\\n 1,\\n strftime('%s', 'now') * 1000,\\n strftime('%s', 'now') * 1000\\n),\\n(\\n 'news-collection',\\n 'news',\\n 'News',\\n 'News article content collection',\\n '{\\\"type\\\":\\\"object\\\",\\\"properties\\\":{\\\"title\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Title\\\",\\\"required\\\":true},\\\"content\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Content\\\",\\\"format\\\":\\\"richtext\\\"},\\\"publish_date\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Publish Date\\\",\\\"format\\\":\\\"date\\\"},\\\"author\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Author\\\"},\\\"category\\\":{\\\"type\\\":\\\"string\\\",\\\"title\\\":\\\"Category\\\",\\\"enum\\\":[\\\"technology\\\",\\\"business\\\",\\\"general\\\"]}},\\\"required\\\":[\\\"title\\\"]}',\\n 1,\\n strftime('%s', 'now') * 1000,\\n strftime('%s', 'now') * 1000\\n);\\n\\n-- Note: Sample content can be created via the admin interface after the admin user is seeded\"\n },\n {\n id: '002',\n name: 'Faq Plugin',\n filename: '002_faq_plugin.sql',\n description: 'Migration 002: Faq Plugin',\n sql: \"-- FAQ Plugin Migration (DEPRECATED - Now managed by third-party plugin)\\n-- Creates FAQ table for the FAQ plugin\\n-- NOTE: This migration is kept for historical purposes. \\n-- The FAQ functionality is now provided by the faq-plugin third-party plugin.\\n\\nCREATE TABLE IF NOT EXISTS faqs (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n question TEXT NOT NULL,\\n answer TEXT NOT NULL,\\n category TEXT,\\n tags TEXT,\\n isPublished INTEGER NOT NULL DEFAULT 1,\\n sortOrder INTEGER NOT NULL DEFAULT 0,\\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\\n);\\n\\n-- Create indexes for better performance\\nCREATE INDEX IF NOT EXISTS idx_faqs_category ON faqs(category);\\nCREATE INDEX IF NOT EXISTS idx_faqs_published ON faqs(isPublished);\\nCREATE INDEX IF NOT EXISTS idx_faqs_sort_order ON faqs(sortOrder);\\n\\n-- Create trigger to update updated_at timestamp\\nCREATE TRIGGER IF NOT EXISTS faqs_updated_at\\n AFTER UPDATE ON faqs\\nBEGIN\\n UPDATE faqs SET updated_at = strftime('%s', 'now') WHERE id = NEW.id;\\nEND;\\n\\n-- Insert sample FAQ data\\nINSERT OR IGNORE INTO faqs (question, answer, category, tags, isPublished, sortOrder) VALUES \\n('What is SonicJS AI?', \\n'SonicJS AI is a modern, TypeScript-first headless CMS built for Cloudflare''s edge platform. It provides a complete content management system with admin interface, API endpoints, and plugin architecture.',\\n'general',\\n'sonicjs, cms, cloudflare',\\n1,\\n1),\\n\\n('How do I get started with SonicJS AI?',\\n'To get started: 1) Clone the repository, 2) Install dependencies with npm install, 3) Set up your Cloudflare account and services, 4) Run the development server with npm run dev, 5) Access the admin interface at /admin.',\\n'general',\\n'getting-started, setup',\\n1,\\n2),\\n\\n('What technologies does SonicJS AI use?',\\n'SonicJS AI is built with: TypeScript for type safety, Hono.js as the web framework, Cloudflare D1 for the database, Cloudflare R2 for media storage, Cloudflare Workers for serverless execution, and Tailwind CSS for styling.',\\n'technical',\\n'technology-stack, typescript, cloudflare',\\n1,\\n3),\\n\\n('How do I create content in SonicJS AI?',\\n'Content creation is done through the admin interface. Navigate to /admin, log in with your credentials, go to Content section, select a collection, and click \\\"New Content\\\" to create articles, pages, or other content types.',\\n'general',\\n'content-creation, admin',\\n1,\\n4),\\n\\n('Is SonicJS AI free to use?',\\n'SonicJS AI is open source and free to use. You only pay for the Cloudflare services you consume (D1 database, R2 storage, Workers execution time). Cloudflare offers generous free tiers for development and small projects.',\\n'billing',\\n'pricing, open-source, cloudflare',\\n1,\\n5),\\n\\n('How do I add custom functionality?',\\n'SonicJS AI features a plugin system that allows you to extend functionality. You can create plugins using the PluginBuilder API, add custom routes, models, admin pages, and integrate with external services.',\\n'technical',\\n'plugins, customization, development',\\n1,\\n6),\\n\\n('Can I customize the admin interface?',\\n'Yes! The admin interface is built with TypeScript templates and can be customized. You can modify existing templates, create new components, add custom pages, and integrate your own styling while maintaining the dark theme.',\\n'technical',\\n'admin-interface, customization, templates',\\n1,\\n7),\\n\\n('How does authentication work?',\\n'SonicJS AI includes a built-in authentication system with JWT tokens, role-based access control (admin, editor, viewer), secure password hashing, and session management. Users can be managed through the admin interface.',\\n'technical',\\n'authentication, security, users',\\n1,\\n8);\"\n },\n {\n id: '003',\n name: 'Stage5 Enhancements',\n filename: '003_stage5_enhancements.sql',\n description: 'Migration 003: Stage5 Enhancements',\n sql: \"-- Stage 5: Advanced Content Management enhancements\\n-- Add scheduling and workflow features to content table\\n\\n-- Add content scheduling columns\\nALTER TABLE content ADD COLUMN scheduled_publish_at INTEGER;\\nALTER TABLE content ADD COLUMN scheduled_unpublish_at INTEGER;\\n\\n-- Add workflow and review columns\\nALTER TABLE content ADD COLUMN review_status TEXT DEFAULT 'none'; -- none, pending, approved, rejected\\nALTER TABLE content ADD COLUMN reviewer_id TEXT REFERENCES users(id);\\nALTER TABLE content ADD COLUMN reviewed_at INTEGER;\\nALTER TABLE content ADD COLUMN review_notes TEXT;\\n\\n-- Add content metadata\\nALTER TABLE content ADD COLUMN meta_title TEXT;\\nALTER TABLE content ADD COLUMN meta_description TEXT;\\nALTER TABLE content ADD COLUMN featured_image_id TEXT REFERENCES media(id);\\nALTER TABLE content ADD COLUMN content_type TEXT DEFAULT 'standard'; -- standard, template, component\\n\\n-- Create content_fields table for dynamic field definitions\\nCREATE TABLE IF NOT EXISTS content_fields (\\n id TEXT PRIMARY KEY,\\n collection_id TEXT NOT NULL REFERENCES collections(id),\\n field_name TEXT NOT NULL,\\n field_type TEXT NOT NULL, -- text, richtext, number, boolean, date, select, media, relationship\\n field_label TEXT NOT NULL,\\n field_options TEXT, -- JSON for select options, validation rules, etc.\\n field_order INTEGER NOT NULL DEFAULT 0,\\n is_required INTEGER NOT NULL DEFAULT 0,\\n is_searchable INTEGER NOT NULL DEFAULT 0,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL,\\n UNIQUE(collection_id, field_name)\\n);\\n\\n-- Create content_relationships table for content relationships\\nCREATE TABLE IF NOT EXISTS content_relationships (\\n id TEXT PRIMARY KEY,\\n source_content_id TEXT NOT NULL REFERENCES content(id),\\n target_content_id TEXT NOT NULL REFERENCES content(id),\\n relationship_type TEXT NOT NULL, -- references, tags, categories\\n created_at INTEGER NOT NULL,\\n UNIQUE(source_content_id, target_content_id, relationship_type)\\n);\\n\\n-- Create workflow_templates table for reusable workflows\\nCREATE TABLE IF NOT EXISTS workflow_templates (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL,\\n description TEXT,\\n collection_id TEXT REFERENCES collections(id), -- null means applies to all collections\\n workflow_steps TEXT NOT NULL, -- JSON array of workflow steps\\n is_active INTEGER NOT NULL DEFAULT 1,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\n\\n-- Add indexes for new columns\\nCREATE INDEX IF NOT EXISTS idx_content_scheduled_publish ON content(scheduled_publish_at);\\nCREATE INDEX IF NOT EXISTS idx_content_scheduled_unpublish ON content(scheduled_unpublish_at);\\nCREATE INDEX IF NOT EXISTS idx_content_review_status ON content(review_status);\\nCREATE INDEX IF NOT EXISTS idx_content_reviewer ON content(reviewer_id);\\nCREATE INDEX IF NOT EXISTS idx_content_content_type ON content(content_type);\\n\\nCREATE INDEX IF NOT EXISTS idx_content_fields_collection ON content_fields(collection_id);\\nCREATE INDEX IF NOT EXISTS idx_content_fields_name ON content_fields(field_name);\\nCREATE INDEX IF NOT EXISTS idx_content_fields_type ON content_fields(field_type);\\nCREATE INDEX IF NOT EXISTS idx_content_fields_order ON content_fields(field_order);\\n\\nCREATE INDEX IF NOT EXISTS idx_content_relationships_source ON content_relationships(source_content_id);\\nCREATE INDEX IF NOT EXISTS idx_content_relationships_target ON content_relationships(target_content_id);\\nCREATE INDEX IF NOT EXISTS idx_content_relationships_type ON content_relationships(relationship_type);\\n\\nCREATE INDEX IF NOT EXISTS idx_workflow_templates_collection ON workflow_templates(collection_id);\\nCREATE INDEX IF NOT EXISTS idx_workflow_templates_active ON workflow_templates(is_active);\\n\\n-- Insert default workflow template\\nINSERT OR IGNORE INTO workflow_templates (\\n id, name, description, workflow_steps, is_active, created_at, updated_at\\n) VALUES (\\n 'default-content-workflow',\\n 'Default Content Workflow',\\n 'Standard content workflow: Draft → Review → Published',\\n '[\\n {\\\"step\\\": \\\"draft\\\", \\\"name\\\": \\\"Draft\\\", \\\"description\\\": \\\"Content is being created\\\", \\\"permissions\\\": [\\\"author\\\", \\\"editor\\\", \\\"admin\\\"]},\\n {\\\"step\\\": \\\"review\\\", \\\"name\\\": \\\"Under Review\\\", \\\"description\\\": \\\"Content is pending review\\\", \\\"permissions\\\": [\\\"editor\\\", \\\"admin\\\"]},\\n {\\\"step\\\": \\\"published\\\", \\\"name\\\": \\\"Published\\\", \\\"description\\\": \\\"Content is live\\\", \\\"permissions\\\": [\\\"editor\\\", \\\"admin\\\"]},\\n {\\\"step\\\": \\\"archived\\\", \\\"name\\\": \\\"Archived\\\", \\\"description\\\": \\\"Content is archived\\\", \\\"permissions\\\": [\\\"admin\\\"]}\\n ]',\\n 1,\\n strftime('%s', 'now') * 1000,\\n strftime('%s', 'now') * 1000\\n);\\n\\n-- Insert enhanced field definitions for existing collections\\nINSERT OR IGNORE INTO content_fields (\\n id, collection_id, field_name, field_type, field_label, field_options, field_order, is_required, is_searchable, created_at, updated_at\\n) VALUES \\n-- Blog Posts fields\\n('blog-title-field', 'blog-posts-collection', 'title', 'text', 'Title', '{\\\"maxLength\\\": 200, \\\"placeholder\\\": \\\"Enter blog post title\\\"}', 1, 1, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('blog-content-field', 'blog-posts-collection', 'content', 'richtext', 'Content', '{\\\"toolbar\\\": \\\"full\\\", \\\"height\\\": 400}', 2, 1, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('blog-excerpt-field', 'blog-posts-collection', 'excerpt', 'text', 'Excerpt', '{\\\"maxLength\\\": 500, \\\"rows\\\": 3, \\\"placeholder\\\": \\\"Brief description of the post\\\"}', 3, 0, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('blog-tags-field', 'blog-posts-collection', 'tags', 'select', 'Tags', '{\\\"multiple\\\": true, \\\"options\\\": [\\\"technology\\\", \\\"business\\\", \\\"tutorial\\\", \\\"news\\\", \\\"update\\\"], \\\"allowCustom\\\": true}', 4, 0, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('blog-featured-image-field', 'blog-posts-collection', 'featured_image', 'media', 'Featured Image', '{\\\"accept\\\": \\\"image/*\\\", \\\"maxSize\\\": \\\"5MB\\\"}', 5, 0, 0, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('blog-publish-date-field', 'blog-posts-collection', 'publish_date', 'date', 'Publish Date', '{\\\"format\\\": \\\"YYYY-MM-DD\\\", \\\"defaultToday\\\": true}', 6, 0, 0, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('blog-featured-field', 'blog-posts-collection', 'is_featured', 'boolean', 'Featured Post', '{\\\"default\\\": false}', 7, 0, 0, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n\\n-- Pages fields\\n('pages-title-field', 'pages-collection', 'title', 'text', 'Title', '{\\\"maxLength\\\": 200, \\\"placeholder\\\": \\\"Enter page title\\\"}', 1, 1, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('pages-content-field', 'pages-collection', 'content', 'richtext', 'Content', '{\\\"toolbar\\\": \\\"full\\\", \\\"height\\\": 500}', 2, 1, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('pages-slug-field', 'pages-collection', 'slug', 'text', 'URL Slug', '{\\\"pattern\\\": \\\"^[a-z0-9-]+$\\\", \\\"placeholder\\\": \\\"url-friendly-slug\\\"}', 3, 1, 0, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('pages-meta-desc-field', 'pages-collection', 'meta_description', 'text', 'Meta Description', '{\\\"maxLength\\\": 160, \\\"rows\\\": 2, \\\"placeholder\\\": \\\"SEO description for search engines\\\"}', 4, 0, 0, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('pages-template-field', 'pages-collection', 'template', 'select', 'Page Template', '{\\\"options\\\": [\\\"default\\\", \\\"landing\\\", \\\"contact\\\", \\\"about\\\"], \\\"default\\\": \\\"default\\\"}', 5, 0, 0, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n\\n-- News fields\\n('news-title-field', 'news-collection', 'title', 'text', 'Title', '{\\\"maxLength\\\": 200, \\\"placeholder\\\": \\\"Enter news title\\\"}', 1, 1, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('news-content-field', 'news-collection', 'content', 'richtext', 'Content', '{\\\"toolbar\\\": \\\"news\\\", \\\"height\\\": 400}', 2, 1, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('news-category-field', 'news-collection', 'category', 'select', 'Category', '{\\\"options\\\": [\\\"technology\\\", \\\"business\\\", \\\"politics\\\", \\\"sports\\\", \\\"entertainment\\\", \\\"health\\\"], \\\"required\\\": true}', 3, 1, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('news-author-field', 'news-collection', 'author', 'text', 'Author', '{\\\"placeholder\\\": \\\"Author name\\\"}', 4, 1, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('news-source-field', 'news-collection', 'source', 'text', 'Source', '{\\\"placeholder\\\": \\\"News source\\\"}', 5, 0, 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000),\\n('news-priority-field', 'news-collection', 'priority', 'select', 'Priority', '{\\\"options\\\": [\\\"low\\\", \\\"normal\\\", \\\"high\\\", \\\"breaking\\\"], \\\"default\\\": \\\"normal\\\"}', 6, 0, 0, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000);\"\n },\n {\n id: '004',\n name: 'Stage6 User Management',\n filename: '004_stage6_user_management.sql',\n description: 'Migration 004: Stage6 User Management',\n sql: \"-- Stage 6: User Management & Permissions enhancements\\n-- Enhanced user system with profiles, teams, permissions, and activity logging\\n\\n-- Add user profile and preferences columns\\nALTER TABLE users ADD COLUMN phone TEXT;\\nALTER TABLE users ADD COLUMN bio TEXT;\\nALTER TABLE users ADD COLUMN avatar_url TEXT;\\nALTER TABLE users ADD COLUMN timezone TEXT DEFAULT 'UTC';\\nALTER TABLE users ADD COLUMN language TEXT DEFAULT 'en';\\nALTER TABLE users ADD COLUMN email_notifications INTEGER DEFAULT 1;\\nALTER TABLE users ADD COLUMN theme TEXT DEFAULT 'dark';\\nALTER TABLE users ADD COLUMN two_factor_enabled INTEGER DEFAULT 0;\\nALTER TABLE users ADD COLUMN two_factor_secret TEXT;\\nALTER TABLE users ADD COLUMN password_reset_token TEXT;\\nALTER TABLE users ADD COLUMN password_reset_expires INTEGER;\\nALTER TABLE users ADD COLUMN email_verified INTEGER DEFAULT 0;\\nALTER TABLE users ADD COLUMN email_verification_token TEXT;\\nALTER TABLE users ADD COLUMN invitation_token TEXT;\\nALTER TABLE users ADD COLUMN invited_by TEXT REFERENCES users(id);\\nALTER TABLE users ADD COLUMN invited_at INTEGER;\\nALTER TABLE users ADD COLUMN accepted_invitation_at INTEGER;\\n\\n-- Create teams table for team-based collaboration\\nCREATE TABLE IF NOT EXISTS teams (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL,\\n description TEXT,\\n slug TEXT NOT NULL UNIQUE,\\n owner_id TEXT NOT NULL REFERENCES users(id),\\n settings TEXT, -- JSON for team settings\\n is_active INTEGER NOT NULL DEFAULT 1,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\n\\n-- Create team memberships table\\nCREATE TABLE IF NOT EXISTS team_memberships (\\n id TEXT PRIMARY KEY,\\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\\n user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,\\n role TEXT NOT NULL DEFAULT 'member', -- owner, admin, editor, member, viewer\\n permissions TEXT, -- JSON for specific permissions\\n joined_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL,\\n UNIQUE(team_id, user_id)\\n);\\n\\n-- Create permissions table for granular access control\\nCREATE TABLE IF NOT EXISTS permissions (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL UNIQUE,\\n description TEXT,\\n category TEXT NOT NULL, -- content, users, collections, media, settings\\n created_at INTEGER NOT NULL\\n);\\n\\n-- Create role permissions mapping\\nCREATE TABLE IF NOT EXISTS role_permissions (\\n id TEXT PRIMARY KEY,\\n role TEXT NOT NULL,\\n permission_id TEXT NOT NULL REFERENCES permissions(id),\\n created_at INTEGER NOT NULL,\\n UNIQUE(role, permission_id)\\n);\\n\\n-- Create user sessions table for better session management\\nCREATE TABLE IF NOT EXISTS user_sessions (\\n id TEXT PRIMARY KEY,\\n user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,\\n token_hash TEXT NOT NULL,\\n ip_address TEXT,\\n user_agent TEXT,\\n is_active INTEGER NOT NULL DEFAULT 1,\\n expires_at INTEGER NOT NULL,\\n created_at INTEGER NOT NULL,\\n last_used_at INTEGER\\n);\\n\\n-- Create activity log table for audit trails\\nCREATE TABLE IF NOT EXISTS activity_logs (\\n id TEXT PRIMARY KEY,\\n user_id TEXT REFERENCES users(id),\\n action TEXT NOT NULL,\\n resource_type TEXT, -- users, content, collections, media, etc.\\n resource_id TEXT,\\n details TEXT, -- JSON with additional details\\n ip_address TEXT,\\n user_agent TEXT,\\n created_at INTEGER NOT NULL\\n);\\n\\n-- Create password history table for security\\nCREATE TABLE IF NOT EXISTS password_history (\\n id TEXT PRIMARY KEY,\\n user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,\\n password_hash TEXT NOT NULL,\\n created_at INTEGER NOT NULL\\n);\\n\\n-- Insert default permissions\\nINSERT OR IGNORE INTO permissions (id, name, description, category, created_at) VALUES\\n ('perm_content_create', 'content.create', 'Create new content', 'content', strftime('%s', 'now') * 1000),\\n ('perm_content_read', 'content.read', 'View content', 'content', strftime('%s', 'now') * 1000),\\n ('perm_content_update', 'content.update', 'Edit existing content', 'content', strftime('%s', 'now') * 1000),\\n ('perm_content_delete', 'content.delete', 'Delete content', 'content', strftime('%s', 'now') * 1000),\\n ('perm_content_publish', 'content.publish', 'Publish/unpublish content', 'content', strftime('%s', 'now') * 1000),\\n \\n ('perm_collections_create', 'collections.create', 'Create new collections', 'collections', strftime('%s', 'now') * 1000),\\n ('perm_collections_read', 'collections.read', 'View collections', 'collections', strftime('%s', 'now') * 1000),\\n ('perm_collections_update', 'collections.update', 'Edit collections', 'collections', strftime('%s', 'now') * 1000),\\n ('perm_collections_delete', 'collections.delete', 'Delete collections', 'collections', strftime('%s', 'now') * 1000),\\n ('perm_collections_fields', 'collections.fields', 'Manage collection fields', 'collections', strftime('%s', 'now') * 1000),\\n \\n ('perm_media_upload', 'media.upload', 'Upload media files', 'media', strftime('%s', 'now') * 1000),\\n ('perm_media_read', 'media.read', 'View media files', 'media', strftime('%s', 'now') * 1000),\\n ('perm_media_update', 'media.update', 'Edit media metadata', 'media', strftime('%s', 'now') * 1000),\\n ('perm_media_delete', 'media.delete', 'Delete media files', 'media', strftime('%s', 'now') * 1000),\\n \\n ('perm_users_create', 'users.create', 'Invite new users', 'users', strftime('%s', 'now') * 1000),\\n ('perm_users_read', 'users.read', 'View user profiles', 'users', strftime('%s', 'now') * 1000),\\n ('perm_users_update', 'users.update', 'Edit user profiles', 'users', strftime('%s', 'now') * 1000),\\n ('perm_users_delete', 'users.delete', 'Deactivate users', 'users', strftime('%s', 'now') * 1000),\\n ('perm_users_roles', 'users.roles', 'Manage user roles', 'users', strftime('%s', 'now') * 1000),\\n \\n ('perm_settings_read', 'settings.read', 'View system settings', 'settings', strftime('%s', 'now') * 1000),\\n ('perm_settings_update', 'settings.update', 'Modify system settings', 'settings', strftime('%s', 'now') * 1000),\\n ('perm_activity_read', 'activity.read', 'View activity logs', 'settings', strftime('%s', 'now') * 1000);\\n\\n-- Assign permissions to default roles\\nINSERT OR IGNORE INTO role_permissions (id, role, permission_id, created_at) VALUES\\n -- Admin has all permissions\\n ('rp_admin_content_create', 'admin', 'perm_content_create', strftime('%s', 'now') * 1000),\\n ('rp_admin_content_read', 'admin', 'perm_content_read', strftime('%s', 'now') * 1000),\\n ('rp_admin_content_update', 'admin', 'perm_content_update', strftime('%s', 'now') * 1000),\\n ('rp_admin_content_delete', 'admin', 'perm_content_delete', strftime('%s', 'now') * 1000),\\n ('rp_admin_content_publish', 'admin', 'perm_content_publish', strftime('%s', 'now') * 1000),\\n ('rp_admin_collections_create', 'admin', 'perm_collections_create', strftime('%s', 'now') * 1000),\\n ('rp_admin_collections_read', 'admin', 'perm_collections_read', strftime('%s', 'now') * 1000),\\n ('rp_admin_collections_update', 'admin', 'perm_collections_update', strftime('%s', 'now') * 1000),\\n ('rp_admin_collections_delete', 'admin', 'perm_collections_delete', strftime('%s', 'now') * 1000),\\n ('rp_admin_collections_fields', 'admin', 'perm_collections_fields', strftime('%s', 'now') * 1000),\\n ('rp_admin_media_upload', 'admin', 'perm_media_upload', strftime('%s', 'now') * 1000),\\n ('rp_admin_media_read', 'admin', 'perm_media_read', strftime('%s', 'now') * 1000),\\n ('rp_admin_media_update', 'admin', 'perm_media_update', strftime('%s', 'now') * 1000),\\n ('rp_admin_media_delete', 'admin', 'perm_media_delete', strftime('%s', 'now') * 1000),\\n ('rp_admin_users_create', 'admin', 'perm_users_create', strftime('%s', 'now') * 1000),\\n ('rp_admin_users_read', 'admin', 'perm_users_read', strftime('%s', 'now') * 1000),\\n ('rp_admin_users_update', 'admin', 'perm_users_update', strftime('%s', 'now') * 1000),\\n ('rp_admin_users_delete', 'admin', 'perm_users_delete', strftime('%s', 'now') * 1000),\\n ('rp_admin_users_roles', 'admin', 'perm_users_roles', strftime('%s', 'now') * 1000),\\n ('rp_admin_settings_read', 'admin', 'perm_settings_read', strftime('%s', 'now') * 1000),\\n ('rp_admin_settings_update', 'admin', 'perm_settings_update', strftime('%s', 'now') * 1000),\\n ('rp_admin_activity_read', 'admin', 'perm_activity_read', strftime('%s', 'now') * 1000),\\n \\n -- Editor permissions\\n ('rp_editor_content_create', 'editor', 'perm_content_create', strftime('%s', 'now') * 1000),\\n ('rp_editor_content_read', 'editor', 'perm_content_read', strftime('%s', 'now') * 1000),\\n ('rp_editor_content_update', 'editor', 'perm_content_update', strftime('%s', 'now') * 1000),\\n ('rp_editor_content_publish', 'editor', 'perm_content_publish', strftime('%s', 'now') * 1000),\\n ('rp_editor_collections_read', 'editor', 'perm_collections_read', strftime('%s', 'now') * 1000),\\n ('rp_editor_media_upload', 'editor', 'perm_media_upload', strftime('%s', 'now') * 1000),\\n ('rp_editor_media_read', 'editor', 'perm_media_read', strftime('%s', 'now') * 1000),\\n ('rp_editor_media_update', 'editor', 'perm_media_update', strftime('%s', 'now') * 1000),\\n ('rp_editor_users_read', 'editor', 'perm_users_read', strftime('%s', 'now') * 1000),\\n \\n -- Viewer permissions\\n ('rp_viewer_content_read', 'viewer', 'perm_content_read', strftime('%s', 'now') * 1000),\\n ('rp_viewer_collections_read', 'viewer', 'perm_collections_read', strftime('%s', 'now') * 1000),\\n ('rp_viewer_media_read', 'viewer', 'perm_media_read', strftime('%s', 'now') * 1000),\\n ('rp_viewer_users_read', 'viewer', 'perm_users_read', strftime('%s', 'now') * 1000);\\n\\n-- Create indexes for performance\\nCREATE INDEX IF NOT EXISTS idx_team_memberships_team_id ON team_memberships(team_id);\\nCREATE INDEX IF NOT EXISTS idx_team_memberships_user_id ON team_memberships(user_id);\\nCREATE INDEX IF NOT EXISTS idx_user_sessions_user_id ON user_sessions(user_id);\\nCREATE INDEX IF NOT EXISTS idx_user_sessions_token_hash ON user_sessions(token_hash);\\nCREATE INDEX IF NOT EXISTS idx_activity_logs_user_id ON activity_logs(user_id);\\nCREATE INDEX IF NOT EXISTS idx_activity_logs_created_at ON activity_logs(created_at);\\nCREATE INDEX IF NOT EXISTS idx_activity_logs_resource ON activity_logs(resource_type, resource_id);\\nCREATE INDEX IF NOT EXISTS idx_password_history_user_id ON password_history(user_id);\\nCREATE INDEX IF NOT EXISTS idx_users_email_verification_token ON users(email_verification_token);\\nCREATE INDEX IF NOT EXISTS idx_users_password_reset_token ON users(password_reset_token);\\nCREATE INDEX IF NOT EXISTS idx_users_invitation_token ON users(invitation_token);\"\n },\n {\n id: '005',\n name: 'Stage7 Workflow Automation',\n filename: '005_stage7_workflow_automation.sql',\n description: 'Migration 005: Stage7 Workflow Automation',\n sql: \"-- Stage 7: Workflow & Automation Migration\\n-- This migration adds workflow and automation capabilities to SonicJS\\n\\n-- Workflow States Table\\nCREATE TABLE IF NOT EXISTS workflow_states (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n name TEXT NOT NULL,\\n description TEXT,\\n color TEXT DEFAULT '#6B7280',\\n is_initial INTEGER DEFAULT 0,\\n is_final INTEGER DEFAULT 0,\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\\n);\\n\\n-- Insert default workflow states\\nINSERT OR IGNORE INTO workflow_states (id, name, description, color, is_initial, is_final) VALUES\\n('draft', 'Draft', 'Content is being worked on', '#F59E0B', 1, 0),\\n('pending-review', 'Pending Review', 'Content is waiting for review', '#3B82F6', 0, 0),\\n('approved', 'Approved', 'Content has been approved', '#10B981', 0, 0),\\n('published', 'Published', 'Content is live', '#059669', 0, 1),\\n('rejected', 'Rejected', 'Content was rejected', '#EF4444', 0, 1),\\n('archived', 'Archived', 'Content has been archived', '#6B7280', 0, 1);\\n\\n-- Workflows Table\\nCREATE TABLE IF NOT EXISTS workflows (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n name TEXT NOT NULL,\\n description TEXT,\\n collection_id TEXT,\\n is_active INTEGER DEFAULT 1,\\n auto_publish INTEGER DEFAULT 0,\\n require_approval INTEGER DEFAULT 1,\\n approval_levels INTEGER DEFAULT 1,\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (collection_id) REFERENCES collections(id) ON DELETE CASCADE\\n);\\n\\n-- Workflow Transitions Table\\nCREATE TABLE IF NOT EXISTS workflow_transitions (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n workflow_id TEXT NOT NULL,\\n from_state_id TEXT NOT NULL,\\n to_state_id TEXT NOT NULL,\\n required_permission TEXT,\\n auto_transition INTEGER DEFAULT 0,\\n transition_conditions TEXT, -- JSON\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (workflow_id) REFERENCES workflows(id) ON DELETE CASCADE,\\n FOREIGN KEY (from_state_id) REFERENCES workflow_states(id),\\n FOREIGN KEY (to_state_id) REFERENCES workflow_states(id)\\n);\\n\\n-- Content Workflow Status Table\\nCREATE TABLE IF NOT EXISTS content_workflow_status (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n content_id TEXT NOT NULL,\\n workflow_id TEXT NOT NULL,\\n current_state_id TEXT NOT NULL,\\n assigned_to TEXT,\\n due_date DATETIME,\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (content_id) REFERENCES content(id) ON DELETE CASCADE,\\n FOREIGN KEY (workflow_id) REFERENCES workflows(id),\\n FOREIGN KEY (current_state_id) REFERENCES workflow_states(id),\\n FOREIGN KEY (assigned_to) REFERENCES users(id),\\n UNIQUE(content_id, workflow_id)\\n);\\n\\n-- Workflow History Table\\nCREATE TABLE IF NOT EXISTS workflow_history (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n content_id TEXT NOT NULL,\\n workflow_id TEXT NOT NULL,\\n from_state_id TEXT,\\n to_state_id TEXT NOT NULL,\\n user_id TEXT NOT NULL,\\n comment TEXT,\\n metadata TEXT, -- JSON\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (content_id) REFERENCES content(id) ON DELETE CASCADE,\\n FOREIGN KEY (workflow_id) REFERENCES workflows(id),\\n FOREIGN KEY (from_state_id) REFERENCES workflow_states(id),\\n FOREIGN KEY (to_state_id) REFERENCES workflow_states(id),\\n FOREIGN KEY (user_id) REFERENCES users(id)\\n);\\n\\n-- Scheduled Content Table\\nCREATE TABLE IF NOT EXISTS scheduled_content (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n content_id TEXT NOT NULL,\\n action TEXT NOT NULL, -- 'publish', 'unpublish', 'archive'\\n scheduled_at DATETIME NOT NULL,\\n timezone TEXT DEFAULT 'UTC',\\n user_id TEXT NOT NULL,\\n status TEXT DEFAULT 'pending', -- 'pending', 'completed', 'failed', 'cancelled'\\n executed_at DATETIME,\\n error_message TEXT,\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (content_id) REFERENCES content(id) ON DELETE CASCADE,\\n FOREIGN KEY (user_id) REFERENCES users(id)\\n);\\n\\n-- Notifications Table\\nCREATE TABLE IF NOT EXISTS notifications (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n user_id TEXT NOT NULL,\\n type TEXT NOT NULL, -- 'workflow', 'schedule', 'system'\\n title TEXT NOT NULL,\\n message TEXT NOT NULL,\\n data TEXT, -- JSON\\n is_read INTEGER DEFAULT 0,\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\\n);\\n\\n-- Notification Preferences Table\\nCREATE TABLE IF NOT EXISTS notification_preferences (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n user_id TEXT NOT NULL,\\n notification_type TEXT NOT NULL,\\n email_enabled INTEGER DEFAULT 1,\\n in_app_enabled INTEGER DEFAULT 1,\\n digest_frequency TEXT DEFAULT 'daily', -- 'immediate', 'hourly', 'daily', 'weekly'\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,\\n UNIQUE(user_id, notification_type)\\n);\\n\\n-- Webhooks Table\\nCREATE TABLE IF NOT EXISTS webhooks (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n name TEXT NOT NULL,\\n url TEXT NOT NULL,\\n secret TEXT,\\n events TEXT NOT NULL, -- JSON array of event types\\n is_active INTEGER DEFAULT 1,\\n retry_count INTEGER DEFAULT 3,\\n timeout_seconds INTEGER DEFAULT 30,\\n last_success_at DATETIME,\\n last_failure_at DATETIME,\\n failure_count INTEGER DEFAULT 0,\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\\n);\\n\\n-- Webhook Deliveries Table\\nCREATE TABLE IF NOT EXISTS webhook_deliveries (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n webhook_id TEXT NOT NULL,\\n event_type TEXT NOT NULL,\\n payload TEXT NOT NULL, -- JSON\\n response_status INTEGER,\\n response_body TEXT,\\n attempt_count INTEGER DEFAULT 1,\\n delivered_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (webhook_id) REFERENCES webhooks(id) ON DELETE CASCADE\\n);\\n\\n-- Content Versions Table (for rollback functionality)\\nCREATE TABLE IF NOT EXISTS content_versions (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n content_id TEXT NOT NULL,\\n version_number INTEGER NOT NULL,\\n title TEXT NOT NULL,\\n content TEXT NOT NULL,\\n fields TEXT, -- JSON\\n user_id TEXT NOT NULL,\\n change_summary TEXT,\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (content_id) REFERENCES content(id) ON DELETE CASCADE,\\n FOREIGN KEY (user_id) REFERENCES users(id),\\n UNIQUE(content_id, version_number)\\n);\\n\\n-- Automation Rules Table\\nCREATE TABLE IF NOT EXISTS automation_rules (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n name TEXT NOT NULL,\\n description TEXT,\\n trigger_type TEXT NOT NULL, -- 'content_created', 'content_updated', 'workflow_transition', 'schedule'\\n trigger_conditions TEXT, -- JSON\\n action_type TEXT NOT NULL, -- 'workflow_transition', 'send_notification', 'webhook_call', 'auto_save'\\n action_config TEXT, -- JSON\\n is_active INTEGER DEFAULT 1,\\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\\n);\\n\\n-- Auto-save Drafts Table\\nCREATE TABLE IF NOT EXISTS auto_save_drafts (\\n id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\\n content_id TEXT,\\n user_id TEXT NOT NULL,\\n title TEXT,\\n content TEXT,\\n fields TEXT, -- JSON\\n last_saved_at DATETIME DEFAULT CURRENT_TIMESTAMP,\\n FOREIGN KEY (content_id) REFERENCES content(id) ON DELETE CASCADE,\\n FOREIGN KEY (user_id) REFERENCES users(id),\\n UNIQUE(content_id, user_id)\\n);\\n\\n-- Add workflow-related columns to existing content table (skip existing columns)\\nALTER TABLE content ADD COLUMN workflow_state_id TEXT DEFAULT 'draft';\\nALTER TABLE content ADD COLUMN embargo_until DATETIME;\\nALTER TABLE content ADD COLUMN expires_at DATETIME;\\nALTER TABLE content ADD COLUMN version_number INTEGER DEFAULT 1;\\nALTER TABLE content ADD COLUMN is_auto_saved INTEGER DEFAULT 0;\\n\\n-- Create indexes for performance\\nCREATE INDEX IF NOT EXISTS idx_content_workflow_status_content_id ON content_workflow_status(content_id);\\nCREATE INDEX IF NOT EXISTS idx_content_workflow_status_workflow_id ON content_workflow_status(workflow_id);\\nCREATE INDEX IF NOT EXISTS idx_workflow_history_content_id ON workflow_history(content_id);\\nCREATE INDEX IF NOT EXISTS idx_scheduled_content_scheduled_at ON scheduled_content(scheduled_at);\\nCREATE INDEX IF NOT EXISTS idx_scheduled_content_status ON scheduled_content(status);\\nCREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications(user_id);\\nCREATE INDEX IF NOT EXISTS idx_notifications_is_read ON notifications(is_read);\\nCREATE INDEX IF NOT EXISTS idx_content_versions_content_id ON content_versions(content_id);\\nCREATE INDEX IF NOT EXISTS idx_auto_save_drafts_user_id ON auto_save_drafts(user_id);\\nCREATE INDEX IF NOT EXISTS idx_content_workflow_state ON content(workflow_state_id);\\nCREATE INDEX IF NOT EXISTS idx_content_scheduled_publish ON content(scheduled_publish_at);\\n\\n-- Insert default workflow for collections\\nINSERT OR IGNORE INTO workflows (id, name, description, collection_id, is_active, require_approval, approval_levels) \\nSELECT \\n 'default-' || id,\\n 'Default Workflow for ' || name,\\n 'Standard content approval workflow',\\n id,\\n 1,\\n 1,\\n 1\\nFROM collections;\\n\\n-- Insert default workflow transitions\\nINSERT OR IGNORE INTO workflow_transitions (workflow_id, from_state_id, to_state_id, required_permission) \\nSELECT \\n w.id,\\n 'draft',\\n 'pending-review',\\n 'content:submit'\\nFROM workflows w;\\n\\nINSERT OR IGNORE INTO workflow_transitions (workflow_id, from_state_id, to_state_id, required_permission) \\nSELECT \\n w.id,\\n 'pending-review',\\n 'approved',\\n 'content:approve'\\nFROM workflows w;\\n\\nINSERT OR IGNORE INTO workflow_transitions (workflow_id, from_state_id, to_state_id, required_permission) \\nSELECT \\n w.id,\\n 'approved',\\n 'published',\\n 'content:publish'\\nFROM workflows w;\\n\\nINSERT OR IGNORE INTO workflow_transitions (workflow_id, from_state_id, to_state_id, required_permission) \\nSELECT \\n w.id,\\n 'pending-review',\\n 'rejected',\\n 'content:approve'\\nFROM workflows w;\\n\\n-- Insert default notification preferences for all users\\nINSERT OR IGNORE INTO notification_preferences (user_id, notification_type, email_enabled, in_app_enabled)\\nSELECT \\n id,\\n 'workflow_assigned',\\n 1,\\n 1\\nFROM users;\\n\\nINSERT OR IGNORE INTO notification_preferences (user_id, notification_type, email_enabled, in_app_enabled)\\nSELECT \\n id,\\n 'workflow_status_change',\\n 1,\\n 1\\nFROM users;\\n\\nINSERT OR IGNORE INTO notification_preferences (user_id, notification_type, email_enabled, in_app_enabled)\\nSELECT \\n id,\\n 'content_scheduled',\\n 1,\\n 1\\nFROM users;\"\n },\n {\n id: '006',\n name: 'Plugin System',\n filename: '006_plugin_system.sql',\n description: 'Migration 006: Plugin System',\n sql: \"-- Plugin System Tables\\n-- Migration: 006_plugin_system\\n-- Description: Add plugin management system tables\\n\\n-- Plugins table\\nCREATE TABLE IF NOT EXISTS plugins (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL UNIQUE,\\n display_name TEXT NOT NULL,\\n description TEXT,\\n version TEXT NOT NULL,\\n author TEXT NOT NULL,\\n category TEXT NOT NULL,\\n icon TEXT,\\n status TEXT DEFAULT 'inactive' CHECK (status IN ('active', 'inactive', 'error')),\\n is_core BOOLEAN DEFAULT FALSE,\\n settings JSON,\\n permissions JSON,\\n dependencies JSON,\\n download_count INTEGER DEFAULT 0,\\n rating REAL DEFAULT 0,\\n installed_at INTEGER NOT NULL,\\n activated_at INTEGER,\\n last_updated INTEGER NOT NULL,\\n error_message TEXT,\\n created_at INTEGER DEFAULT (unixepoch()),\\n updated_at INTEGER DEFAULT (unixepoch())\\n);\\n\\n-- Plugin hooks table (registered hooks by plugins)\\nCREATE TABLE IF NOT EXISTS plugin_hooks (\\n id TEXT PRIMARY KEY,\\n plugin_id TEXT NOT NULL,\\n hook_name TEXT NOT NULL,\\n handler_name TEXT NOT NULL,\\n priority INTEGER DEFAULT 10,\\n is_active BOOLEAN DEFAULT TRUE,\\n created_at INTEGER DEFAULT (unixepoch()),\\n FOREIGN KEY (plugin_id) REFERENCES plugins(id) ON DELETE CASCADE,\\n UNIQUE(plugin_id, hook_name, handler_name)\\n);\\n\\n-- Plugin routes table\\nCREATE TABLE IF NOT EXISTS plugin_routes (\\n id TEXT PRIMARY KEY,\\n plugin_id TEXT NOT NULL,\\n path TEXT NOT NULL,\\n method TEXT NOT NULL,\\n handler_name TEXT NOT NULL,\\n middleware JSON,\\n is_active BOOLEAN DEFAULT TRUE,\\n created_at INTEGER DEFAULT (unixepoch()),\\n FOREIGN KEY (plugin_id) REFERENCES plugins(id) ON DELETE CASCADE,\\n UNIQUE(plugin_id, path, method)\\n);\\n\\n-- Plugin assets table (CSS, JS files provided by plugins)\\nCREATE TABLE IF NOT EXISTS plugin_assets (\\n id TEXT PRIMARY KEY,\\n plugin_id TEXT NOT NULL,\\n asset_type TEXT NOT NULL CHECK (asset_type IN ('css', 'js', 'image', 'font')),\\n asset_path TEXT NOT NULL,\\n load_order INTEGER DEFAULT 100,\\n load_location TEXT DEFAULT 'footer' CHECK (load_location IN ('header', 'footer')),\\n is_active BOOLEAN DEFAULT TRUE,\\n created_at INTEGER DEFAULT (unixepoch()),\\n FOREIGN KEY (plugin_id) REFERENCES plugins(id) ON DELETE CASCADE\\n);\\n\\n-- Plugin activity log\\nCREATE TABLE IF NOT EXISTS plugin_activity_log (\\n id TEXT PRIMARY KEY,\\n plugin_id TEXT NOT NULL,\\n action TEXT NOT NULL,\\n user_id TEXT,\\n details JSON,\\n timestamp INTEGER DEFAULT (unixepoch()),\\n FOREIGN KEY (plugin_id) REFERENCES plugins(id) ON DELETE CASCADE\\n);\\n\\n-- Create indexes\\nCREATE INDEX IF NOT EXISTS idx_plugins_status ON plugins(status);\\nCREATE INDEX IF NOT EXISTS idx_plugins_category ON plugins(category);\\nCREATE INDEX IF NOT EXISTS idx_plugin_hooks_plugin ON plugin_hooks(plugin_id);\\nCREATE INDEX IF NOT EXISTS idx_plugin_routes_plugin ON plugin_routes(plugin_id);\\nCREATE INDEX IF NOT EXISTS idx_plugin_assets_plugin ON plugin_assets(plugin_id);\\nCREATE INDEX IF NOT EXISTS idx_plugin_activity_plugin ON plugin_activity_log(plugin_id);\\nCREATE INDEX IF NOT EXISTS idx_plugin_activity_timestamp ON plugin_activity_log(timestamp);\\n\\n-- Insert core plugins\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, installed_at, last_updated\\n) VALUES \\n(\\n 'core-auth',\\n 'core-auth',\\n 'Authentication System',\\n 'Core authentication and user management system',\\n '1.0.0',\\n 'SonicJS Team',\\n 'security',\\n '🔐',\\n 'active',\\n TRUE,\\n '[\\\"manage:users\\\", \\\"manage:roles\\\", \\\"manage:permissions\\\"]',\\n unixepoch(),\\n unixepoch()\\n),\\n(\\n 'core-media',\\n 'core-media', \\n 'Media Manager',\\n 'Core media upload and management system',\\n '1.0.0',\\n 'SonicJS Team',\\n 'media',\\n '📸',\\n 'active',\\n TRUE,\\n '[\\\"manage:media\\\", \\\"upload:files\\\"]',\\n unixepoch(),\\n unixepoch()\\n),\\n(\\n 'core-workflow',\\n 'core-workflow',\\n 'Workflow Engine',\\n 'Content workflow and approval system',\\n '1.0.0',\\n 'SonicJS Team',\\n 'content',\\n '🔄',\\n 'active',\\n TRUE,\\n '[\\\"manage:workflows\\\", \\\"approve:content\\\"]',\\n unixepoch(),\\n unixepoch()\\n);\\n\\n-- FAQ Plugin will be added as a third-party plugin through the admin interface\\n\\n-- Add plugin management permission\\nINSERT OR IGNORE INTO permissions (id, name, description, category, created_at)\\nVALUES (\\n 'manage:plugins',\\n 'Manage Plugins',\\n 'Install, uninstall, activate, and configure plugins',\\n 'system',\\n unixepoch()\\n);\\n\\n-- Grant plugin management permission to admin role\\nINSERT OR IGNORE INTO role_permissions (id, role, permission_id, created_at)\\nVALUES ('role-perm-manage-plugins', 'admin', 'manage:plugins', unixepoch());\"\n },\n {\n id: '007',\n name: 'Demo Login Plugin',\n filename: '007_demo_login_plugin.sql',\n description: 'Migration 007: Demo Login Plugin',\n sql: \"-- Demo Login Plugin Migration\\n-- Migration: 007_demo_login_plugin\\n-- Description: Add demo login prefill plugin to the plugin registry\\n\\n-- Insert demo login plugin\\nINSERT INTO plugins (\\n id, name, display_name, description, version, author, category, icon, \\n status, is_core, permissions, installed_at, last_updated\\n) VALUES (\\n 'demo-login-prefill',\\n 'demo-login-plugin',\\n 'Demo Login Prefill',\\n 'Prefills login form with demo credentials (admin@sonicjs.com/sonicjs!) for easy site demonstration',\\n '1.0.0',\\n 'SonicJS',\\n 'demo',\\n '🎯',\\n 'inactive',\\n TRUE,\\n '[]',\\n unixepoch(),\\n unixepoch()\\n);\"\n },\n {\n id: '008',\n name: 'Fix Slug Validation',\n filename: '008_fix_slug_validation.sql',\n description: 'Migration 008: Fix Slug Validation',\n sql: \"-- Migration: Fix overly restrictive slug validation patterns\\n-- This migration relaxes the slug field validation to be more user-friendly\\n\\n-- Update the pages collection slug field to allow underscores and be less restrictive\\nUPDATE content_fields \\nSET field_options = '{\\\"pattern\\\": \\\"^[a-zA-Z0-9_-]+$\\\", \\\"placeholder\\\": \\\"url-friendly-slug\\\", \\\"help\\\": \\\"Use letters, numbers, underscores, and hyphens only\\\"}'\\nWHERE field_name = 'slug' AND collection_id = 'pages-collection';\\n\\n-- Update blog posts slug field if it exists\\nUPDATE content_fields \\nSET field_options = '{\\\"pattern\\\": \\\"^[a-zA-Z0-9_-]+$\\\", \\\"placeholder\\\": \\\"url-friendly-slug\\\", \\\"help\\\": \\\"Use letters, numbers, underscores, and hyphens only\\\"}'\\nWHERE field_name = 'slug' AND collection_id = 'blog-posts-collection';\\n\\n-- Update news slug field if it exists\\nUPDATE content_fields \\nSET field_options = '{\\\"pattern\\\": \\\"^[a-zA-Z0-9_-]+$\\\", \\\"placeholder\\\": \\\"url-friendly-slug\\\", \\\"help\\\": \\\"Use letters, numbers, underscores, and hyphens only\\\"}'\\nWHERE field_name = 'slug' AND collection_id = 'news-collection';\\n\\n-- Update any other slug fields with the restrictive pattern\\nUPDATE content_fields \\nSET field_options = REPLACE(field_options, '\\\"pattern\\\": \\\"^[a-z0-9-]+$\\\"', '\\\"pattern\\\": \\\"^[a-zA-Z0-9_-]+$\\\"')\\nWHERE field_options LIKE '%\\\"pattern\\\": \\\"^[a-z0-9-]+$\\\"%';\"\n },\n {\n id: '009',\n name: 'System Logging',\n filename: '009_system_logging.sql',\n description: 'Migration 009: System Logging',\n sql: \"-- System Logging Tables\\n-- Migration: 009_system_logging\\n-- Description: Add system logging and configuration tables\\n\\n-- System logs table for tracking application events\\nCREATE TABLE IF NOT EXISTS system_logs (\\n id TEXT PRIMARY KEY,\\n level TEXT NOT NULL CHECK (level IN ('debug', 'info', 'warn', 'error', 'fatal')),\\n category TEXT NOT NULL CHECK (category IN ('auth', 'api', 'workflow', 'plugin', 'media', 'system', 'security', 'error')),\\n message TEXT NOT NULL,\\n data TEXT, -- JSON data\\n user_id TEXT,\\n session_id TEXT,\\n request_id TEXT,\\n ip_address TEXT,\\n user_agent TEXT,\\n method TEXT,\\n url TEXT,\\n status_code INTEGER,\\n duration INTEGER, -- milliseconds\\n stack_trace TEXT,\\n tags TEXT, -- JSON array\\n source TEXT, -- source of the log entry\\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\\n FOREIGN KEY (user_id) REFERENCES users(id)\\n);\\n\\n-- Log configuration table for managing log settings per category\\nCREATE TABLE IF NOT EXISTS log_config (\\n id TEXT PRIMARY KEY,\\n category TEXT NOT NULL UNIQUE CHECK (category IN ('auth', 'api', 'workflow', 'plugin', 'media', 'system', 'security', 'error')),\\n enabled BOOLEAN NOT NULL DEFAULT TRUE,\\n level TEXT NOT NULL DEFAULT 'info' CHECK (level IN ('debug', 'info', 'warn', 'error', 'fatal')),\\n retention_days INTEGER NOT NULL DEFAULT 30,\\n max_size_mb INTEGER NOT NULL DEFAULT 100,\\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\\n);\\n\\n-- Create indexes for better performance\\nCREATE INDEX IF NOT EXISTS idx_system_logs_level ON system_logs(level);\\nCREATE INDEX IF NOT EXISTS idx_system_logs_category ON system_logs(category);\\nCREATE INDEX IF NOT EXISTS idx_system_logs_created_at ON system_logs(created_at);\\nCREATE INDEX IF NOT EXISTS idx_system_logs_user_id ON system_logs(user_id);\\nCREATE INDEX IF NOT EXISTS idx_system_logs_status_code ON system_logs(status_code);\\nCREATE INDEX IF NOT EXISTS idx_system_logs_source ON system_logs(source);\\n\\n-- Insert default log configurations\\nINSERT OR IGNORE INTO log_config (id, category, enabled, level, retention_days, max_size_mb) VALUES\\n('log-config-auth', 'auth', TRUE, 'info', 90, 50),\\n('log-config-api', 'api', TRUE, 'info', 30, 100),\\n('log-config-workflow', 'workflow', TRUE, 'info', 60, 50),\\n('log-config-plugin', 'plugin', TRUE, 'warn', 30, 25),\\n('log-config-media', 'media', TRUE, 'info', 30, 50),\\n('log-config-system', 'system', TRUE, 'info', 90, 100),\\n('log-config-security', 'security', TRUE, 'warn', 180, 100),\\n('log-config-error', 'error', TRUE, 'error', 90, 200);\"\n },\n {\n id: '011',\n name: 'Config Managed Collections',\n filename: '011_config_managed_collections.sql',\n description: 'Migration 011: Config Managed Collections',\n sql: \"-- Migration: Add Config-Managed Collections Support\\n-- Description: Add 'managed' column to collections table to support config-based collection definitions\\n-- Created: 2025-10-03\\n\\n-- Add 'managed' column to collections table\\n-- This column indicates whether a collection is managed by configuration files (true) or user-created (false)\\n-- Managed collections cannot be edited through the admin UI\\n-- Use a safe approach to add the column only if it doesn't exist\\nALTER TABLE collections ADD COLUMN managed INTEGER DEFAULT 0 NOT NULL;\\n\\n-- Create an index on the managed column for faster queries\\nCREATE INDEX IF NOT EXISTS idx_collections_managed ON collections(managed);\\n\\n-- Create an index on managed + is_active for efficient filtering\\nCREATE INDEX IF NOT EXISTS idx_collections_managed_active ON collections(managed, is_active);\\n\"\n },\n {\n id: '012',\n name: 'Testimonials Plugin',\n filename: '012_testimonials_plugin.sql',\n description: 'Migration 012: Testimonials Plugin',\n sql: \"-- Testimonials Plugin Migration\\n-- Creates testimonials table for the testimonials plugin\\n-- This demonstrates a code-based collection defined in src/plugins/core-plugins/testimonials-plugin.ts\\n\\nCREATE TABLE IF NOT EXISTS testimonials (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n author_name TEXT NOT NULL,\\n author_title TEXT,\\n author_company TEXT,\\n testimonial_text TEXT NOT NULL,\\n rating INTEGER CHECK(rating >= 1 AND rating <= 5),\\n isPublished INTEGER NOT NULL DEFAULT 1,\\n sortOrder INTEGER NOT NULL DEFAULT 0,\\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\\n);\\n\\n-- Create indexes for better performance\\nCREATE INDEX IF NOT EXISTS idx_testimonials_published ON testimonials(isPublished);\\nCREATE INDEX IF NOT EXISTS idx_testimonials_sort_order ON testimonials(sortOrder);\\nCREATE INDEX IF NOT EXISTS idx_testimonials_rating ON testimonials(rating);\\n\\n-- Create trigger to update updated_at timestamp\\nCREATE TRIGGER IF NOT EXISTS testimonials_updated_at\\n AFTER UPDATE ON testimonials\\nBEGIN\\n UPDATE testimonials SET updated_at = strftime('%s', 'now') WHERE id = NEW.id;\\nEND;\\n\\n-- Insert plugin record\\nINSERT OR IGNORE INTO plugins (name, display_name, description, version, status, category, settings) VALUES\\n('testimonials',\\n 'Customer Testimonials',\\n 'Manage customer testimonials and reviews with rating support. This is a code-based collection example.',\\n '1.0.0',\\n 'active',\\n 'content',\\n '{\\\"defaultPublished\\\": true, \\\"requireRating\\\": false}');\\n\\n-- Insert sample testimonial data\\nINSERT OR IGNORE INTO testimonials (author_name, author_title, author_company, testimonial_text, rating, isPublished, sortOrder) VALUES\\n('Jane Smith',\\n 'CTO',\\n 'TechStartup Inc',\\n 'SonicJS AI has transformed how we manage our content. The plugin architecture is brilliant and the edge deployment is blazing fast.',\\n 5,\\n 1,\\n 1),\\n\\n('Michael Chen',\\n 'Lead Developer',\\n 'Digital Agency Co',\\n 'We migrated from WordPress to SonicJS AI and couldn''t be happier. The TypeScript-first approach and modern tooling make development a joy.',\\n 5,\\n 1,\\n 2),\\n\\n('Sarah Johnson',\\n 'Product Manager',\\n 'E-commerce Solutions',\\n 'The headless CMS approach combined with Cloudflare Workers gives us unmatched performance. Our content is served globally with minimal latency.',\\n 4,\\n 1,\\n 3),\\n\\n('David Rodriguez',\\n 'Full Stack Developer',\\n 'Creative Studio',\\n 'Great CMS for modern web applications. The admin interface is clean and the API is well-designed. Plugin system is very flexible.',\\n 5,\\n 1,\\n 4),\\n\\n('Emily Watson',\\n 'Technical Director',\\n 'Media Company',\\n 'SonicJS AI solved our content distribution challenges. The R2 integration for media storage works flawlessly and scales effortlessly.',\\n 4,\\n 1,\\n 5);\\n\"\n },\n {\n id: '013',\n name: 'Code Examples Plugin',\n filename: '013_code_examples_plugin.sql',\n description: 'Migration 013: Code Examples Plugin',\n sql: \"-- Code Examples Plugin Migration\\n-- Creates code_examples table for the code examples plugin\\n-- This demonstrates a code-based collection for storing and managing code snippets\\n\\nCREATE TABLE IF NOT EXISTS code_examples (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n title TEXT NOT NULL,\\n description TEXT,\\n code TEXT NOT NULL,\\n language TEXT NOT NULL,\\n category TEXT,\\n tags TEXT,\\n isPublished INTEGER NOT NULL DEFAULT 1,\\n sortOrder INTEGER NOT NULL DEFAULT 0,\\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\\n);\\n\\n-- Create indexes for better performance\\nCREATE INDEX IF NOT EXISTS idx_code_examples_published ON code_examples(isPublished);\\nCREATE INDEX IF NOT EXISTS idx_code_examples_sort_order ON code_examples(sortOrder);\\nCREATE INDEX IF NOT EXISTS idx_code_examples_language ON code_examples(language);\\nCREATE INDEX IF NOT EXISTS idx_code_examples_category ON code_examples(category);\\n\\n-- Create trigger to update updated_at timestamp\\nCREATE TRIGGER IF NOT EXISTS code_examples_updated_at\\n AFTER UPDATE ON code_examples\\nBEGIN\\n UPDATE code_examples SET updated_at = strftime('%s', 'now') WHERE id = NEW.id;\\nEND;\\n\\n-- Insert plugin record\\nINSERT OR IGNORE INTO plugins (name, display_name, description, version, status, category, settings) VALUES\\n('code-examples',\\n 'Code Examples',\\n 'Manage code snippets and examples with syntax highlighting support. Perfect for documentation and tutorials.',\\n '1.0.0',\\n 'active',\\n 'content',\\n '{\\\"defaultPublished\\\": true, \\\"supportedLanguages\\\": [\\\"javascript\\\", \\\"typescript\\\", \\\"python\\\", \\\"go\\\", \\\"rust\\\", \\\"java\\\", \\\"php\\\", \\\"ruby\\\", \\\"sql\\\"]}');\\n\\n-- Insert sample code examples\\nINSERT OR IGNORE INTO code_examples (title, description, code, language, category, tags, isPublished, sortOrder) VALUES\\n('React useState Hook',\\n 'Basic example of using the useState hook in React for managing component state.',\\n 'import { useState } from ''react'';\\n\\nfunction Counter() {\\n const [count, setCount] = useState(0);\\n\\n return (\\n <div>\\n <p>Count: {count}</p>\\n <button onClick={() => setCount(count + 1)}>\\n Increment\\n </button>\\n </div>\\n );\\n}\\n\\nexport default Counter;',\\n 'javascript',\\n 'frontend',\\n 'react,hooks,state',\\n 1,\\n 1),\\n\\n('TypeScript Interface Example',\\n 'Defining a TypeScript interface for type-safe objects.',\\n 'interface User {\\n id: string;\\n email: string;\\n name: string;\\n role: ''admin'' | ''editor'' | ''viewer'';\\n createdAt: Date;\\n}\\n\\nfunction greetUser(user: User): string {\\n return `Hello, ${user.name}!`;\\n}\\n\\nconst user: User = {\\n id: ''123'',\\n email: ''user@example.com'',\\n name: ''John Doe'',\\n role: ''admin'',\\n createdAt: new Date()\\n};\\n\\nconsole.log(greetUser(user));',\\n 'typescript',\\n 'backend',\\n 'typescript,types,interface',\\n 1,\\n 2),\\n\\n('Python List Comprehension',\\n 'Elegant way to create lists in Python using list comprehensions.',\\n '# Basic list comprehension\\nsquares = [x**2 for x in range(10)]\\nprint(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\\n\\n# With condition\\neven_squares = [x**2 for x in range(10) if x % 2 == 0]\\nprint(even_squares) # [0, 4, 16, 36, 64]\\n\\n# Nested list comprehension\\nmatrix = [[i+j for j in range(3)] for i in range(3)]\\nprint(matrix) # [[0, 1, 2], [1, 2, 3], [2, 3, 4]]',\\n 'python',\\n 'general',\\n 'python,lists,comprehension',\\n 1,\\n 3),\\n\\n('SQL Join Example',\\n 'Common SQL JOIN patterns for combining data from multiple tables.',\\n '-- INNER JOIN: Returns only matching rows\\nSELECT users.name, orders.total\\nFROM users\\nINNER JOIN orders ON users.id = orders.user_id;\\n\\n-- LEFT JOIN: Returns all users, even without orders\\nSELECT users.name, orders.total\\nFROM users\\nLEFT JOIN orders ON users.id = orders.user_id;\\n\\n-- Multiple JOINs\\nSELECT\\n users.name,\\n orders.order_date,\\n products.name AS product_name\\nFROM users\\nINNER JOIN orders ON users.id = orders.user_id\\nINNER JOIN order_items ON orders.id = order_items.order_id\\nINNER JOIN products ON order_items.product_id = products.id;',\\n 'sql',\\n 'database',\\n 'sql,joins,queries',\\n 1,\\n 4),\\n\\n('Go Error Handling',\\n 'Idiomatic error handling pattern in Go.',\\n 'package main\\n\\nimport (\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n)\\n\\nfunc divide(a, b float64) (float64, error) {\\n\\tif b == 0 {\\n\\t\\treturn 0, errors.New(\\\"division by zero\\\")\\n\\t}\\n\\treturn a / b, nil\\n}\\n\\nfunc main() {\\n\\tresult, err := divide(10, 2)\\n\\tif err != nil {\\n\\t\\tfmt.Println(\\\"Error:\\\", err)\\n\\t\\treturn\\n\\t}\\n\\tfmt.Printf(\\\"Result: %.2f\\\\n\\\", result)\\n\\n\\t// This will error\\n\\t_, err = divide(10, 0)\\n\\tif err != nil {\\n\\t\\tfmt.Println(\\\"Error:\\\", err)\\n\\t}\\n}',\\n 'go',\\n 'backend',\\n 'go,error-handling,functions',\\n 1,\\n 5);\\n\"\n },\n {\n id: '014',\n name: 'Fix Plugin Registry',\n filename: '014_fix_plugin_registry.sql',\n description: 'Migration 014: Fix Plugin Registry',\n sql: \"-- Fix Plugin Registry\\n-- Migration: 014_fix_plugin_registry\\n-- Description: Add missing plugins and fix plugin name mismatches\\n\\n-- Note: Cannot easily update plugin names as they may be referenced elsewhere\\n-- Instead we'll add the missing plugins with correct names\\n\\n-- Insert missing plugins\\n\\n-- Core Analytics Plugin\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, installed_at, last_updated\\n) VALUES (\\n 'core-analytics',\\n 'core-analytics',\\n 'Analytics & Tracking',\\n 'Core analytics tracking and reporting plugin with page views and event tracking',\\n '1.0.0',\\n 'SonicJS Team',\\n 'seo',\\n '📊',\\n 'active',\\n TRUE,\\n '[\\\"view:analytics\\\", \\\"manage:tracking\\\"]',\\n unixepoch(),\\n unixepoch()\\n);\\n\\n-- FAQ Plugin\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, installed_at, last_updated\\n) VALUES (\\n 'faq-plugin',\\n 'faq-plugin',\\n 'FAQ Management',\\n 'Frequently Asked Questions management plugin with categories, search, and custom styling',\\n '1.0.0',\\n 'SonicJS',\\n 'content',\\n '❓',\\n 'active',\\n FALSE,\\n '[\\\"manage:faqs\\\"]',\\n unixepoch(),\\n unixepoch()\\n);\\n\\n-- Seed Data Plugin\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, installed_at, last_updated\\n) VALUES (\\n 'seed-data',\\n 'seed-data',\\n 'Seed Data Generator',\\n 'Generate realistic example users and content for testing and development',\\n '1.0.0',\\n 'SonicJS Team',\\n 'development',\\n '🌱',\\n 'inactive',\\n FALSE,\\n '[\\\"admin\\\"]',\\n unixepoch(),\\n unixepoch()\\n);\\n\\n-- Database Tools Plugin\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, installed_at, last_updated\\n) VALUES (\\n 'database-tools',\\n 'database-tools',\\n 'Database Tools',\\n 'Database management tools including truncate, backup, and validation',\\n '1.0.0',\\n 'SonicJS Team',\\n 'system',\\n '🗄️',\\n 'active',\\n FALSE,\\n '[\\\"manage:database\\\", \\\"admin\\\"]',\\n unixepoch(),\\n unixepoch()\\n);\\n\"\n },\n {\n id: '015',\n name: 'Add Remaining Plugins',\n filename: '015_add_remaining_plugins.sql',\n description: 'Migration 015: Add Remaining Plugins',\n sql: \"-- Add Remaining Plugins\\n-- Migration: 015_add_remaining_plugins\\n-- Description: Add all remaining core plugins that were missing from the registry\\n\\n-- Testimonials Plugin (with correct name)\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, dependencies, settings, installed_at, last_updated\\n) VALUES (\\n 'testimonials-plugin',\\n 'testimonials-plugin',\\n 'Customer Testimonials',\\n 'Manage customer testimonials and reviews with rating support',\\n '1.0.0',\\n 'SonicJS',\\n 'content',\\n '⭐',\\n 'active',\\n FALSE,\\n '[\\\"manage:testimonials\\\"]',\\n '[]',\\n '{\\\"defaultPublished\\\": true, \\\"requireRating\\\": false}',\\n unixepoch(),\\n unixepoch()\\n);\\n\\n-- Code Examples Plugin (with correct name)\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, dependencies, settings, installed_at, last_updated\\n) VALUES (\\n 'code-examples-plugin',\\n 'code-examples-plugin',\\n 'Code Examples',\\n 'Manage code snippets and examples with syntax highlighting support',\\n '1.0.0',\\n 'SonicJS',\\n 'content',\\n '💻',\\n 'active',\\n FALSE,\\n '[\\\"manage:code-examples\\\"]',\\n '[]',\\n '{\\\"defaultPublished\\\": true, \\\"supportedLanguages\\\": [\\\"javascript\\\", \\\"typescript\\\", \\\"python\\\", \\\"go\\\", \\\"rust\\\", \\\"java\\\", \\\"php\\\", \\\"ruby\\\", \\\"sql\\\"]}',\\n unixepoch(),\\n unixepoch()\\n);\\n\\n-- Workflow Plugin (with correct name)\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, dependencies, installed_at, last_updated\\n) VALUES (\\n 'workflow-plugin',\\n 'workflow-plugin',\\n 'Workflow Engine',\\n 'Content workflow management with approval chains, scheduling, and automation',\\n '1.0.0',\\n 'SonicJS Team',\\n 'content',\\n '🔄',\\n 'active',\\n TRUE,\\n '[\\\"manage:workflows\\\", \\\"approve:content\\\"]',\\n '[]',\\n unixepoch(),\\n unixepoch()\\n);\\n\\n-- Demo Login Plugin (already exists with correct name from migration 007, but let's ensure it's there)\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, dependencies, installed_at, last_updated\\n) VALUES (\\n 'demo-login-plugin',\\n 'demo-login-plugin',\\n 'Demo Login Prefill',\\n 'Prefills login form with demo credentials for easy site demonstration',\\n '1.0.0',\\n 'SonicJS',\\n 'demo',\\n '🎯',\\n 'active',\\n FALSE,\\n '[]',\\n '[]',\\n unixepoch(),\\n unixepoch()\\n);\\n\"\n },\n {\n id: '016',\n name: 'Remove Duplicate Cache Plugin',\n filename: '016_remove_duplicate_cache_plugin.sql',\n description: 'Migration 016: Remove Duplicate Cache Plugin',\n sql: \"-- Migration: Remove duplicate cache plugin entry\\n-- Description: Removes the old 'cache' plugin (id: 'cache') that is a duplicate of 'core-cache'\\n-- This fixes the issue where Cache System appears twice in the plugins list\\n-- Created: 2025-10-14\\n\\n-- Remove the old 'cache' plugin entry if it exists\\n-- The correct plugin is 'core-cache' which is managed by plugin-bootstrap.ts\\nDELETE FROM plugins WHERE id = 'cache' AND name = 'cache';\\n\\n-- Clean up any related entries in plugin activity log\\nDELETE FROM plugin_activity_log WHERE plugin_id = 'cache';\\n\\n-- Clean up any related entries in plugin hooks\\nDELETE FROM plugin_hooks WHERE plugin_id = 'cache';\\n\\n-- Clean up any related entries in plugin routes\\nDELETE FROM plugin_routes WHERE plugin_id = 'cache';\\n\"\n },\n {\n id: '017',\n name: 'Auth Configurable Fields',\n filename: '017_auth_configurable_fields.sql',\n description: 'Migration 017: Auth Configurable Fields',\n sql: \"-- Migration: Make authentication fields configurable\\n-- This migration updates the core-auth plugin to support configurable required fields\\n\\n-- The settings will be stored in the plugins table as JSON\\n-- Default settings for core-auth plugin include:\\n-- {\\n-- \\\"requiredFields\\\": {\\n-- \\\"email\\\": { \\\"required\\\": true, \\\"minLength\\\": 5 },\\n-- \\\"password\\\": { \\\"required\\\": true, \\\"minLength\\\": 8 },\\n-- \\\"username\\\": { \\\"required\\\": true, \\\"minLength\\\": 3 },\\n-- \\\"firstName\\\": { \\\"required\\\": true, \\\"minLength\\\": 1 },\\n-- \\\"lastName\\\": { \\\"required\\\": true, \\\"minLength\\\": 1 }\\n-- },\\n-- \\\"validation\\\": {\\n-- \\\"emailFormat\\\": true,\\n-- \\\"allowDuplicateUsernames\\\": false\\n-- }\\n-- }\\n\\n-- Update core-auth plugin settings with configurable field requirements\\nUPDATE plugins\\nSET settings = json_object(\\n 'requiredFields', json_object(\\n 'email', json_object('required', 1, 'minLength', 5, 'label', 'Email', 'type', 'email'),\\n 'password', json_object('required', 1, 'minLength', 8, 'label', 'Password', 'type', 'password'),\\n 'username', json_object('required', 1, 'minLength', 3, 'label', 'Username', 'type', 'text'),\\n 'firstName', json_object('required', 1, 'minLength', 1, 'label', 'First Name', 'type', 'text'),\\n 'lastName', json_object('required', 1, 'minLength', 1, 'label', 'Last Name', 'type', 'text')\\n ),\\n 'validation', json_object(\\n 'emailFormat', 1,\\n 'allowDuplicateUsernames', 0,\\n 'passwordRequirements', json_object(\\n 'requireUppercase', 0,\\n 'requireLowercase', 0,\\n 'requireNumbers', 0,\\n 'requireSpecialChars', 0\\n )\\n ),\\n 'registration', json_object(\\n 'enabled', 1,\\n 'requireEmailVerification', 0,\\n 'defaultRole', 'viewer'\\n )\\n)\\nWHERE id = 'core-auth';\\n\\n-- If core-auth plugin doesn't exist, this migration will be handled by bootstrap\\n-- No need to insert here as plugin bootstrap handles initial plugin creation\\n\"\n },\n {\n id: '018',\n name: 'Settings Table',\n filename: '018_settings_table.sql',\n description: 'Migration 018: Settings Table',\n sql: \"-- Create settings table for storing application settings\\nCREATE TABLE IF NOT EXISTS settings (\\n id TEXT PRIMARY KEY,\\n category TEXT NOT NULL, -- 'general', 'appearance', 'security', etc.\\n key TEXT NOT NULL,\\n value TEXT NOT NULL, -- JSON value\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL,\\n UNIQUE(category, key)\\n);\\n\\n-- Insert default general settings\\nINSERT OR IGNORE INTO settings (id, category, key, value, created_at, updated_at)\\nVALUES\\n (lower(hex(randomblob(16))), 'general', 'siteName', '\\\"SonicJS AI\\\"', unixepoch() * 1000, unixepoch() * 1000),\\n (lower(hex(randomblob(16))), 'general', 'siteDescription', '\\\"A modern headless CMS powered by AI\\\"', unixepoch() * 1000, unixepoch() * 1000),\\n (lower(hex(randomblob(16))), 'general', 'timezone', '\\\"UTC\\\"', unixepoch() * 1000, unixepoch() * 1000),\\n (lower(hex(randomblob(16))), 'general', 'language', '\\\"en\\\"', unixepoch() * 1000, unixepoch() * 1000),\\n (lower(hex(randomblob(16))), 'general', 'maintenanceMode', 'false', unixepoch() * 1000, unixepoch() * 1000);\\n\\n-- Create index for faster lookups\\nCREATE INDEX IF NOT EXISTS idx_settings_category ON settings(category);\\nCREATE INDEX IF NOT EXISTS idx_settings_category_key ON settings(category, key);\\n\"\n },\n {\n id: '019',\n name: 'Remove Blog Posts Collection',\n filename: '019_remove_blog_posts_collection.sql',\n description: 'Migration 019: Remove Blog Posts Collection',\n sql: \"-- Migration: Remove blog_posts from database-managed collections\\n-- Description: Remove blog-posts-collection from the database so it can be managed by code-based collection\\n-- Created: 2025-11-04\\n\\n-- Delete content associated with blog-posts-collection\\nDELETE FROM content WHERE collection_id = 'blog-posts-collection';\\n\\n-- Delete content fields for blog-posts-collection\\nDELETE FROM content_fields WHERE collection_id = 'blog-posts-collection';\\n\\n-- Delete the blog-posts collection itself\\nDELETE FROM collections WHERE id = 'blog-posts-collection';\\n\\n-- The blog-posts collection will now be managed by the code-based collection\\n-- in src/collections/blog-posts.collection.ts\\n\"\n },\n {\n id: '020',\n name: 'Add Email Plugin',\n filename: '020_add_email_plugin.sql',\n description: 'Migration 020: Add Email Plugin',\n sql: \"-- Add Email Plugin\\n-- Migration: 020_add_email_plugin\\n-- Description: Add email plugin for transactional emails via Resend\\n\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, installed_at, last_updated\\n) VALUES (\\n 'email',\\n 'email',\\n 'Email',\\n 'Send transactional emails using Resend',\\n '1.0.0-beta.1',\\n 'SonicJS Team',\\n 'utilities',\\n '📧',\\n 'inactive',\\n TRUE,\\n '[\\\"email:manage\\\", \\\"email:send\\\", \\\"email:view-logs\\\"]',\\n unixepoch(),\\n unixepoch()\\n);\\n\"\n },\n {\n id: '021',\n name: 'Add Magic Link Auth Plugin',\n filename: '021_add_magic_link_auth_plugin.sql',\n description: 'Migration 021: Add Magic Link Auth Plugin',\n sql: \"-- Add Magic Link Authentication Plugin\\n-- Migration: 021_add_magic_link_auth_plugin\\n-- Description: Add magic link authentication plugin for passwordless login\\n\\n-- Create magic_links table\\nCREATE TABLE IF NOT EXISTS magic_links (\\n id TEXT PRIMARY KEY,\\n user_email TEXT NOT NULL,\\n token TEXT NOT NULL UNIQUE,\\n expires_at INTEGER NOT NULL,\\n used INTEGER DEFAULT 0,\\n used_at INTEGER,\\n ip_address TEXT,\\n user_agent TEXT,\\n created_at INTEGER NOT NULL\\n);\\n\\n-- Create indexes for performance\\nCREATE INDEX IF NOT EXISTS idx_magic_links_token ON magic_links(token);\\nCREATE INDEX IF NOT EXISTS idx_magic_links_email ON magic_links(user_email);\\nCREATE INDEX IF NOT EXISTS idx_magic_links_expires ON magic_links(expires_at);\\n\\n-- Register the plugin\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, dependencies, installed_at, last_updated\\n) VALUES (\\n 'magic-link-auth',\\n 'magic-link-auth',\\n 'Magic Link Authentication',\\n 'Passwordless authentication via email magic links. Users receive a secure one-time link to sign in without entering a password.',\\n '1.0.0',\\n 'SonicJS Team',\\n 'security',\\n '🔗',\\n 'inactive',\\n FALSE,\\n '[\\\"auth:manage\\\", \\\"auth:magic-link\\\"]',\\n '[\\\"email\\\"]',\\n unixepoch(),\\n unixepoch()\\n);\\n\"\n },\n {\n id: '022',\n name: 'Add Tinymce Plugin',\n filename: '022_add_tinymce_plugin.sql',\n description: 'Migration 022: Add Tinymce Plugin',\n sql: \"-- Add TinyMCE Rich Text Editor Plugin\\n-- Migration: 022_add_tinymce_plugin\\n-- Description: Add TinyMCE plugin for WYSIWYG rich text editing\\n\\n-- Register the plugin (active by default since it replaces hardcoded TinyMCE)\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, dependencies, settings, installed_at, last_updated\\n) VALUES (\\n 'tinymce-plugin',\\n 'tinymce-plugin',\\n 'TinyMCE Rich Text Editor',\\n 'Powerful WYSIWYG rich text editor for content creation. Provides a full-featured editor with formatting, media embedding, and customizable toolbars for richtext fields.',\\n '1.0.0',\\n 'SonicJS Team',\\n 'editor',\\n '✏️',\\n 'active',\\n FALSE,\\n '[]',\\n '[]',\\n '{\\\"apiKey\\\":\\\"no-api-key\\\",\\\"defaultHeight\\\":300,\\\"defaultToolbar\\\":\\\"full\\\",\\\"skin\\\":\\\"oxide-dark\\\"}',\\n unixepoch(),\\n unixepoch()\\n);\\n\"\n },\n {\n id: '023',\n name: 'Add Easy Mdx Plugin',\n filename: '023_add_easy_mdx_plugin.sql',\n description: 'Migration 023: Add Easy Mdx Plugin',\n sql: \"-- Add EasyMDE Markdown Editor Plugin\\n-- Migration: 023_add_easy_mdx_plugin\\n-- Description: Add EasyMDE plugin for lightweight markdown editing\\n\\n-- Register the plugin (active by default)\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, dependencies, settings, installed_at, last_updated\\n) VALUES (\\n 'easy-mdx',\\n 'easy-mdx',\\n 'EasyMDE Markdown Editor',\\n 'Lightweight markdown editor with live preview. Provides a simple and efficient editor with markdown support for richtext fields.',\\n '1.0.0',\\n 'SonicJS Team',\\n 'editor',\\n '📝',\\n 'active',\\n FALSE,\\n '[]',\\n '[]',\\n '{\\\"defaultHeight\\\":400,\\\"theme\\\":\\\"dark\\\",\\\"toolbar\\\":\\\"full\\\",\\\"placeholder\\\":\\\"Start writing your content...\\\"}',\\n unixepoch(),\\n unixepoch()\\n);\\n\"\n },\n {\n id: '024',\n name: 'Add Quill Editor Plugin',\n filename: '024_add_quill_editor_plugin.sql',\n description: 'Migration 024: Add Quill Editor Plugin',\n sql: \"-- Add Quill Rich Text Editor Plugin\\n-- Migration: 024_add_quill_editor_plugin\\n-- Description: Add Quill plugin for modern rich text editing\\n\\n-- Register the plugin (active by default)\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, dependencies, settings, installed_at, last_updated\\n) VALUES (\\n 'quill-editor',\\n 'quill-editor',\\n 'Quill Rich Text Editor',\\n 'Modern rich text editor for content creation. Provides a clean, intuitive WYSIWYG editor with customizable toolbars for richtext fields.',\\n '1.0.0',\\n 'SonicJS Team',\\n 'editor',\\n '✍️',\\n 'active',\\n FALSE,\\n '[]',\\n '[]',\\n '{\\\"theme\\\":\\\"snow\\\",\\\"defaultHeight\\\":300,\\\"defaultToolbar\\\":\\\"full\\\",\\\"placeholder\\\":\\\"Start writing your content...\\\"}',\\n unixepoch(),\\n unixepoch()\\n);\\n\"\n },\n {\n id: '025',\n name: 'Add Easymde Plugin',\n filename: '025_add_easymde_plugin.sql',\n description: 'Migration 025: Add Easymde Plugin',\n sql: \"-- Add EasyMDE Rich Text Editor Plugin\\n-- Migration: 025_add_easymde_plugin\\n-- Description: Add EasyMDE plugin for markdown-based rich text editing\\n\\n-- Register the plugin (inactive by default, replacing MDXEditor)\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, dependencies, settings, installed_at, last_updated\\n) VALUES (\\n 'easymde-editor',\\n 'easymde-editor',\\n 'EasyMDE Editor',\\n 'Lightweight markdown editor for content creation. Simple, elegant WYSIWYG markdown editor with live preview, toolbar, and dark mode support for richtext fields.',\\n '1.0.0',\\n 'SonicJS Team',\\n 'editor',\\n '✍️',\\n 'inactive',\\n TRUE,\\n '[]',\\n '[]',\\n '{\\\"theme\\\":\\\"dark\\\",\\\"defaultHeight\\\":300,\\\"toolbar\\\":\\\"full\\\",\\\"spellChecker\\\":false,\\\"placeholder\\\":\\\"Start writing your content...\\\"}',\\n unixepoch(),\\n unixepoch()\\n);\\n\"\n },\n {\n id: '026',\n name: 'Add Otp Login',\n filename: '026_add_otp_login.sql',\n description: 'Migration 026: Add Otp Login',\n sql: \"-- Add OTP Login Plugin\\n-- Migration: 021_add_otp_login\\n-- Description: Add OTP login plugin for passwordless authentication via email codes\\n\\n-- Create table for OTP codes\\nCREATE TABLE IF NOT EXISTS otp_codes (\\n id TEXT PRIMARY KEY,\\n user_email TEXT NOT NULL,\\n code TEXT NOT NULL,\\n expires_at INTEGER NOT NULL,\\n used INTEGER DEFAULT 0,\\n used_at INTEGER,\\n ip_address TEXT,\\n user_agent TEXT,\\n attempts INTEGER DEFAULT 0,\\n created_at INTEGER NOT NULL\\n);\\n\\n-- Create indexes for performance\\nCREATE INDEX IF NOT EXISTS idx_otp_email_code ON otp_codes(user_email, code);\\nCREATE INDEX IF NOT EXISTS idx_otp_expires ON otp_codes(expires_at);\\nCREATE INDEX IF NOT EXISTS idx_otp_used ON otp_codes(used);\\n\\n-- Add plugin record\\nINSERT OR IGNORE INTO plugins (\\n id, name, display_name, description, version, author, category, icon,\\n status, is_core, permissions, installed_at, last_updated\\n) VALUES (\\n 'otp-login',\\n 'otp-login',\\n 'OTP Login',\\n 'Passwordless authentication via email one-time codes',\\n '1.0.0-beta.1',\\n 'SonicJS Team',\\n 'security',\\n '🔢',\\n 'inactive',\\n TRUE,\\n '[\\\"otp:manage\\\", \\\"otp:request\\\", \\\"otp:verify\\\"]',\\n unixepoch(),\\n unixepoch()\\n);\\n\"\n },\n {\n id: '027',\n name: 'Fix Slug Field Type',\n filename: '027_fix_slug_field_type.sql',\n description: 'Migration 027: Fix Slug Field Type',\n sql: \"-- Migration: Fix slug field type\\n-- Description: Update slug fields to use 'slug' field type instead of 'text' for proper auto-generation\\n-- Created: 2026-01-10\\n\\n-- Update pages collection slug field to use 'slug' field type\\nUPDATE content_fields \\nSET field_type = 'slug'\\nWHERE field_name = 'slug' AND collection_id = 'pages-collection';\\n\\n-- Update blog posts slug field if it exists\\nUPDATE content_fields \\nSET field_type = 'slug'\\nWHERE field_name = 'slug' AND collection_id = 'blog-posts-collection';\\n\\n-- Update news slug field if it exists\\nUPDATE content_fields \\nSET field_type = 'slug'\\nWHERE field_name = 'slug' AND collection_id = 'news-collection';\\n\"\n },\n {\n id: '028',\n name: 'Fix Slug Field Type In Schemas',\n filename: '028_fix_slug_field_type_in_schemas.sql',\n description: 'Migration 028: Fix Slug Field Type In Schemas',\n sql: \"-- Migration: Fix slug field type in collection schemas\\n-- Description: Update slug fields in collection schemas to use 'slug' type instead of 'string'\\n-- Created: 2026-01-10\\n\\n-- Update pages-collection schema\\nUPDATE collections \\nSET schema = REPLACE(\\n schema,\\n '\\\"slug\\\":{\\\"type\\\":\\\"string\\\"',\\n '\\\"slug\\\":{\\\"type\\\":\\\"slug\\\"'\\n)\\nWHERE id = 'pages-collection' AND schema LIKE '%\\\"slug\\\":{\\\"type\\\":\\\"string\\\"%';\\n\\n-- Update blog-posts-collection schema if it exists\\nUPDATE collections \\nSET schema = REPLACE(\\n schema,\\n '\\\"slug\\\":{\\\"type\\\":\\\"string\\\"',\\n '\\\"slug\\\":{\\\"type\\\":\\\"slug\\\"'\\n)\\nWHERE id = 'blog-posts-collection' AND schema LIKE '%\\\"slug\\\":{\\\"type\\\":\\\"string\\\"%';\\n\\n-- Update news-collection schema if it exists\\nUPDATE collections \\nSET schema = REPLACE(\\n schema,\\n '\\\"slug\\\":{\\\"type\\\":\\\"string\\\"',\\n '\\\"slug\\\":{\\\"type\\\":\\\"slug\\\"'\\n)\\nWHERE id = 'news-collection' AND schema LIKE '%\\\"slug\\\":{\\\"type\\\":\\\"string\\\"%';\\n\"\n },\n {\n id: '029',\n name: 'Add Forms System',\n filename: '029_add_forms_system.sql',\n description: 'Migration 029: Add Forms System',\n sql: \"-- Migration: 029_add_forms_system.sql\\n-- Description: Add Form.io integration for advanced form building\\n-- Date: January 23, 2026\\n-- Phase: 1 - Database Schema\\n\\n-- =====================================================\\n-- Table: forms\\n-- Description: Stores form definitions and configuration\\n-- =====================================================\\n\\nCREATE TABLE IF NOT EXISTS forms (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL UNIQUE, -- Machine name (e.g., \\\"contact-form\\\")\\n display_name TEXT NOT NULL, -- Human name (e.g., \\\"Contact Form\\\")\\n description TEXT, -- Optional description\\n category TEXT DEFAULT 'general', -- Form category (contact, survey, registration, etc.)\\n \\n -- Form.io schema (JSON)\\n formio_schema TEXT NOT NULL, -- Complete Form.io JSON schema\\n \\n -- Settings\\n settings TEXT, -- JSON: {\\n -- emailNotifications: true,\\n -- notifyEmail: \\\"admin@example.com\\\",\\n -- successMessage: \\\"Thank you!\\\",\\n -- redirectUrl: \\\"/thank-you\\\",\\n -- allowAnonymous: true,\\n -- requireAuth: false,\\n -- maxSubmissions: null,\\n -- submitButtonText: \\\"Submit\\\",\\n -- saveProgress: true,\\n -- webhookUrl: null\\n -- }\\n \\n -- Status & Management\\n is_active INTEGER DEFAULT 1, -- Active/inactive flag\\n is_public INTEGER DEFAULT 1, -- Public (anyone) vs private (auth required)\\n managed INTEGER DEFAULT 0, -- Code-managed (like collections)\\n \\n -- Metadata\\n icon TEXT, -- Optional icon for admin UI\\n color TEXT, -- Optional color (hex) for admin UI\\n tags TEXT, -- JSON array of tags\\n \\n -- Stats\\n submission_count INTEGER DEFAULT 0, -- Total submissions received\\n view_count INTEGER DEFAULT 0, -- Form views (optional tracking)\\n \\n -- Ownership\\n created_by TEXT REFERENCES users(id), -- User who created the form\\n updated_by TEXT REFERENCES users(id), -- User who last updated\\n \\n -- Timestamps\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\n\\n-- Indexes for forms\\nCREATE INDEX IF NOT EXISTS idx_forms_name ON forms(name);\\nCREATE INDEX IF NOT EXISTS idx_forms_category ON forms(category);\\nCREATE INDEX IF NOT EXISTS idx_forms_active ON forms(is_active);\\nCREATE INDEX IF NOT EXISTS idx_forms_public ON forms(is_public);\\nCREATE INDEX IF NOT EXISTS idx_forms_created_by ON forms(created_by);\\n\\n-- =====================================================\\n-- Table: form_submissions\\n-- Description: Stores submitted form data\\n-- =====================================================\\n\\nCREATE TABLE IF NOT EXISTS form_submissions (\\n id TEXT PRIMARY KEY,\\n form_id TEXT NOT NULL REFERENCES forms(id) ON DELETE CASCADE,\\n \\n -- Submission data\\n submission_data TEXT NOT NULL, -- JSON: The actual form data submitted\\n \\n -- Submission metadata\\n status TEXT DEFAULT 'pending', -- pending, reviewed, approved, rejected, spam\\n submission_number INTEGER, -- Sequential number per form\\n \\n -- User information (if authenticated)\\n user_id TEXT REFERENCES users(id), -- Submitter user ID (if logged in)\\n user_email TEXT, -- Email from form (or user account)\\n \\n -- Tracking information\\n ip_address TEXT, -- IP address of submitter\\n user_agent TEXT, -- Browser user agent\\n referrer TEXT, -- Page that referred to form\\n utm_source TEXT, -- UTM tracking params\\n utm_medium TEXT,\\n utm_campaign TEXT,\\n \\n -- Review/Processing\\n reviewed_by TEXT REFERENCES users(id), -- Admin who reviewed\\n reviewed_at INTEGER, -- Review timestamp\\n review_notes TEXT, -- Admin notes\\n \\n -- Flags\\n is_spam INTEGER DEFAULT 0, -- Spam flag\\n is_archived INTEGER DEFAULT 0, -- Archived flag\\n \\n -- Timestamps\\n submitted_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\n\\n-- Indexes for submissions\\nCREATE INDEX IF NOT EXISTS idx_form_submissions_form_id ON form_submissions(form_id);\\nCREATE INDEX IF NOT EXISTS idx_form_submissions_status ON form_submissions(status);\\nCREATE INDEX IF NOT EXISTS idx_form_submissions_user_id ON form_submissions(user_id);\\nCREATE INDEX IF NOT EXISTS idx_form_submissions_email ON form_submissions(user_email);\\nCREATE INDEX IF NOT EXISTS idx_form_submissions_submitted_at ON form_submissions(submitted_at);\\nCREATE INDEX IF NOT EXISTS idx_form_submissions_spam ON form_submissions(is_spam);\\n\\n-- Trigger to auto-increment submission_number per form\\nCREATE TRIGGER IF NOT EXISTS set_submission_number\\nAFTER INSERT ON form_submissions\\nBEGIN\\n UPDATE form_submissions \\n SET submission_number = (\\n SELECT COUNT(*) \\n FROM form_submissions \\n WHERE form_id = NEW.form_id \\n AND id <= NEW.id\\n )\\n WHERE id = NEW.id;\\nEND;\\n\\n-- Trigger to update form submission_count\\nCREATE TRIGGER IF NOT EXISTS increment_form_submission_count\\nAFTER INSERT ON form_submissions\\nBEGIN\\n UPDATE forms \\n SET submission_count = submission_count + 1,\\n updated_at = unixepoch() * 1000\\n WHERE id = NEW.form_id;\\nEND;\\n\\n-- =====================================================\\n-- Table: form_files (Optional)\\n-- Description: Link form submissions to uploaded files\\n-- =====================================================\\n\\nCREATE TABLE IF NOT EXISTS form_files (\\n id TEXT PRIMARY KEY,\\n submission_id TEXT NOT NULL REFERENCES form_submissions(id) ON DELETE CASCADE,\\n media_id TEXT NOT NULL REFERENCES media(id) ON DELETE CASCADE,\\n field_name TEXT NOT NULL, -- Form field that uploaded this file\\n uploaded_at INTEGER NOT NULL\\n);\\n\\n-- Indexes for form files\\nCREATE INDEX IF NOT EXISTS idx_form_files_submission ON form_files(submission_id);\\nCREATE INDEX IF NOT EXISTS idx_form_files_media ON form_files(media_id);\\n\\n-- =====================================================\\n-- Sample Data: Create a default contact form\\n-- =====================================================\\n\\nINSERT OR IGNORE INTO forms (\\n id,\\n name,\\n display_name,\\n description,\\n category,\\n formio_schema,\\n settings,\\n is_active,\\n is_public,\\n created_at,\\n updated_at\\n) VALUES (\\n 'default-contact-form',\\n 'contact',\\n 'Contact Form',\\n 'A simple contact form for getting in touch',\\n 'contact',\\n '{\\\"components\\\":[]}',\\n '{\\\"emailNotifications\\\":false,\\\"successMessage\\\":\\\"Thank you for your submission!\\\",\\\"submitButtonText\\\":\\\"Submit\\\",\\\"requireAuth\\\":false}',\\n 1,\\n 1,\\n unixepoch() * 1000,\\n unixepoch() * 1000\\n);\\n\"\n },\n {\n id: '030',\n name: 'Add Turnstile To Forms',\n filename: '030_add_turnstile_to_forms.sql',\n description: 'Migration 030: Add Turnstile To Forms',\n sql: \"-- Add Turnstile configuration to forms table\\n-- This allows per-form Turnstile settings with global fallback\\n\\n-- Add columns (D1 may not support CHECK constraints in ALTER TABLE)\\nALTER TABLE forms ADD COLUMN turnstile_enabled INTEGER DEFAULT 0;\\nALTER TABLE forms ADD COLUMN turnstile_settings TEXT;\\n\\n-- Set default to inherit global settings for existing forms\\nUPDATE forms \\nSET turnstile_settings = '{\\\"inherit\\\":true}' \\nWHERE turnstile_settings IS NULL;\\n\\n-- Add index for faster lookups\\nCREATE INDEX IF NOT EXISTS idx_forms_turnstile ON forms(turnstile_enabled);\\n\"\n },\n {\n id: '031',\n name: 'Ai Search Plugin',\n filename: '031_ai_search_plugin.sql',\n description: 'Migration 031: Ai Search Plugin',\n sql: \"-- AI Search plugin settings\\nCREATE TABLE IF NOT EXISTS ai_search_settings (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n enabled BOOLEAN DEFAULT 0,\\n ai_mode_enabled BOOLEAN DEFAULT 1,\\n selected_collections TEXT, -- JSON array of collection IDs to index\\n dismissed_collections TEXT, -- JSON array of collection IDs user chose not to index\\n autocomplete_enabled BOOLEAN DEFAULT 1,\\n cache_duration INTEGER DEFAULT 1, -- hours\\n results_limit INTEGER DEFAULT 20,\\n index_media BOOLEAN DEFAULT 0,\\n index_status TEXT, -- JSON object with status per collection\\n last_indexed_at INTEGER,\\n created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),\\n updated_at INTEGER DEFAULT (strftime('%s', 'now') * 1000)\\n);\\n\\n-- Search history/analytics\\nCREATE TABLE IF NOT EXISTS ai_search_history (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n query TEXT NOT NULL,\\n mode TEXT, -- 'ai' or 'keyword'\\n results_count INTEGER,\\n user_id INTEGER,\\n created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000)\\n);\\n\\n-- Index metadata tracking (per collection)\\nCREATE TABLE IF NOT EXISTS ai_search_index_meta (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n collection_id INTEGER NOT NULL,\\n collection_name TEXT NOT NULL, -- Cache collection name for display\\n total_items INTEGER DEFAULT 0,\\n indexed_items INTEGER DEFAULT 0,\\n last_sync_at INTEGER,\\n status TEXT DEFAULT 'pending', -- 'pending', 'indexing', 'completed', 'error'\\n error_message TEXT,\\n UNIQUE(collection_id)\\n);\\n\\n-- Indexes for performance\\nCREATE INDEX IF NOT EXISTS idx_ai_search_history_created_at ON ai_search_history(created_at);\\nCREATE INDEX IF NOT EXISTS idx_ai_search_history_mode ON ai_search_history(mode);\\nCREATE INDEX IF NOT EXISTS idx_ai_search_index_meta_collection_id ON ai_search_index_meta(collection_id);\\nCREATE INDEX IF NOT EXISTS idx_ai_search_index_meta_status ON ai_search_index_meta(status);\\n\"\n },\n {\n id: '032',\n name: 'User Profiles',\n filename: '032_user_profiles.sql',\n description: 'Migration 032: User Profiles',\n sql: \"-- User Profiles Table (Core Migration)\\n-- Stores extended user profile data separate from auth concerns\\n-- Required by admin-users.ts for user edit page profile management\\n--\\n-- Originally introduced as app-level migration (my-sonicjs-app/migrations/018_user_profiles.sql)\\n-- in upstream PR #508. Core routes (admin-users.ts) were updated to query this table in PR #512,\\n-- but no corresponding core migration was added. This migration corrects that gap.\\n--\\n-- IF NOT EXISTS guards ensure idempotency for databases that already have the table\\n-- from the app-level migration.\\n\\nCREATE TABLE IF NOT EXISTS user_profiles (\\n id TEXT PRIMARY KEY,\\n user_id TEXT NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,\\n\\n display_name TEXT,\\n bio TEXT,\\n company TEXT,\\n job_title TEXT,\\n website TEXT,\\n location TEXT,\\n date_of_birth INTEGER,\\n\\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),\\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)\\n);\\n\\n-- Index for fast user lookups\\nCREATE INDEX IF NOT EXISTS idx_user_profiles_user_id ON user_profiles(user_id);\\n\\n-- Trigger to auto-update updated_at timestamp\\nCREATE TRIGGER IF NOT EXISTS user_profiles_updated_at\\n AFTER UPDATE ON user_profiles\\nBEGIN\\n UPDATE user_profiles SET updated_at = strftime('%s', 'now') * 1000 WHERE id = NEW.id;\\nEND;\\n\"\n },\n {\n id: '033',\n name: 'Form Content Integration',\n filename: '033_form_content_integration.sql',\n description: 'Migration 033: Form Content Integration',\n sql: \"-- Migration 033: Form-Content Integration\\n-- Adds bridge columns to link forms to collections and submissions to content items\\n\\n-- Add source_type and source_id to collections for form-derived collections\\nALTER TABLE collections ADD COLUMN source_type TEXT DEFAULT 'user';\\nALTER TABLE collections ADD COLUMN source_id TEXT;\\n\\n-- Index for efficient lookup of form-derived collections\\nCREATE INDEX IF NOT EXISTS idx_collections_source ON collections(source_type, source_id);\\n\\n-- Add content_id to form_submissions for linking to content items\\nALTER TABLE form_submissions ADD COLUMN content_id TEXT REFERENCES content(id);\\n\\n-- Index for efficient lookup by content_id\\nCREATE INDEX IF NOT EXISTS idx_form_submissions_content_id ON form_submissions(content_id);\\n\\n-- Create system user for anonymous form submissions\\nINSERT OR IGNORE INTO users (id, email, username, first_name, last_name, password_hash, role, is_active, created_at, updated_at)\\nVALUES ('system-form-submission', 'system-forms@sonicjs.internal', 'system-forms', 'Form', 'Submission', NULL, 'viewer', 0, strftime('%s','now') * 1000, strftime('%s','now') * 1000);\\n\"\n }\n]\n\n// Map for quick lookup by ID\nexport const migrationsByIdMap = new Map<string, BundledMigration>(\n bundledMigrations.map(m => [m.id, m])\n)\n\n// Get migration SQL by ID\nexport function getMigrationSQLById(id: string): string | null {\n return migrationsByIdMap.get(id)?.sql ?? null\n}\n\n// Get all migration info (without SQL for lighter payloads)\nexport function getMigrationList(): Array<Omit<BundledMigration, 'sql'>> {\n return bundledMigrations.map(({ sql, ...rest }) => rest)\n}\n","import { D1Database } from '@cloudflare/workers-types'\nimport { bundledMigrations, getMigrationSQLById, type BundledMigration } from '../db/migrations-bundle'\n\nexport interface Migration {\n id: string\n name: string\n filename: string\n description?: string\n applied: boolean\n appliedAt?: string\n size?: number\n}\n\nexport interface MigrationStatus {\n totalMigrations: number\n appliedMigrations: number\n pendingMigrations: number\n lastApplied?: string\n migrations: Migration[]\n}\n\nexport class MigrationService {\n constructor(private db: D1Database) {}\n\n /**\n * Initialize the migrations tracking table\n */\n async initializeMigrationsTable(): Promise<void> {\n const createTableQuery = `\n CREATE TABLE IF NOT EXISTS migrations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n filename TEXT NOT NULL,\n applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n checksum TEXT\n )\n `\n\n await this.db.prepare(createTableQuery).run()\n }\n\n /**\n * Get all available migrations from the bundled migrations\n */\n async getAvailableMigrations(): Promise<Migration[]> {\n const migrations: Migration[] = []\n\n // Get applied migrations from database\n const appliedResult = await this.db.prepare(\n 'SELECT id, name, filename, applied_at FROM migrations ORDER BY applied_at ASC'\n ).all()\n\n const appliedMigrations = new Map(\n appliedResult.results?.map((row: any) => [row.id, row]) || []\n )\n\n // Auto-detect applied migrations by checking if their tables exist\n await this.autoDetectAppliedMigrations(appliedMigrations)\n\n // Use bundled migrations as the source of truth\n for (const bundled of bundledMigrations) {\n const applied = appliedMigrations.has(bundled.id)\n const appliedData = appliedMigrations.get(bundled.id)\n\n migrations.push({\n id: bundled.id,\n name: bundled.name,\n filename: bundled.filename,\n description: bundled.description,\n applied,\n appliedAt: applied ? appliedData?.applied_at : undefined,\n size: bundled.sql.length\n })\n }\n\n return migrations\n }\n\n /**\n * Auto-detect applied migrations by checking if their tables exist\n */\n private async autoDetectAppliedMigrations(appliedMigrations: Map<string, any>): Promise<void> {\n // Check if basic schema tables exist (migration 001)\n if (!appliedMigrations.has('001')) {\n const hasBasicTables = await this.checkTablesExist(['users', 'content', 'collections', 'media'])\n if (hasBasicTables) {\n appliedMigrations.set('001', {\n id: '001',\n applied_at: new Date().toISOString(),\n name: 'Initial Schema',\n filename: '001_initial_schema.sql'\n })\n await this.markMigrationApplied('001', 'Initial Schema', '001_initial_schema.sql')\n }\n }\n\n // Check if FAQ tables exist (migration 002)\n // Migration 002 creates only the 'faqs' table\n if (!appliedMigrations.has('002')) {\n const hasFaqTables = await this.checkTablesExist(['faqs'])\n if (hasFaqTables) {\n appliedMigrations.set('002', {\n id: '002',\n applied_at: new Date().toISOString(),\n name: 'Faq Plugin',\n filename: '002_faq_plugin.sql'\n })\n await this.markMigrationApplied('002', 'Faq Plugin', '002_faq_plugin.sql')\n }\n }\n\n // Check if stage 5 enhancement tables exist (migration 003)\n // Migration 003 creates content_fields, content_relationships, workflow_templates tables\n if (!appliedMigrations.has('003')) {\n const hasStage5Tables = await this.checkTablesExist(['content_fields', 'content_relationships', 'workflow_templates'])\n if (hasStage5Tables) {\n appliedMigrations.set('003', {\n id: '003',\n applied_at: new Date().toISOString(),\n name: 'Stage 5 Enhancements',\n filename: '003_stage5_enhancements.sql'\n })\n await this.markMigrationApplied('003', 'Stage 5 Enhancements', '003_stage5_enhancements.sql')\n }\n }\n\n // Check if testimonials table exists (migration 012)\n if (!appliedMigrations.has('012')) {\n const hasTestimonialsTables = await this.checkTablesExist(['testimonials'])\n if (hasTestimonialsTables) {\n appliedMigrations.set('012', {\n id: '012',\n applied_at: new Date().toISOString(),\n name: 'Testimonials Plugin',\n filename: '012_testimonials_plugin.sql'\n })\n await this.markMigrationApplied('012', 'Testimonials Plugin', '012_testimonials_plugin.sql')\n }\n }\n\n // Check if code_examples table exists (migration 013)\n if (!appliedMigrations.has('013')) {\n const hasCodeExamplesTables = await this.checkTablesExist(['code_examples'])\n if (hasCodeExamplesTables) {\n appliedMigrations.set('013', {\n id: '013',\n applied_at: new Date().toISOString(),\n name: 'Code Examples Plugin',\n filename: '013_code_examples_plugin.sql'\n })\n await this.markMigrationApplied('013', 'Code Examples Plugin', '013_code_examples_plugin.sql')\n }\n }\n\n // Check if user management tables exist (migration 004)\n if (!appliedMigrations.has('004')) {\n const hasUserTables = await this.checkTablesExist(['api_tokens', 'workflow_history'])\n if (hasUserTables) {\n appliedMigrations.set('004', {\n id: '004',\n applied_at: new Date().toISOString(),\n name: 'User Management',\n filename: '004_stage6_user_management.sql'\n })\n await this.markMigrationApplied('004', 'User Management', '004_stage6_user_management.sql')\n }\n }\n\n // Check if plugin system tables exist (migration 006)\n if (!appliedMigrations.has('006')) {\n const hasPluginTables = await this.checkTablesExist(['plugins', 'plugin_hooks'])\n if (hasPluginTables) {\n appliedMigrations.set('006', {\n id: '006',\n applied_at: new Date().toISOString(),\n name: 'Plugin System',\n filename: '006_plugin_system.sql'\n })\n await this.markMigrationApplied('006', 'Plugin System', '006_plugin_system.sql')\n }\n }\n\n // Check if managed column exists (migration 011)\n // This handles both cases:\n // 1. Migration not marked as applied but column exists -> mark as applied\n // 2. Migration marked as applied but column doesn't exist -> remove from applied (will re-run)\n const hasManagedColumn = await this.checkColumnExists('collections', 'managed')\n if (!appliedMigrations.has('011') && hasManagedColumn) {\n appliedMigrations.set('011', {\n id: '011',\n applied_at: new Date().toISOString(),\n name: 'Config Managed Collections',\n filename: '011_config_managed_collections.sql'\n })\n await this.markMigrationApplied('011', 'Config Managed Collections', '011_config_managed_collections.sql')\n } else if (appliedMigrations.has('011') && !hasManagedColumn) {\n // Migration was marked as applied but column doesn't exist - remove it so it will re-run\n console.log('[Migration] Migration 011 marked as applied but managed column missing - will re-run')\n appliedMigrations.delete('011')\n await this.removeMigrationApplied('011')\n }\n\n // Check if system_logs table exists (migration 009)\n if (!appliedMigrations.has('009')) {\n const hasLoggingTables = await this.checkTablesExist(['system_logs', 'log_config'])\n if (hasLoggingTables) {\n appliedMigrations.set('009', {\n id: '009',\n applied_at: new Date().toISOString(),\n name: 'System Logging',\n filename: '009_system_logging.sql'\n })\n await this.markMigrationApplied('009', 'System Logging', '009_system_logging.sql')\n }\n }\n\n // Check if settings table exists (migration 018)\n if (!appliedMigrations.has('018')) {\n const hasSettingsTable = await this.checkTablesExist(['settings'])\n if (hasSettingsTable) {\n appliedMigrations.set('018', {\n id: '018',\n applied_at: new Date().toISOString(),\n name: 'Settings Table',\n filename: '018_settings_table.sql'\n })\n await this.markMigrationApplied('018', 'Settings Table', '018_settings_table.sql')\n }\n }\n\n // Check if user_profiles table exists (migration 032)\n // Table may already exist from app-level migration (my-sonicjs-app/migrations/018_user_profiles.sql)\n if (!appliedMigrations.has('032')) {\n const hasUserProfilesTable = await this.checkTablesExist(['user_profiles'])\n if (hasUserProfilesTable) {\n appliedMigrations.set('032', {\n id: '032',\n applied_at: new Date().toISOString(),\n name: 'User Profiles',\n filename: '032_user_profiles.sql'\n })\n await this.markMigrationApplied('032', 'User Profiles', '032_user_profiles.sql')\n }\n }\n }\n\n /**\n * Check if specific tables exist in the database\n */\n private async checkTablesExist(tableNames: string[]): Promise<boolean> {\n try {\n for (const tableName of tableNames) {\n const result = await this.db.prepare(\n `SELECT name FROM sqlite_master WHERE type='table' AND name=?`\n ).bind(tableName).first()\n\n if (!result) {\n return false\n }\n }\n return true\n } catch (error) {\n return false\n }\n }\n\n /**\n * Check if a specific column exists in a table\n */\n private async checkColumnExists(tableName: string, columnName: string): Promise<boolean> {\n try {\n const result = await this.db.prepare(\n `SELECT * FROM pragma_table_info(?) WHERE name = ?`\n ).bind(tableName, columnName).first()\n\n return !!result\n } catch (error) {\n return false\n }\n }\n\n /**\n * Get migration status summary\n */\n async getMigrationStatus(): Promise<MigrationStatus> {\n await this.initializeMigrationsTable()\n\n const migrations = await this.getAvailableMigrations()\n const appliedMigrations = migrations.filter(m => m.applied)\n const pendingMigrations = migrations.filter(m => !m.applied)\n\n const lastApplied = appliedMigrations.length > 0\n ? appliedMigrations[appliedMigrations.length - 1]?.appliedAt\n : undefined\n\n return {\n totalMigrations: migrations.length,\n appliedMigrations: appliedMigrations.length,\n pendingMigrations: pendingMigrations.length,\n lastApplied,\n migrations\n }\n }\n\n /**\n * Mark a migration as applied\n */\n async markMigrationApplied(migrationId: string, name: string, filename: string): Promise<void> {\n await this.initializeMigrationsTable()\n\n await this.db.prepare(\n 'INSERT OR REPLACE INTO migrations (id, name, filename, applied_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP)'\n ).bind(migrationId, name, filename).run()\n }\n\n /**\n * Remove a migration from the applied list (so it can be re-run)\n */\n async removeMigrationApplied(migrationId: string): Promise<void> {\n await this.initializeMigrationsTable()\n\n await this.db.prepare(\n 'DELETE FROM migrations WHERE id = ?'\n ).bind(migrationId).run()\n }\n\n /**\n * Check if a specific migration has been applied\n */\n async isMigrationApplied(migrationId: string): Promise<boolean> {\n await this.initializeMigrationsTable()\n\n const result = await this.db.prepare(\n 'SELECT COUNT(*) as count FROM migrations WHERE id = ?'\n ).bind(migrationId).first()\n\n return (result?.count as number) > 0\n }\n\n /**\n * Get the last applied migration\n */\n async getLastAppliedMigration(): Promise<Migration | null> {\n await this.initializeMigrationsTable()\n\n const result = await this.db.prepare(\n 'SELECT id, name, filename, applied_at FROM migrations ORDER BY applied_at DESC LIMIT 1'\n ).first()\n\n if (!result) return null\n\n return {\n id: result.id as string,\n name: result.name as string,\n filename: result.filename as string,\n applied: true,\n appliedAt: result.applied_at as string\n }\n }\n\n /**\n * Run pending migrations\n */\n async runPendingMigrations(): Promise<{ success: boolean; message: string; applied: string[] }> {\n await this.initializeMigrationsTable()\n\n const status = await this.getMigrationStatus()\n const pendingMigrations = status.migrations.filter(m => !m.applied)\n\n if (pendingMigrations.length === 0) {\n return {\n success: true,\n message: 'All migrations are up to date',\n applied: []\n }\n }\n\n // Actually execute the migration files\n const applied: string[] = []\n const errors: string[] = []\n\n for (const migration of pendingMigrations) {\n try {\n console.log(`[Migration] Applying ${migration.id}: ${migration.name}`)\n await this.applyMigration(migration)\n await this.markMigrationApplied(migration.id, migration.name, migration.filename)\n applied.push(migration.id)\n console.log(`[Migration] Successfully applied ${migration.id}`)\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(`[Migration] Failed to apply migration ${migration.id}:`, errorMessage)\n errors.push(`${migration.id}: ${errorMessage}`)\n // Continue with other migrations instead of stopping on first failure\n // This allows independent migrations to still be applied\n }\n }\n\n if (errors.length > 0 && applied.length === 0) {\n return {\n success: false,\n message: `Failed to apply migrations: ${errors.join('; ')}`,\n applied\n }\n }\n\n return {\n success: true,\n message: applied.length > 0\n ? `Applied ${applied.length} migration(s)${errors.length > 0 ? ` (${errors.length} failed)` : ''}`\n : 'No migrations applied',\n applied\n }\n }\n\n /**\n * Apply a specific migration\n */\n private async applyMigration(migration: Migration): Promise<void> {\n // Get the actual migration SQL from the bundle\n const migrationSQL = getMigrationSQLById(migration.id)\n\n if (migrationSQL === null) {\n throw new Error(`Migration SQL not found for ${migration.id}`)\n }\n\n if (migrationSQL.trim() === '') {\n console.log(`[Migration] Skipping empty migration ${migration.id}`)\n return\n }\n\n // Split SQL into individual statements, handling triggers properly\n const statements = this.splitSQLStatements(migrationSQL)\n\n for (const statement of statements) {\n if (statement.trim()) {\n try {\n await this.db.prepare(statement).run()\n } catch (error) {\n // Check if it's a \"already exists\" type error and skip it\n const errorMessage = error instanceof Error ? error.message : String(error)\n if (errorMessage.includes('already exists') ||\n errorMessage.includes('duplicate column name') ||\n errorMessage.includes('UNIQUE constraint failed')) {\n console.log(`[Migration] Skipping (already exists): ${statement.substring(0, 50)}...`)\n continue\n }\n console.error(`[Migration] Error executing statement: ${statement.substring(0, 100)}...`)\n throw error\n }\n }\n }\n }\n\n /**\n * Split SQL into statements, handling CREATE TRIGGER properly\n */\n private splitSQLStatements(sql: string): string[] {\n const statements: string[] = []\n let current = ''\n let inTrigger = false\n\n const lines = sql.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Skip comments and empty lines\n if (trimmed.startsWith('--') || trimmed.length === 0) {\n continue\n }\n\n // Check if we're entering a trigger\n if (trimmed.toUpperCase().includes('CREATE TRIGGER')) {\n inTrigger = true\n }\n\n current += line + '\\n'\n\n // Check if we're exiting a trigger\n if (inTrigger && trimmed.toUpperCase() === 'END;') {\n statements.push(current.trim())\n current = ''\n inTrigger = false\n }\n // Check for regular statement end (not in trigger)\n else if (!inTrigger && trimmed.endsWith(';')) {\n statements.push(current.trim())\n current = ''\n }\n }\n\n // Add any remaining statement\n if (current.trim()) {\n statements.push(current.trim())\n }\n\n return statements.filter(s => s.length > 0)\n }\n\n /**\n * Validate database schema\n */\n async validateSchema(): Promise<{ valid: boolean; issues: string[] }> {\n const issues: string[] = []\n\n // Basic table existence checks\n const requiredTables = [\n 'users', 'content', 'collections', 'media'\n ]\n\n for (const table of requiredTables) {\n try {\n await this.db.prepare(`SELECT COUNT(*) FROM ${table} LIMIT 1`).first()\n } catch (error) {\n issues.push(`Missing table: ${table}`)\n }\n }\n\n // Check for managed column in collections\n const hasManagedColumn = await this.checkColumnExists('collections', 'managed')\n if (!hasManagedColumn) {\n issues.push('Missing column: collections.managed')\n }\n\n return {\n valid: issues.length === 0,\n issues\n }\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { syncCollections, PluginBootstrapService } from './chunk-27AOVQTR.js';
2
- import { MigrationService } from './chunk-6O3RJV3C.js';
1
+ import { syncCollections, syncAllFormCollections, PluginBootstrapService } from './chunk-NMLFKXWW.js';
2
+ import { MigrationService } from './chunk-H55AYIRI.js';
3
3
  import { metricsTracker } from './chunk-FICTAGD4.js';
4
4
  import { sign, verify } from 'hono/jwt';
5
5
  import { setCookie, getCookie } from 'hono/cookie';
@@ -63,6 +63,12 @@ function bootstrapMiddleware(config = {}) {
63
63
  } catch (error) {
64
64
  console.error("[Bootstrap] Error syncing collections:", error);
65
65
  }
66
+ console.log("[Bootstrap] Syncing form collections...");
67
+ try {
68
+ await syncAllFormCollections(c.env.DB);
69
+ } catch (error) {
70
+ console.error("[Bootstrap] Error syncing form collections:", error);
71
+ }
66
72
  if (!config.plugins?.disableAll) {
67
73
  console.log("[Bootstrap] Bootstrapping core plugins...");
68
74
  const bootstrapService = new PluginBootstrapService(c.env.DB);
@@ -537,5 +543,5 @@ var getActivePlugins = () => [];
537
543
  var isPluginActive = () => false;
538
544
 
539
545
  export { AuthManager, PermissionManager, bootstrapMiddleware, cacheHeaders, compressionMiddleware, csrfProtection, detailedLoggingMiddleware, generateCsrfToken, getActivePlugins, isPluginActive, logActivity, loggingMiddleware, metricsMiddleware, optionalAuth, performanceLoggingMiddleware, rateLimit, requireActivePlugin, requireActivePlugins, requireAnyPermission, requireAuth, requirePermission, requireRole, securityHeadersMiddleware, securityLoggingMiddleware, validateCsrfToken, verifySecurityConfig };
540
- //# sourceMappingURL=chunk-4TTMQQC7.js.map
541
- //# sourceMappingURL=chunk-4TTMQQC7.js.map
546
+ //# sourceMappingURL=chunk-GKRGDJGG.js.map
547
+ //# sourceMappingURL=chunk-GKRGDJGG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware/bootstrap.ts","../src/middleware/auth.ts","../src/middleware/metrics.ts","../src/middleware/csrf.ts","../src/middleware/rate-limit.ts","../src/middleware/security-headers.ts","../src/middleware/index.ts"],"names":["result","JWT_SECRET_FALLBACK","getCookie","setCookie"],"mappings":"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","file":"chunk-GKRGDJGG.js","sourcesContent":["import { Context, Next } from \"hono\";\nimport { syncCollections } from \"../services/collection-sync\";\nimport { syncAllFormCollections } from \"../services/form-collection-sync\";\nimport { MigrationService } from \"../services/migrations\";\nimport { PluginBootstrapService } from \"../services/plugin-bootstrap\";\nimport type { SonicJSConfig } from \"../app\";\n\ntype Bindings = {\n DB: D1Database;\n KV: KVNamespace;\n JWT_SECRET?: string;\n CORS_ORIGINS?: string;\n ENVIRONMENT?: string;\n};\n\n// Track if bootstrap has been run in this worker instance\nlet bootstrapComplete = false;\n\n/**\n * Verify security-critical environment configuration at startup.\n * Logs warnings in development, throws in production to prevent\n * insecure deployments from silently running.\n */\nexport function verifySecurityConfig(env: Bindings): void {\n const warnings: string[] = [];\n\n // Check JWT secret\n if (!env.JWT_SECRET) {\n warnings.push(\n \"JWT_SECRET is not set — using hardcoded fallback. Set via `wrangler secret put JWT_SECRET`\"\n );\n } else if (env.JWT_SECRET.includes(\"change-in-production\")) {\n warnings.push(\n \"JWT_SECRET contains the default value — tokens are forgeable. Generate a strong random secret\"\n );\n }\n\n // Check CORS origins\n if (!env.CORS_ORIGINS) {\n warnings.push(\n \"CORS_ORIGINS is not set — all cross-origin API requests will be rejected\"\n );\n }\n\n // Check environment designation\n if (!env.ENVIRONMENT) {\n warnings.push(\n \"ENVIRONMENT is not set — HSTS header will not be applied. Set to \\\"production\\\" or \\\"development\\\"\"\n );\n }\n\n if (warnings.length === 0) {\n return;\n }\n\n const isProduction = env.ENVIRONMENT === \"production\";\n\n for (const warning of warnings) {\n console.warn(`[SonicJS Security] ${warning}`);\n }\n\n if (isProduction) {\n // In production, a missing or default JWT_SECRET is a hard failure —\n // every token issued would be forgeable by anyone reading the source code.\n const hasCritical =\n !env.JWT_SECRET || env.JWT_SECRET.includes(\"change-in-production\");\n if (hasCritical) {\n throw new Error(\n \"[SonicJS Security] CRITICAL: Production deployment is missing a secure JWT_SECRET. \" +\n \"Set it via `wrangler secret put JWT_SECRET` before deploying.\"\n );\n }\n }\n}\n\n/**\n * Bootstrap middleware that ensures system initialization\n * Runs once per worker instance\n */\nexport function bootstrapMiddleware(config: SonicJSConfig = {}) {\n return async (c: Context<{ Bindings: Bindings }>, next: Next) => {\n // Skip if already bootstrapped in this worker instance\n if (bootstrapComplete) {\n return next();\n }\n\n // Skip bootstrap for static assets and health checks\n const path = c.req.path;\n if (\n path.startsWith(\"/images/\") ||\n path.startsWith(\"/assets/\") ||\n path === \"/health\" ||\n path.endsWith(\".js\") ||\n path.endsWith(\".css\") ||\n path.endsWith(\".png\") ||\n path.endsWith(\".jpg\") ||\n path.endsWith(\".ico\")\n ) {\n return next();\n }\n\n try {\n console.log(\"[Bootstrap] Starting system initialization...\");\n\n // 1. Run database migrations first\n console.log(\"[Bootstrap] Running database migrations...\");\n const migrationService = new MigrationService(c.env.DB);\n await migrationService.runPendingMigrations();\n\n // 2. Sync collection configurations\n console.log(\"[Bootstrap] Syncing collection configurations...\");\n try {\n await syncCollections(c.env.DB);\n } catch (error) {\n console.error(\"[Bootstrap] Error syncing collections:\", error);\n // Continue bootstrap even if collection sync fails\n }\n\n // 2b. Sync form-derived shadow collections\n console.log(\"[Bootstrap] Syncing form collections...\");\n try {\n await syncAllFormCollections(c.env.DB);\n } catch (error) {\n console.error(\"[Bootstrap] Error syncing form collections:\", error);\n }\n\n // 3. Bootstrap core plugins (unless disableAll is set)\n if (!config.plugins?.disableAll) {\n console.log(\"[Bootstrap] Bootstrapping core plugins...\");\n const bootstrapService = new PluginBootstrapService(c.env.DB);\n\n // Check if bootstrap is needed\n const needsBootstrap = await bootstrapService.isBootstrapNeeded();\n if (needsBootstrap) {\n await bootstrapService.bootstrapCorePlugins();\n }\n } else {\n console.log(\"[Bootstrap] Plugin bootstrap skipped (disableAll is true)\");\n }\n\n // Mark bootstrap as complete for this worker instance\n bootstrapComplete = true;\n console.log(\"[Bootstrap] System initialization completed\");\n } catch (error) {\n console.error(\"[Bootstrap] Error during system initialization:\", error);\n // Don't prevent the app from starting, but log the error\n }\n\n // 4. Verify security configuration (outside try/catch so critical\n // errors in production propagate and prevent insecure deployments)\n verifySecurityConfig(c.env as Bindings);\n\n return next();\n };\n}\n\n/**\n * Reset bootstrap flag (useful for testing)\n */\nexport function resetBootstrap() {\n bootstrapComplete = false;\n}\n","import { sign, verify } from 'hono/jwt'\nimport { Context, Next } from 'hono'\nimport { getCookie, setCookie } from 'hono/cookie'\n\ntype JWTPayload = {\n userId: string\n email: string\n role: string\n exp: number\n iat: number\n}\n\n// Fallback JWT secret for local development only (no wrangler secret set)\nconst JWT_SECRET_FALLBACK = 'your-super-secret-jwt-key-change-in-production'\n\nexport class AuthManager {\n static async generateToken(userId: string, email: string, role: string, secret?: string): Promise<string> {\n const payload: JWTPayload = {\n userId,\n email,\n role,\n exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24), // 24 hours\n iat: Math.floor(Date.now() / 1000)\n }\n\n return await sign(payload, secret || JWT_SECRET_FALLBACK, 'HS256')\n }\n\n static async verifyToken(token: string, secret?: string): Promise<JWTPayload | null> {\n try {\n const payload = await verify(token, secret || JWT_SECRET_FALLBACK, 'HS256') as JWTPayload\n \n // Check if token is expired\n if (payload.exp < Math.floor(Date.now() / 1000)) {\n return null\n }\n \n return payload\n } catch (error) {\n console.error('Token verification failed:', error)\n return null\n }\n }\n\n static async hashPassword(password: string): Promise<string> {\n const iterations = 100000\n const salt = new Uint8Array(16)\n crypto.getRandomValues(salt)\n\n const encoder = new TextEncoder()\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(password),\n 'PBKDF2',\n false,\n ['deriveBits']\n )\n\n const hashBuffer = await crypto.subtle.deriveBits(\n {\n name: 'PBKDF2',\n salt,\n iterations,\n hash: 'SHA-256'\n },\n keyMaterial,\n 256\n )\n\n const saltHex = Array.from(salt).map(b => b.toString(16).padStart(2, '0')).join('')\n const hashHex = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('')\n\n return `pbkdf2:${iterations}:${saltHex}:${hashHex}`\n }\n\n static async hashPasswordLegacy(password: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(password + 'salt-change-in-production')\n const hashBuffer = await crypto.subtle.digest('SHA-256', data)\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n return hashArray.map(b => b.toString(16).padStart(2, '0')).join('')\n }\n\n static async verifyPassword(password: string, storedHash: string): Promise<boolean> {\n if (storedHash.startsWith('pbkdf2:')) {\n // PBKDF2 format: pbkdf2:<iterations>:<salt_hex>:<hash_hex>\n const parts = storedHash.split(':')\n if (parts.length !== 4) return false\n\n const iterationsStr = parts[1]!\n const saltHex = parts[2]!\n const expectedHashHex = parts[3]!\n const iterations = parseInt(iterationsStr, 10)\n\n const saltBytes = saltHex.match(/.{2}/g)\n if (!saltBytes) return false\n const salt = new Uint8Array(saltBytes.map(byte => parseInt(byte, 16)))\n\n const encoder = new TextEncoder()\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(password),\n 'PBKDF2',\n false,\n ['deriveBits']\n )\n\n const hashBuffer = await crypto.subtle.deriveBits(\n {\n name: 'PBKDF2',\n salt,\n iterations,\n hash: 'SHA-256'\n },\n keyMaterial,\n 256\n )\n\n const actualHashHex = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('')\n\n // Constant-time comparison\n if (actualHashHex.length !== expectedHashHex.length) return false\n let result = 0\n for (let i = 0; i < actualHashHex.length; i++) {\n result |= actualHashHex.charCodeAt(i) ^ expectedHashHex.charCodeAt(i)\n }\n return result === 0\n }\n\n // Legacy SHA-256 format (no colons in hash)\n const legacyHash = await this.hashPasswordLegacy(password)\n // Constant-time comparison for legacy too\n if (legacyHash.length !== storedHash.length) return false\n let result = 0\n for (let i = 0; i < legacyHash.length; i++) {\n result |= legacyHash.charCodeAt(i) ^ storedHash.charCodeAt(i)\n }\n return result === 0\n }\n\n static isLegacyHash(storedHash: string): boolean {\n return !storedHash.startsWith('pbkdf2:')\n }\n\n /**\n * Set authentication cookie - useful for plugins implementing alternative auth methods\n * @param c - Hono context\n * @param token - JWT token to set in cookie\n * @param options - Optional cookie configuration\n */\n static setAuthCookie(c: Context, token: string, options?: {\n maxAge?: number\n secure?: boolean\n httpOnly?: boolean\n sameSite?: 'Strict' | 'Lax' | 'None'\n }): void {\n setCookie(c, 'auth_token', token, {\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? true,\n sameSite: options?.sameSite ?? 'Strict',\n maxAge: options?.maxAge ?? (60 * 60 * 24) // 24 hours default\n })\n }\n}\n\n// Middleware to require authentication\nexport const requireAuth = () => {\n return async (c: Context, next: Next) => {\n try {\n // Try to get token from Authorization header\n let token = c.req.header('Authorization')?.replace('Bearer ', '')\n\n // If no header token, try cookie\n if (!token) {\n token = getCookie(c, 'auth_token')\n }\n\n if (!token) {\n // Check if this is a browser request (HTML accept header)\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=Please login to access the admin area')\n }\n return c.json({ error: 'Authentication required' }, 401)\n }\n\n // Try to get cached token verification from KV\n const kv = c.env?.KV\n let payload: JWTPayload | null = null\n\n if (kv) {\n const cacheKey = `auth:${token.substring(0, 20)}` // Use token prefix as key\n const cached = await kv.get(cacheKey, 'json')\n if (cached) {\n payload = cached as JWTPayload\n }\n }\n\n // If not cached, verify token\n if (!payload) {\n const jwtSecret = (c.env as any)?.JWT_SECRET\n payload = await AuthManager.verifyToken(token, jwtSecret)\n\n // Cache the verified payload for 5 minutes\n if (payload && kv) {\n const cacheKey = `auth:${token.substring(0, 20)}`\n await kv.put(cacheKey, JSON.stringify(payload), { expirationTtl: 300 })\n }\n }\n\n if (!payload) {\n // Check if this is a browser request (HTML accept header)\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=Your session has expired, please login again')\n }\n return c.json({ error: 'Invalid or expired token' }, 401)\n }\n\n // Add user info to context\n c.set('user', payload)\n\n return await next()\n } catch (error) {\n console.error('Auth middleware error:', error)\n // Check if this is a browser request (HTML accept header)\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=Authentication failed, please login again')\n }\n return c.json({ error: 'Authentication failed' }, 401)\n }\n }\n}\n\n// Middleware to require specific role\nexport const requireRole = (requiredRole: string | string[]) => {\n return async (c: Context, next: Next) => {\n const user = c.get('user') as JWTPayload\n \n if (!user) {\n // Check if this is a browser request (HTML accept header)\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=Please login to access the admin area')\n }\n return c.json({ error: 'Authentication required' }, 401)\n }\n \n const roles = Array.isArray(requiredRole) ? requiredRole : [requiredRole]\n \n if (!roles.includes(user.role)) {\n // Check if this is a browser request (HTML accept header)\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=You do not have permission to access this area')\n }\n return c.json({ error: 'Insufficient permissions' }, 403)\n }\n \n return await next()\n }\n}\n\n// Optional auth middleware (doesn't block if no token)\nexport const optionalAuth = () => {\n return async (c: Context, next: Next) => {\n try {\n let token = c.req.header('Authorization')?.replace('Bearer ', '')\n \n if (!token) {\n token = getCookie(c, 'auth_token')\n }\n \n if (token) {\n const jwtSecret = (c.env as any)?.JWT_SECRET\n const payload = await AuthManager.verifyToken(token, jwtSecret)\n if (payload) {\n c.set('user', payload)\n }\n }\n \n return await next()\n } catch (error) {\n // Don't block on auth errors in optional auth\n console.error('Optional auth error:', error)\n return await next()\n }\n }\n}\n","import { MiddlewareHandler } from 'hono'\nimport { metricsTracker } from '../utils/metrics'\n\n/**\n * Middleware to track all HTTP requests for real-time analytics\n * Excludes the metrics endpoint itself to avoid inflating the count\n */\nexport const metricsMiddleware = (): MiddlewareHandler => {\n return async (c, next) => {\n const path = new URL(c.req.url).pathname\n\n // Don't track the metrics endpoint itself to avoid self-inflating counts\n if (path !== '/admin/dashboard/api/metrics') {\n metricsTracker.recordRequest()\n }\n\n // Continue with the request\n await next()\n }\n}\n","/**\n * CSRF Protection Middleware — Signed Double-Submit Cookie\n *\n * Stateless CSRF protection for Cloudflare Workers (no session store needed).\n * Token format: `<nonce>.<hmac>` where HMAC-SHA256 is keyed with JWT_SECRET.\n *\n * Flow:\n * GET — ensureCsrfCookie(): reuse existing valid cookie or set a new one\n * POST/PUT/DELETE/PATCH — validate X-CSRF-Token header === csrf_token cookie, HMAC valid\n *\n * Exempt:\n * - Safe methods (GET, HEAD, OPTIONS)\n * - Auth routes that create sessions (/auth/login*, /auth/register*, etc.)\n * - Public form submissions (/forms/*, /api/forms/*) — NOT /admin/forms/*\n * - Requests with no auth_token cookie (Bearer-only or API-key-only)\n */\n\nimport type { Context, Next } from 'hono'\nimport { getCookie, setCookie } from 'hono/cookie'\n\n// Fallback secret — mirrors auth.ts behavior for local dev without wrangler secret\nconst JWT_SECRET_FALLBACK = 'your-super-secret-jwt-key-change-in-production'\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** Convert ArrayBuffer to URL-safe base64 (no padding). */\nexport function arrayBufferToBase64Url(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]!)\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\n/** Import a string key for HMAC-SHA256. */\nasync function getHmacKey(secret: string): Promise<CryptoKey> {\n const encoder = new TextEncoder()\n return crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify']\n )\n}\n\n// ============================================================================\n// Token Generation & Validation\n// ============================================================================\n\n/**\n * Generate a signed CSRF token: `<nonce>.<hmac_signature>`\n * - nonce = 32 random bytes, base64url-encoded\n * - signature = HMAC-SHA256(nonce, secret), base64url-encoded\n */\nexport async function generateCsrfToken(secret: string): Promise<string> {\n const nonceBytes = new Uint8Array(32)\n crypto.getRandomValues(nonceBytes)\n const nonce = arrayBufferToBase64Url(nonceBytes.buffer)\n\n const key = await getHmacKey(secret)\n const encoder = new TextEncoder()\n const signatureBuffer = await crypto.subtle.sign('HMAC', key, encoder.encode(nonce))\n const signature = arrayBufferToBase64Url(signatureBuffer)\n\n return `${nonce}.${signature}`\n}\n\n/**\n * Validate a signed CSRF token.\n *\n * Checks that the token has the correct `<nonce>.<signature>` format and that\n * the HMAC signature is valid for the given secret. Uses crypto.subtle.verify\n * which provides constant-time comparison.\n *\n * NOTE: No expiry check here — by design. The security property of signed\n * double-submit comes from the unpredictability of the nonce + the\n * secret-bound HMAC, not from time-bounding. The cookie's maxAge (86400s)\n * handles expiry at the browser level.\n */\nexport async function validateCsrfToken(token: string, secret: string): Promise<boolean> {\n if (!token || typeof token !== 'string') return false\n\n const dotIndex = token.indexOf('.')\n if (dotIndex === -1) return false\n\n const nonce = token.substring(0, dotIndex)\n const signature = token.substring(dotIndex + 1)\n\n if (!nonce || !signature) return false\n\n try {\n const key = await getHmacKey(secret)\n const encoder = new TextEncoder()\n\n // Decode the signature from base64url\n const sigPadded = signature.replace(/-/g, '+').replace(/_/g, '/')\n const sigBinary = atob(sigPadded)\n const sigBytes = new Uint8Array(sigBinary.length)\n for (let i = 0; i < sigBinary.length; i++) {\n sigBytes[i] = sigBinary.charCodeAt(i)\n }\n\n // crypto.subtle.verify is constant-time\n return await crypto.subtle.verify('HMAC', key, sigBytes.buffer, encoder.encode(nonce))\n } catch {\n return false\n }\n}\n\n// ============================================================================\n// Default Exempt Paths\n// ============================================================================\n\nconst DEFAULT_EXEMPT_PATHS = [\n '/auth/login',\n '/auth/register',\n '/auth/seed-admin',\n '/auth/accept-invitation',\n '/auth/reset-password',\n '/auth/request-password-reset',\n]\n\n/**\n * Check whether a request path is exempt from CSRF validation.\n * - Exact match or startsWith for auth routes (e.g. /auth/login/form)\n * - /forms/* and /api/forms/* are exempt (public submissions)\n * - /api/search* is exempt (read-only POST for complex query params)\n * - /admin/forms/* is NOT exempt\n */\nfunction isExemptPath(path: string, extraExemptPaths: string[] = []): boolean {\n // Public form routes — NOT /admin/forms/*\n if (path.startsWith('/forms/') || path.startsWith('/api/forms/') || path === '/forms' || path === '/api/forms') {\n return true\n }\n\n // Search API — read-only POST (includes /api/search/click, /api/search/facet-click)\n if (path.startsWith('/api/search')) {\n return true\n }\n\n const allExempt = [...DEFAULT_EXEMPT_PATHS, ...extraExemptPaths]\n for (const exempt of allExempt) {\n if (path === exempt || path.startsWith(exempt + '/')) {\n return true\n }\n }\n\n return false\n}\n\n// ============================================================================\n// Middleware\n// ============================================================================\n\nexport interface CsrfOptions {\n /** Additional paths to exempt from CSRF validation. */\n exemptPaths?: string[]\n}\n\n/**\n * CSRF protection middleware (Signed Double-Submit Cookie).\n *\n * - GET/HEAD/OPTIONS: ensure a valid csrf_token cookie exists\n * - POST/PUT/DELETE/PATCH: validate X-CSRF-Token header matches cookie, HMAC valid\n * - Exempt: auth routes, public /forms/*, Bearer-only, API-key-only\n */\nexport function csrfProtection(options: CsrfOptions = {}) {\n return async (c: Context, next: Next): Promise<Response | void> => {\n const method = c.req.method.toUpperCase()\n const path = new URL(c.req.url).pathname\n const secret = c.env?.JWT_SECRET || JWT_SECRET_FALLBACK\n\n // Warn if using fallback secret in production\n if (c.env?.ENVIRONMENT === 'production' && !c.env?.JWT_SECRET) {\n console.warn(\n '[CSRF] WARNING: JWT_SECRET is not set in production. ' +\n 'CSRF tokens are signed with the fallback key, which is insecure.'\n )\n }\n\n // Safe methods — just ensure cookie, then pass through\n if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') {\n await ensureCsrfCookie(c, secret)\n await next()\n return\n }\n\n // Exempt paths — pass through without validation\n if (isExemptPath(path, options.exemptPaths)) {\n await next()\n return\n }\n\n // Bearer-only or API-key-only requests (no auth_token cookie) — exempt\n const authCookie = getCookie(c, 'auth_token')\n if (!authCookie) {\n await next()\n return\n }\n\n // State-changing request with cookie auth — validate CSRF\n const cookieToken = getCookie(c, 'csrf_token')\n let headerToken = c.req.header('X-CSRF-Token')\n\n // Fallback: check _csrf field in form-encoded body (regular HTML form submissions)\n if (!headerToken) {\n const contentType = c.req.header('Content-Type') || ''\n if (contentType.includes('application/x-www-form-urlencoded') || contentType.includes('multipart/form-data')) {\n try {\n const body = await c.req.parseBody()\n headerToken = body['_csrf'] as string | undefined\n } catch {\n // Body not parseable — leave headerToken undefined\n }\n }\n }\n\n if (!cookieToken || !headerToken) {\n return csrfError(c, 'CSRF token missing')\n }\n\n if (cookieToken !== headerToken) {\n return csrfError(c, 'CSRF token mismatch')\n }\n\n const isValid = await validateCsrfToken(cookieToken, secret)\n if (!isValid) {\n return csrfError(c, 'CSRF token invalid')\n }\n\n await next()\n }\n}\n\n/**\n * Ensure a valid CSRF cookie exists. Check-then-reuse: if the existing cookie\n * has a valid HMAC signature, reuse it (no new Set-Cookie header). Only\n * generate a fresh token when the cookie is missing or has an invalid signature.\n */\nasync function ensureCsrfCookie(c: Context, secret: string): Promise<void> {\n const existing = getCookie(c, 'csrf_token')\n\n if (existing) {\n const isValid = await validateCsrfToken(existing, secret)\n if (isValid) {\n // Reuse existing valid token — no Set-Cookie needed\n c.set('csrfToken', existing)\n return\n }\n }\n\n // Generate fresh token\n const token = await generateCsrfToken(secret)\n c.set('csrfToken', token)\n\n const isDev = c.env?.ENVIRONMENT === 'development' || !c.env?.ENVIRONMENT\n setCookie(c, 'csrf_token', token, {\n httpOnly: false, // JS must read this cookie\n secure: !isDev,\n sameSite: 'Strict',\n path: '/',\n maxAge: 86400, // 24 hours — browser-side expiry\n })\n}\n\n/** Return a 403 CSRF error — HTML for browser requests, JSON for API. */\nfunction csrfError(c: Context, message: string): Response {\n const accept = c.req.header('Accept') || ''\n if (accept.includes('text/html')) {\n return c.html(\n `<!DOCTYPE html><html><head><title>403 Forbidden</title></head>` +\n `<body><h1>403 Forbidden</h1><p>${message}</p></body></html>`,\n 403\n )\n }\n return c.json({ error: message, status: 403 }, 403)\n}\n","import { Context, Next } from 'hono'\n\ninterface RateLimitOptions {\n max: number\n windowMs: number\n keyPrefix: string\n}\n\ninterface RateLimitEntry {\n count: number\n resetAt: number\n}\n\n/**\n * KV-based sliding window rate limiter middleware.\n * Gracefully skips if CACHE_KV binding is not available.\n */\nexport function rateLimit(options: RateLimitOptions) {\n const { max, windowMs, keyPrefix } = options\n\n return async (c: Context, next: Next) => {\n const kv = (c.env as any)?.CACHE_KV\n if (!kv) {\n // No KV binding available — skip rate limiting\n return await next()\n }\n\n const ip = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown'\n const key = `ratelimit:${keyPrefix}:${ip}`\n\n try {\n const now = Date.now()\n const stored = await kv.get(key, 'json') as RateLimitEntry | null\n\n let entry: RateLimitEntry\n if (stored && stored.resetAt > now) {\n entry = stored\n } else {\n entry = { count: 0, resetAt: now + windowMs }\n }\n\n entry.count++\n\n // Calculate TTL in seconds (KV expiration)\n const ttlSeconds = Math.ceil((entry.resetAt - now) / 1000)\n\n if (entry.count > max) {\n // Store the updated count even when rejecting\n await kv.put(key, JSON.stringify(entry), { expirationTtl: Math.max(ttlSeconds, 1) })\n\n const retryAfter = Math.ceil((entry.resetAt - now) / 1000)\n c.header('Retry-After', String(retryAfter))\n c.header('X-RateLimit-Limit', String(max))\n c.header('X-RateLimit-Remaining', '0')\n c.header('X-RateLimit-Reset', String(Math.ceil(entry.resetAt / 1000)))\n return c.json({ error: 'Too many requests. Please try again later.' }, 429)\n }\n\n await kv.put(key, JSON.stringify(entry), { expirationTtl: Math.max(ttlSeconds, 1) })\n\n c.header('X-RateLimit-Limit', String(max))\n c.header('X-RateLimit-Remaining', String(max - entry.count))\n c.header('X-RateLimit-Reset', String(Math.ceil(entry.resetAt / 1000)))\n\n return await next()\n } catch (error) {\n // Rate limiting should never break the app\n console.error('Rate limiter error (non-fatal):', error)\n return await next()\n }\n }\n}\n","import { Context, Next } from 'hono'\n\n/**\n * Security headers middleware.\n * Sets standard security headers on every response.\n * Skips HSTS in development to avoid local dev issues.\n */\nexport const securityHeadersMiddleware = () => {\n return async (c: Context, next: Next) => {\n await next()\n\n c.header('X-Content-Type-Options', 'nosniff')\n c.header('X-Frame-Options', 'SAMEORIGIN')\n c.header('Referrer-Policy', 'strict-origin-when-cross-origin')\n c.header('Permissions-Policy', 'camera=(), microphone=(), geolocation=()')\n\n // Only set HSTS in non-development environments\n const environment = (c.env as any)?.ENVIRONMENT\n if (environment !== 'development') {\n c.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')\n }\n }\n}\n","/**\n * Middleware Module Exports\n *\n * Request processing middleware for SonicJS\n *\n * Note: Most middleware is currently in the monolith and will be migrated later.\n * For now, we only export the bootstrap middleware which is used for system initialization.\n */\n\n// Bootstrap middleware\nexport { bootstrapMiddleware, verifySecurityConfig } from './bootstrap'\n\n// Auth middleware\nexport { AuthManager, requireAuth, requireRole, optionalAuth } from './auth'\n\n// Metrics middleware\nexport { metricsMiddleware } from './metrics'\n\n// CSRF protection middleware\nexport { csrfProtection, generateCsrfToken, validateCsrfToken } from './csrf'\n\n// Rate limiting middleware\nexport { rateLimit } from './rate-limit'\n\n// Re-export types and functions that are referenced but implemented in monolith\n// These are placeholder exports to maintain API compatibility\nexport type Permission = string\nexport type UserPermissions = {\n userId: string\n permissions: Permission[]\n}\n\n// Middleware stubs - these return pass-through middleware that call next()\nexport const loggingMiddleware: any = () => async (_c: any, next: any) => await next()\nexport const detailedLoggingMiddleware: any = () => async (_c: any, next: any) => await next()\nexport const securityLoggingMiddleware: any = () => async (_c: any, next: any) => await next()\nexport const performanceLoggingMiddleware: any = () => async (_c: any, next: any) => await next()\nexport const cacheHeaders: any = () => async (_c: any, next: any) => await next()\nexport const compressionMiddleware: any = async (_c: any, next: any) => await next()\nexport { securityHeadersMiddleware as securityHeaders } from './security-headers'\n\n// Other stubs\nexport const PermissionManager: any = {}\nexport const requirePermission: any = () => async (_c: any, next: any) => await next()\nexport const requireAnyPermission: any = () => async (_c: any, next: any) => await next()\nexport const logActivity: any = () => {}\nexport const requireActivePlugin: any = () => async (_c: any, next: any) => await next()\nexport const requireActivePlugins: any = () => async (_c: any, next: any) => await next()\nexport const getActivePlugins: any = () => []\nexport const isPluginActive: any = () => false\n"]}
@@ -1705,6 +1705,13 @@ CREATE INDEX IF NOT EXISTS idx_forms_turnstile ON forms(turnstile_enabled);
1705
1705
  filename: "032_user_profiles.sql",
1706
1706
  description: "Migration 032: User Profiles",
1707
1707
  sql: "-- User Profiles Table (Core Migration)\n-- Stores extended user profile data separate from auth concerns\n-- Required by admin-users.ts for user edit page profile management\n--\n-- Originally introduced as app-level migration (my-sonicjs-app/migrations/018_user_profiles.sql)\n-- in upstream PR #508. Core routes (admin-users.ts) were updated to query this table in PR #512,\n-- but no corresponding core migration was added. This migration corrects that gap.\n--\n-- IF NOT EXISTS guards ensure idempotency for databases that already have the table\n-- from the app-level migration.\n\nCREATE TABLE IF NOT EXISTS user_profiles (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,\n\n display_name TEXT,\n bio TEXT,\n company TEXT,\n job_title TEXT,\n website TEXT,\n location TEXT,\n date_of_birth INTEGER,\n\n created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),\n updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)\n);\n\n-- Index for fast user lookups\nCREATE INDEX IF NOT EXISTS idx_user_profiles_user_id ON user_profiles(user_id);\n\n-- Trigger to auto-update updated_at timestamp\nCREATE TRIGGER IF NOT EXISTS user_profiles_updated_at\n AFTER UPDATE ON user_profiles\nBEGIN\n UPDATE user_profiles SET updated_at = strftime('%s', 'now') * 1000 WHERE id = NEW.id;\nEND;\n"
1708
+ },
1709
+ {
1710
+ id: "033",
1711
+ name: "Form Content Integration",
1712
+ filename: "033_form_content_integration.sql",
1713
+ description: "Migration 033: Form Content Integration",
1714
+ sql: "-- Migration 033: Form-Content Integration\n-- Adds bridge columns to link forms to collections and submissions to content items\n\n-- Add source_type and source_id to collections for form-derived collections\nALTER TABLE collections ADD COLUMN source_type TEXT DEFAULT 'user';\nALTER TABLE collections ADD COLUMN source_id TEXT;\n\n-- Index for efficient lookup of form-derived collections\nCREATE INDEX IF NOT EXISTS idx_collections_source ON collections(source_type, source_id);\n\n-- Add content_id to form_submissions for linking to content items\nALTER TABLE form_submissions ADD COLUMN content_id TEXT REFERENCES content(id);\n\n-- Index for efficient lookup by content_id\nCREATE INDEX IF NOT EXISTS idx_form_submissions_content_id ON form_submissions(content_id);\n\n-- Create system user for anonymous form submissions\nINSERT OR IGNORE INTO users (id, email, username, first_name, last_name, password_hash, role, is_active, created_at, updated_at)\nVALUES ('system-form-submission', 'system-forms@sonicjs.internal', 'system-forms', 'Form', 'Submission', NULL, 'viewer', 0, strftime('%s','now') * 1000, strftime('%s','now') * 1000);\n"
1708
1715
  }
1709
1716
  ];
1710
1717
  var migrationsByIdMap = new Map(
@@ -2125,5 +2132,5 @@ var MigrationService = class {
2125
2132
  };
2126
2133
 
2127
2134
  export { MigrationService };
2128
- //# sourceMappingURL=chunk-6O3RJV3C.js.map
2129
- //# sourceMappingURL=chunk-6O3RJV3C.js.map
2135
+ //# sourceMappingURL=chunk-H55AYIRI.js.map
2136
+ //# sourceMappingURL=chunk-H55AYIRI.js.map