radix-cms 0.1.0 → 1.0.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 (59) hide show
  1. package/.env.example +17 -0
  2. package/CHANGELOG.md +10 -0
  3. package/CONTRIBUTING.md +40 -0
  4. package/LICENSE +197 -0
  5. package/NOTICE +3 -0
  6. package/README.md +126 -56
  7. package/SECURITY.md +22 -0
  8. package/dist/app.js +80 -0
  9. package/dist/config/db.js +66 -0
  10. package/dist/init/dbInit.js +132 -0
  11. package/dist/init/dbInit2.js +234 -0
  12. package/dist/middleware/auth.js +48 -0
  13. package/dist/plugins/my-seo-plugin/index.js +29 -0
  14. package/dist/routes/admin.js +808 -0
  15. package/dist/routes/index.js +218 -0
  16. package/dist/sql/radix.sql +118 -0
  17. package/dist/types/plugin.js +10 -0
  18. package/dist/utils/PluginManager.js +112 -0
  19. package/dist/utils/settings.js +80 -0
  20. package/dist/utils/themeScanner.js +33 -0
  21. package/dist/utils/versionCheck.js +74 -0
  22. package/docs/CMS_CODE_GUIDE.md +372 -0
  23. package/docs/CMS_USER_GUIDE.md +344 -0
  24. package/docs/Publishing Guidelines.txt +14 -0
  25. package/package.json +62 -6
  26. package/public/css/admin.css +178 -0
  27. package/public/css/fallback.css +53 -0
  28. package/public/hello.html +1 -0
  29. package/views/admin/create-page.ejs +82 -0
  30. package/views/admin/dashboard.ejs +44 -0
  31. package/views/admin/edit-page.ejs +87 -0
  32. package/views/admin/edit-user.ejs +45 -0
  33. package/views/admin/login.ejs +39 -0
  34. package/views/admin/media.ejs +79 -0
  35. package/views/admin/pages.ejs +82 -0
  36. package/views/admin/partials/footer.ejs +4 -0
  37. package/views/admin/partials/header.ejs +57 -0
  38. package/views/admin/settings.ejs +91 -0
  39. package/views/admin/setup-admin.ejs +69 -0
  40. package/views/admin/setup-db.ejs +112 -0
  41. package/views/admin/themes.ejs +24 -0
  42. package/views/admin/updates.ejs +79 -0
  43. package/views/admin/users.ejs +107 -0
  44. package/views/defaults/404.ejs +20 -0
  45. package/views/defaults/500.ejs +20 -0
  46. package/views/layouts/admin-layout.ejs +18 -0
  47. package/views/pages/gallery.ejs +5 -0
  48. package/views/pages/hello.ejs +4 -0
  49. package/views/pages/services.ejs +3 -0
  50. package/views/themes/default/assets/css/style.css +199 -0
  51. package/views/themes/default/assets/js/main.js +8 -0
  52. package/views/themes/default/index.ejs +55 -0
  53. package/views/themes/landing/assets/css/style.css +6 -0
  54. package/views/themes/landing/assets/js/main.js +8 -0
  55. package/views/themes/landing/index.ejs +26 -0
  56. package/views/themes/test/default.ejs +0 -0
  57. package/views/themes/test/full-width.ejs +0 -0
  58. package/views/themes/test/landing-page.ejs +0 -0
  59. package/index.js +0 -15
@@ -0,0 +1,79 @@
1
+ <%- include('partials/header', { title: 'Media Library', activeNav: 'media' }) %>
2
+
3
+ <!-- Dropzone CSS & JS CDN -->
4
+ <link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" type="text/css" />
5
+ <script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
6
+
7
+ <!-- Drag & Drop Upload Card -->
8
+ <div class="card" style="margin-bottom: 25px;">
9
+ <h2 style="font-size: 1.25rem; margin-bottom: 10px;">Upload Files</h2>
10
+ <p style="font-size: 0.85rem; color: #64748b; margin-bottom: 15px;">Drag & drop images here or click to upload. Files manually copied to <code>/public/uploads</code> will also automatically appear below.</p>
11
+
12
+ <form action="/admin/media/upload" class="dropzone" id="mediaDropzone" style="border: 2px dashed #cbd5e1; border-radius: 8px; background: #f8fafc; padding: 20px; text-align: center; cursor: pointer;">
13
+ <div class="dz-message" data-dz-message>
14
+ <span style="font-size: 2rem;">📁</span>
15
+ <p style="font-weight: 600; margin-top: 8px;">Drop files here or click to upload</p>
16
+ </div>
17
+ </form>
18
+ </div>
19
+
20
+ <!-- Media Grid Card -->
21
+ <div class="card">
22
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
23
+ <h1 style="font-size: 1.5rem;">Media Library (<%= media.length %>)</h1>
24
+ </div>
25
+
26
+ <% if (media && media.length > 0) { %>
27
+ <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 20px;">
28
+ <% media.forEach(file => { %>
29
+ <div style="border: 1px solid var(--border-color); border-radius: 8px; overflow: hidden; background: #fff; display: flex; flex-direction: column;">
30
+ <div style="height: 140px; background: #f1f5f9; display: flex; align-items: center; justify-content: center; overflow: hidden;">
31
+ <img src="<%= file.url %>" alt="<%= file.name %>" style="max-width: 100%; max-height: 100%; object-fit: contain;">
32
+ </div>
33
+ <div style="padding: 10px; font-size: 0.8rem; flex-grow: 1; display: flex; flex-direction: column; justify-content: space-between;">
34
+ <div>
35
+ <div style="font-weight: 600; word-break: break-all; margin-bottom: 4px;" title="<%= file.name %>"><%= file.name %></div>
36
+ <div style="color: #64748b; font-size: 0.75rem;"><%= file.size %></div>
37
+ </div>
38
+ <div style="margin-top: 10px; display: flex; justify-content: space-between; align-items: center;">
39
+ <button type="button" onclick="copyUrl('<%= file.url %>')" class="btn" style="padding: 3px 8px; font-size: 0.75rem; background: #e2e8f0; color: #334155;">Copy URL</button>
40
+
41
+ <% if (user && user.role === 'Administrator') { %>
42
+ <form action="/admin/media/delete" method="POST" onsubmit="return confirm('Delete this image?');" style="display: inline;">
43
+ <input type="hidden" name="filename" value="<%= file.name %>">
44
+ <button type="submit" class="btn btn-danger" style="padding: 3px 8px; font-size: 0.75rem;">Delete</button>
45
+ </form>
46
+ <% } %>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ <% }) %>
51
+ </div>
52
+ <% } else { %>
53
+ <p style="color: #64748b; text-align: center; padding: 40px 0;">No media files uploaded yet. Drag files into the box above or drop images into <code>/public/uploads/</code>.</p>
54
+ <% } %>
55
+ </div>
56
+
57
+ <script>
58
+ // Initialize Dropzone
59
+ Dropzone.options.mediaDropzone = {
60
+ paramName: "file",
61
+ maxFilesize: 10, // MB
62
+ acceptedFiles: "image/*",
63
+ init: function() {
64
+ this.on("queuecomplete", function() {
65
+ // Auto-refresh page once all files in the batch finish uploading
66
+ window.location.reload();
67
+ });
68
+ }
69
+ };
70
+
71
+ // Helper to copy image relative URL to clipboard
72
+ function copyUrl(url) {
73
+ navigator.clipboard.writeText(url).then(() => {
74
+ alert('Image URL copied: ' + url);
75
+ });
76
+ }
77
+ </script>
78
+
79
+ <%- include('partials/footer') %>
@@ -0,0 +1,82 @@
1
+ <%- include('partials/header', { title: 'Pages Management', activeNav: 'pages' }) %>
2
+
3
+ <div class="card">
4
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
5
+ <div>
6
+ <h1 style="font-size: 1.5rem; margin-bottom: 6px;"><%= showTrash ? 'Trash Bin' : 'Pages Management' %></h1>
7
+
8
+ <div style="display: flex; gap: 12px; font-size: 0.85rem;">
9
+ <a href="/admin/pages" style="color: <%= !showTrash ? '#2563eb' : '#64748b' %>; font-weight: <%= !showTrash ? '600' : 'normal' %>; text-decoration: none;">All Active Pages</a> |
10
+ <a href="/admin/pages?view=trash" style="color: <%= showTrash ? '#ef4444' : '#64748b' %>; font-weight: <%= showTrash ? '600' : 'normal' %>; text-decoration: none;">Trash 🗑️</a>
11
+ </div>
12
+ </div>
13
+
14
+ <% if (!showTrash) { %>
15
+ <a href="/admin/create" class="btn btn-primary">+ Create New Page</a>
16
+ <% } %>
17
+ </div>
18
+
19
+ <table class="data-table">
20
+ <thead>
21
+ <tr>
22
+ <th>Title</th>
23
+ <th>Status</th>
24
+ <th>Min Access</th>
25
+ <th>Publish Date</th>
26
+ <th>Actions</th>
27
+ </tr>
28
+ </thead>
29
+ <tbody>
30
+ <% if (pages && pages.length > 0) { %>
31
+ <% pages.forEach(page => { %>
32
+ <tr>
33
+ <td>
34
+ <strong><%= page.title %></strong>
35
+ <% if (page.isStaticOverride) { %>
36
+ <span style="display: inline-block; background-color: #fef3c7; color: #b45309; border: 1px solid #fde68a; font-size: 0.7rem; font-weight: 600; padding: 2px 6px; border-radius: 4px; margin-left: 6px;" title="This page content is overridden by a file in /views/pages/">
37
+ ⚡ Static Override
38
+ </span>
39
+ <% } %>
40
+ <br>
41
+ <small style="color: #64748b;">/<%= page.slug %></small>
42
+ </td>
43
+ <td>
44
+ <span style="padding: 3px 8px; border-radius: 12px; font-size: 0.75rem; font-weight: 600;
45
+ background: <%= page.status === 'Published' ? '#dcfce7' : page.status === 'Draft' ? '#f1f5f9' : page.status === 'Pending Review' ? '#fef3c7' : page.status === 'Archived' ? '#e2e8f0' : '#fee2e2' %>;
46
+ color: <%= page.status === 'Published' ? '#166534' : page.status === 'Draft' ? '#475569' : page.status === 'Pending Review' ? '#92400e' : page.status === 'Archived' ? '#334155' : '#991b1b' %>;">
47
+ <%= page.status %>
48
+ </span>
49
+ </td>
50
+ <td><code><%= page.min_role %></code></td>
51
+ <td><%= new Date(page.publish_at).toLocaleString() %></td>
52
+ <td>
53
+ <% if (!showTrash) { %>
54
+ <a href="/admin/edit/<%= page.id %>" class="btn btn-primary" style="padding: 4px 10px; font-size: 0.8rem;">Edit</a>
55
+ <form action="/admin/delete/<%= page.id %>" method="POST" style="display: inline;" onsubmit="return confirm('Move page to trash?');">
56
+ <button type="submit" class="btn btn-danger" style="padding: 4px 10px; font-size: 0.8rem;">Trash</button>
57
+ </form>
58
+ <% } else { %>
59
+ <form action="/admin/restore/<%= page.id %>" method="POST" style="display: inline;">
60
+ <button type="submit" class="btn" style="padding: 4px 10px; font-size: 0.8rem; background: #e2e8f0; color: #1e293b;">Restore</button>
61
+ </form>
62
+ <% if (user && user.role === 'Administrator') { %>
63
+ <form action="/admin/permanent-delete/<%= page.id %>" method="POST" style="display: inline;" onsubmit="return confirm('Permanently delete this page? This action cannot be undone.');">
64
+ <button type="submit" class="btn btn-danger" style="padding: 4px 10px; font-size: 0.8rem;">Delete Permanently</button>
65
+ </form>
66
+ <% } %>
67
+ <% } %>
68
+ </td>
69
+ </tr>
70
+ <% }) %>
71
+ <% } else { %>
72
+ <tr>
73
+ <td colspan="5" style="text-align: center; color: #64748b; padding: 20px;">
74
+ <%= showTrash ? 'Trash is empty.' : 'No pages found.' %>
75
+ </td>
76
+ </tr>
77
+ <% } %>
78
+ </tbody>
79
+ </table>
80
+ </div>
81
+
82
+ <%- include('partials/footer') %>
@@ -0,0 +1,4 @@
1
+ </main>
2
+ </div>
3
+ </body>
4
+ </html>
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title><%= typeof title !== 'undefined' ? title : 'Admin' %> | Radix Engine</title>
7
+ <link rel="stylesheet" href="/css/admin.css">
8
+ <% if (typeof tinyApiKey !== 'undefined' && tinyApiKey) { %>
9
+ <script src="https://cdn.tiny.cloud/1/<%= tinyApiKey %>/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script>
10
+ <% } %>
11
+ </head>
12
+ <body>
13
+
14
+ <!-- Sidebar Navigation Shell -->
15
+ <aside class="admin-sidebar">
16
+ <div class="brand">Radix<span>CMS</span></div>
17
+ <ul class="admin-nav">
18
+ <li class="<%= activeNav === 'dashboard' ? 'active' : '' %>">
19
+ <a href="/admin"><span class="icon">📊</span> Dashboard</a>
20
+ </li>
21
+ <li class="<%= activeNav === 'updates' ? 'active' : '' %>">
22
+ <a href="/admin/updates"><span class="icon">🚀</span> Updates</a>
23
+ </li>
24
+ <li class="<%= activeNav === 'pages' ? 'active' : '' %>">
25
+ <a href="/admin/pages"><span class="icon">📄</span> Pages</a>
26
+ </li>
27
+ <li class="<%= activeNav === 'themes' ? 'active' : '' %>">
28
+ <a href="/admin/themes"><span class="icon">🎨</span> Themes</a>
29
+ </li>
30
+ <li class="<%= activeNav === 'media' ? 'active' : '' %>">
31
+ <a href="/admin/media"><span class="icon">🖼️</span> Media Library</a>
32
+ </li>
33
+ <li class="<%= activeNav === 'users' ? 'active' : '' %>">
34
+ <a href="/admin/users"><span class="icon">👥</span> Users & Roles</a>
35
+ </li>
36
+ <li class="<%= activeNav === 'settings' ? 'active' : '' %>">
37
+ <a href="/admin/settings"><span class="icon">⚙️</span> Settings</a>
38
+ </li>
39
+ </ul>
40
+ </aside>
41
+
42
+ <!-- Main Content Area Wrapper -->
43
+ <div class="admin-wrapper">
44
+ <header class="admin-header">
45
+ <div class="header-left">
46
+ <a href="/" target="_blank" style="text-decoration: none; color: #64748b; font-size: 0.9rem;">
47
+ 🏠 View Site
48
+ </a>
49
+ </div>
50
+ <div class="header-right">
51
+ <span>Logged in as <strong><%= user ? user.username : 'Admin' %></strong></span>
52
+ <span class="user-badge"><%= user ? user.role : 'User' %></span>
53
+ <a href="/admin/logout" style="color: #ef4444; text-decoration: none; font-weight: 500;">Logout</a>
54
+ </div>
55
+ </header>
56
+
57
+ <main class="admin-content">
@@ -0,0 +1,91 @@
1
+ <%- include('partials/header', { title: 'Settings', activeNav: 'settings' }) %>
2
+
3
+ <h1 style="font-size: 1.5rem; margin-bottom: 20px;">System Settings</h1>
4
+
5
+ <% if (message) { %>
6
+ <div style="background: #dcfce7; color: #15803d; border: 1px solid #bbf7d0; padding: 12px 16px; border-radius: 6px; margin-bottom: 20px;">
7
+ <%= message %>
8
+ </div>
9
+ <% } %>
10
+
11
+ <% if (error) { %>
12
+ <div style="background: #fee2e2; color: #b91c1c; border: 1px solid #fca5a5; padding: 12px 16px; border-radius: 6px; margin-bottom: 20px;">
13
+ <%= error %>
14
+ </div>
15
+ <% } %>
16
+
17
+ <form action="/admin/settings" method="POST">
18
+ <!-- Branding Section -->
19
+ <div class="card" style="margin-bottom: 25px;">
20
+ <h2 style="font-size: 1.2rem; margin-bottom: 15px;">Site Branding</h2>
21
+ <div style="margin-bottom: 15px;">
22
+ <label style="display: block; font-weight: 600; margin-bottom: 5px;">Website Logo Path/URL</label>
23
+ <div style="display: flex; gap: 10px;">
24
+ <input type="text" id="site_logo" name="site_logo" value="<%= settings.site_logo || '' %>" placeholder="/uploads/logo.png" style="flex: 1; padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px;">
25
+ <a href="/admin/media" target="_blank" class="btn" style="background: #e2e8f0; color: #334155;">Browse Media</a>
26
+ </div>
27
+ <small style="color: #64748b; margin-top: 4px; display: block;">Enter path from Media Library or external image URL.</small>
28
+ </div>
29
+
30
+ <% if (settings.site_logo) { %>
31
+ <div style="margin-top: 10px; padding: 10px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 4px; width: fit-content;">
32
+ <small style="display: block; color: #64748b; margin-bottom: 5px;">Logo Preview:</small>
33
+ <img src="<%= settings.site_logo %>" alt="Site Logo" style="max-height: 50px; object-fit: contain;">
34
+ </div>
35
+ <% } %>
36
+ </div>
37
+
38
+ <!-- SMTP Configuration Section -->
39
+ <div class="card" style="margin-bottom: 25px;">
40
+ <h2 style="font-size: 1.2rem; margin-bottom: 15px;">Mail Server Configuration (SMTP)</h2>
41
+
42
+ <div style="display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 15px; margin-bottom: 15px;">
43
+ <div>
44
+ <label style="display: block; font-weight: 600; margin-bottom: 5px;">SMTP Host</label>
45
+ <input type="text" name="mail_host" value="<%= settings.mail_host || '192.168.11.10' %>" required style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px;">
46
+ </div>
47
+ <div>
48
+ <label style="display: block; font-weight: 600; margin-bottom: 5px;">Port</label>
49
+ <input type="number" name="mail_port" value="<%= settings.mail_port || '25' %>" required style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px;">
50
+ </div>
51
+ <div>
52
+ <label style="display: block; font-weight: 600; margin-bottom: 5px;">Encryption</label>
53
+ <select name="mail_secure" style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px;">
54
+ <option value="0" <%= settings.mail_secure !== '1' ? 'selected' : '' %>>None / STARTTLS</option>
55
+ <option value="1" <%= settings.mail_secure === '1' ? 'selected' : '' %>>SSL / TLS</option>
56
+ </select>
57
+ </div>
58
+ </div>
59
+
60
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 15px;">
61
+ <div>
62
+ <label style="display: block; font-weight: 600; margin-bottom: 5px;">SMTP Username</label>
63
+ <input type="text" name="mail_user" value="<%= settings.mail_user || '' %>" style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px;">
64
+ </div>
65
+ <div>
66
+ <label style="display: block; font-weight: 600; margin-bottom: 5px;">SMTP Password</label>
67
+ <input type="password" name="mail_pass" value="<%= settings.mail_pass || '' %>" style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px;">
68
+ </div>
69
+ </div>
70
+
71
+ <div style="margin-bottom: 15px;">
72
+ <label style="display: block; font-weight: 600; margin-bottom: 5px;">Sender Email ("From" Address)</label>
73
+ <input type="email" name="mail_from" value="<%= settings.mail_from || 'noreply@radixcms.local' %>" style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px;">
74
+ </div>
75
+
76
+ <button type="submit" class="btn btn-primary">Save Settings</button>
77
+ </div>
78
+ </form>
79
+
80
+ <!-- Test Email Card -->
81
+ <div class="card">
82
+ <h2 style="font-size: 1.2rem; margin-bottom: 10px;">Send Test Email</h2>
83
+ <p style="color: #64748b; font-size: 0.9rem; margin-bottom: 15px;">Verify your SMTP configuration by sending a test message.</p>
84
+
85
+ <form action="/admin/settings/test-email" method="POST" style="display: flex; gap: 10px; max-width: 500px;">
86
+ <input type="email" name="recipient_email" required placeholder="recipient@example.com" style="flex: 1; padding: 8px; border: 1px solid #cbd5e1; border-radius: 4px;">
87
+ <button type="submit" class="btn" style="background: #059669; color: #fff;">Send Test Email</button>
88
+ </form>
89
+ </div>
90
+
91
+ <%- include('partials/footer') %>
@@ -0,0 +1,69 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Radix - Site & Admin Setup</title>
6
+ <style>
7
+ body { font-family: system-ui, sans-serif; background: #f7fafc; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; }
8
+ .card { background: #fff; padding: 28px; border-radius: 8px; box-shadow: 0 6px 18px rgba(0,0,0,0.08); width: 100%; max-width: 640px; }
9
+ h1 { margin-top: 0; font-size: 1.5rem; text-align: center; }
10
+ .form-row { display: flex; gap: 12px; }
11
+ .form-group { margin-bottom: 12px; flex: 1; }
12
+ label { display:block; font-weight:600; margin-bottom:6px }
13
+ input[type="text"], input[type="password"], input[type="email"] { width:100%; padding:10px; border:1px solid #e2e8f0; border-radius:4px }
14
+ .btn { background:#2b6cb0; color:#fff; border:none; padding:10px 14px; border-radius:4px; cursor:pointer; font-weight:700 }
15
+ .error { background:#fed7d7; color:#9b2c2c; padding:8px; border-radius:4px; margin-bottom:12px }
16
+ .hint { font-size:0.9rem; color:#4a5568 }
17
+ .checkbox { display:flex; align-items:center; gap:8px }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <div class="card">
22
+ <h1>Phase 2: Site Information & Admin Setup</h1>
23
+
24
+ <% if (error) { %>
25
+ <div class="error"><%= error %></div>
26
+ <% } %>
27
+
28
+ <p class="hint">After validating the database connection, provide the site title and administrator account details.</p>
29
+
30
+ <form action="/admin/setup/admin" method="POST">
31
+ <div class="form-group">
32
+ <label for="siteTitle">Site Title</label>
33
+ <input type="text" id="siteTitle" name="siteTitle" value="<%= values.siteTitle || 'My Radix Site' %>" required />
34
+ </div>
35
+
36
+ <div class="form-row">
37
+ <div class="form-group">
38
+ <label for="username">Admin Username</label>
39
+ <input type="text" id="username" name="username" value="<%= values.username || '' %>" required />
40
+ </div>
41
+ <div class="form-group">
42
+ <label for="email">Admin Email</label>
43
+ <input type="email" id="email" name="email" value="<%= values.email || '' %>" required />
44
+ </div>
45
+ </div>
46
+
47
+ <div class="form-row">
48
+ <div class="form-group">
49
+ <label for="password">Password</label>
50
+ <input type="password" id="password" name="password" required />
51
+ </div>
52
+ <div class="form-group">
53
+ <label for="passwordConfirm">Confirm Password</label>
54
+ <input type="password" id="passwordConfirm" name="passwordConfirm" required />
55
+ </div>
56
+ </div>
57
+
58
+ <div class="form-group checkbox">
59
+ <input type="checkbox" id="searchEngineVisibility" name="searchEngineVisibility" <%= values.searchEngineVisibility ? 'checked' : '' %> />
60
+ <label for="searchEngineVisibility">Discourage search engines from indexing this site (recommended for local/staging)</label>
61
+ </div>
62
+
63
+ <div style="text-align:center; margin-top:16px">
64
+ <button type="submit" class="btn">Create Site & Admin Account</button>
65
+ </div>
66
+ </form>
67
+ </div>
68
+ </body>
69
+ </html>
@@ -0,0 +1,112 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Radix - Database Setup</title>
6
+ <style>
7
+ body { font-family: system-ui, sans-serif; background: #f7fafc; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; }
8
+ .card { background: #fff; padding: 28px; border-radius: 8px; box-shadow: 0 6px 18px rgba(0,0,0,0.08); width: 100%; max-width: 640px; }
9
+ h1 { margin-top: 0; font-size: 1.5rem; text-align: center; }
10
+ .form-row { display: flex; gap: 12px; }
11
+ .form-group { margin-bottom: 12px; flex: 1; }
12
+ label { display:block; font-weight:600; margin-bottom:6px }
13
+ input[type="text"], input[type="password"], input[type="number"] { width:100%; padding:10px; border:1px solid #e2e8f0; border-radius:4px }
14
+ .btn { background:#2b6cb0; color:#fff; border:none; padding:10px 14px; border-radius:4px; cursor:pointer; font-weight:700 }
15
+ .error { background:#fed7d7; color:#9b2c2c; padding:8px; border-radius:4px; margin-bottom:12px }
16
+ .hint { font-size:0.9rem; color:#4a5568 }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <div class="card">
21
+ <h1>Phase 1: Database Connection Setup</h1>
22
+
23
+ <% if (error) { %>
24
+ <div class="error"><%= error %></div>
25
+ <% } %>
26
+
27
+ <p class="hint">These details tell Radix where your database is located and how to log into it. Values are saved to your .env file.</p>
28
+
29
+ <form action="/admin/setup/db" method="POST">
30
+ <div class="form-row">
31
+ <div class="form-group">
32
+ <label for="dbName">Database Name</label>
33
+ <input type="text" id="dbName" name="dbName" value="<%= values.dbName || 'radix' %>" required />
34
+ </div>
35
+ <div class="form-group">
36
+ <label for="tablePrefix">Table Prefix</label>
37
+ <input type="text" id="tablePrefix" name="tablePrefix" value="<%= values.tablePrefix || '' %>" placeholder="Optional" />
38
+ </div>
39
+ </div>
40
+
41
+ <div class="form-row">
42
+ <div class="form-group">
43
+ <label for="dbUser">Username</label>
44
+ <input type="text" id="dbUser" name="dbUser" value="<%= values.dbUser || '' %>" required />
45
+ </div>
46
+ <div class="form-group">
47
+ <label for="dbPass">Password</label>
48
+ <input type="password" id="dbPass" name="dbPass" value="" />
49
+ </div>
50
+ </div>
51
+
52
+ <div class="form-row">
53
+ <div class="form-group">
54
+ <label for="dbHost">Database Host</label>
55
+ <input type="text" id="dbHost" name="dbHost" value="<%= values.dbHost || '127.0.0.1' %>" required />
56
+ </div>
57
+ <div class="form-group">
58
+ <label for="dbPort">Port</label>
59
+ <input type="number" id="dbPort" name="dbPort" value="<%= values.dbPort || '3306' %>" />
60
+ </div>
61
+ </div>
62
+
63
+ <div style="text-align:center; margin-top:16px; display:flex; gap:8px; justify-content:center; align-items:center;">
64
+ <button type="button" id="testConn" class="btn" style="background:#059669;">Test Connection</button>
65
+ <button type="submit" class="btn">Save & Initialize Database</button>
66
+ </div>
67
+ <div id="testResult" style="margin-top:12px; text-align:center; display:none;" aria-live="polite"></div>
68
+ </form>
69
+ </div>
70
+
71
+ <script>
72
+ document.getElementById('testConn').addEventListener('click', async function () {
73
+ const dbName = document.getElementById('dbName').value;
74
+ const dbUser = document.getElementById('dbUser').value;
75
+ const dbPass = document.getElementById('dbPass').value;
76
+ const dbHost = document.getElementById('dbHost').value;
77
+ const dbPort = document.getElementById('dbPort').value;
78
+ const resultDiv = document.getElementById('testResult');
79
+
80
+ resultDiv.style.display = 'block';
81
+ resultDiv.style.color = '#0f172a';
82
+ resultDiv.textContent = 'Testing connection...';
83
+
84
+ try {
85
+ const resp = await fetch('/admin/setup/db/test', {
86
+ method: 'POST',
87
+ headers: { 'Content-Type': 'application/json' },
88
+ body: JSON.stringify({ dbName, dbUser, dbPass, dbHost, dbPort })
89
+ });
90
+
91
+ const data = await resp.json();
92
+ if (resp.ok && data.ok) {
93
+ resultDiv.style.background = '#ecfdf5';
94
+ resultDiv.style.border = '1px solid #bbf7d0';
95
+ resultDiv.style.color = '#065f46';
96
+ resultDiv.textContent = data.message || 'Connection successful';
97
+ } else {
98
+ resultDiv.style.background = '#fee2e2';
99
+ resultDiv.style.border = '1px solid #fecaca';
100
+ resultDiv.style.color = '#9b2c2c';
101
+ resultDiv.textContent = (data && data.error) ? data.error : 'Connection failed';
102
+ }
103
+ } catch (err) {
104
+ resultDiv.style.background = '#fee2e2';
105
+ resultDiv.style.border = '1px solid #fecaca';
106
+ resultDiv.style.color = '#9b2c2c';
107
+ resultDiv.textContent = String(err) || 'Connection failed';
108
+ }
109
+ });
110
+ </script>
111
+ </body>
112
+ </html>
@@ -0,0 +1,24 @@
1
+ <%- include('partials/header', { title: 'Themes & Templates', activeNav: 'themes' }) %>
2
+
3
+ <div class="card">
4
+ <h1 style="font-size: 1.5rem; margin-bottom: 8px;">Available Themes & Templates</h1>
5
+ <p style="color: #64748b; margin-bottom: 20px;">Discovered layout files ready to apply across dynamic pages.</p>
6
+
7
+ <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px;">
8
+ <% if (templates && templates.length > 0) { %>
9
+ <% templates.forEach(tpl => { %>
10
+ <div style="border: 1px solid #e2e8f0; padding: 16px; border-radius: 6px; background: #f8fafc;">
11
+ <div style="font-size: 1.2rem; margin-bottom: 6px;">🎨 <strong><%= tpl %></strong></div>
12
+ <small style="color: #64748b; display: block; margin-bottom: 10px;">Template Name: <code><%= tpl %></code></small>
13
+ <span style="display: inline-block; background: #dbeafe; color: #1e40af; font-size: 0.75rem; font-weight: 600; padding: 2px 8px; border-radius: 12px;">
14
+ Active Layout
15
+ </span>
16
+ </div>
17
+ <% }) %>
18
+ <% } else { %>
19
+ <p style="color: #64748b;">No dynamic theme templates found in the themes directory.</p>
20
+ <% } %>
21
+ </div>
22
+ </div>
23
+
24
+ <%- include('partials/footer') %>
@@ -0,0 +1,79 @@
1
+ <%- include('partials/header', { title: 'System Updates', activeNav: 'updates' }) %>
2
+
3
+ <div class="card" style="max-width: 900px; margin: 0 auto;">
4
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
5
+ <h1 style="font-size: 1.5rem;">System Updates</h1>
6
+ <a href="/admin/updates" class="btn" style="background: #e2e8f0; color: #334155; font-size: 0.85rem;">🔄 Check Again</a>
7
+ </div>
8
+
9
+ <% if (message) { %>
10
+ <div style="background: #dcfce7; border: 1px solid #bbf7d0; color: #15803d; padding: 15px; border-radius: 6px; margin-bottom: 20px;">
11
+ ✅ <strong><%= message %></strong>
12
+ </div>
13
+ <% } %>
14
+
15
+ <% if (error || updateInfo.error) { %>
16
+ <div style="background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; padding: 15px; border-radius: 6px; margin-bottom: 20px;">
17
+ ⚠️ <strong>Update Error:</strong> <%= error || updateInfo.error %>
18
+ </div>
19
+ <% } %>
20
+
21
+ <!-- Current Version Overview Card -->
22
+ <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px; padding: 20px; margin-bottom: 20px;">
23
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
24
+ <div>
25
+ <small style="color: #64748b; text-transform: uppercase; font-weight: 600;">Installed Version</small>
26
+ <div style="font-size: 1.8rem; font-weight: bold; color: #1e293b; margin-top: 4px;">
27
+ v<%= updateInfo.currentVersion %>
28
+ </div>
29
+ </div>
30
+ <div>
31
+ <small style="color: #64748b; text-transform: uppercase; font-weight: 600;">Latest Remote Release</small>
32
+ <div style="font-size: 1.8rem; font-weight: bold; color: #2563eb; margin-top: 4px;">
33
+ <%= updateInfo.remoteVersion ? 'v' + updateInfo.remoteVersion : 'Unavailable' %>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+
39
+ <!-- Release Status Details -->
40
+ <% if (updateInfo.hasUpdate && updateInfo.details) { %>
41
+ <div style="background: #f0fdf4; border: 1px solid #bbf7d0; padding: 20px; border-radius: 6px; margin-bottom: 20px;">
42
+ <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
43
+ <span style="font-size: 1.4rem;">🚀</span>
44
+ <h2 style="font-size: 1.2rem; color: #166534; margin: 0;">New Update Available!</h2>
45
+ </div>
46
+ <p style="color: #15803d; font-size: 0.95rem; margin-bottom: 15px;">
47
+ Version <strong><%= updateInfo.remoteVersion %></strong> was released on <strong><%= updateInfo.details.ReleaseDate %></strong>.
48
+ </p>
49
+
50
+ <% if (updateInfo.details.Notes) { %>
51
+ <div style="background: #ffffff; border: 1px solid #cbd5e1; padding: 12px; border-radius: 4px; margin-bottom: 15px; font-size: 0.9rem; color: #334155;">
52
+ <strong>Release Notes:</strong>
53
+ <p style="margin-top: 5px;"><%= updateInfo.details.Notes %></p>
54
+ </div>
55
+ <% } %>
56
+
57
+ <!-- Action Buttons -->
58
+ <div style="display: flex; gap: 12px; align-items: center; margin-top: 15px;">
59
+ <form action="/admin/updates/apply" method="POST" onsubmit="return confirm('Are you sure you want to update Radix CMS codebase? Clean backup recommended prior to update.');">
60
+ <button type="submit" class="btn btn-primary" style="background: #16a34a; border-color: #16a34a;">
61
+ ⚡ Perform Auto-Update
62
+ </button>
63
+ </form>
64
+
65
+ <a href="https://vault.ultraspark.net/radix/<%= updateInfo.details.InstallFile %>" class="btn" style="background: #e2e8f0; color: #334155;" target="_blank" download>
66
+ 📥 Download Zip File
67
+ </a>
68
+ </div>
69
+ </div>
70
+ <% } else if (!updateInfo.error) { %>
71
+ <div style="background: #f0f9ff; border: 1px solid #bae6fd; padding: 20px; border-radius: 6px; text-align: center;">
72
+ <span style="font-size: 2rem;">✅</span>
73
+ <h2 style="font-size: 1.2rem; color: #0369a1; margin-top: 8px;">Radix CMS is up to date</h2>
74
+ <p style="color: #0284c7; font-size: 0.9rem; margin-top: 4px;">You are running the latest release (v<%= updateInfo.currentVersion %>).</p>
75
+ </div>
76
+ <% } %>
77
+ </div>
78
+
79
+ <%- include('partials/footer') %>