domma-cms 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -3
- package/admin/css/admin.css +78 -1
- package/admin/js/api.js +32 -0
- package/admin/js/app.js +24 -7
- package/admin/js/config/sidebar-config.js +8 -0
- package/admin/js/templates/collection-editor.html +80 -0
- package/admin/js/templates/collection-entries.html +36 -0
- package/admin/js/templates/collections.html +12 -0
- package/admin/js/templates/documentation.html +136 -0
- package/admin/js/templates/navigation.html +26 -4
- package/admin/js/templates/page-editor.html +91 -85
- package/admin/js/templates/settings.html +433 -172
- package/admin/js/views/collection-editor.js +487 -0
- package/admin/js/views/collection-entries.js +484 -0
- package/admin/js/views/collections.js +153 -0
- package/admin/js/views/dashboard.js +14 -6
- package/admin/js/views/index.js +9 -3
- package/admin/js/views/login.js +3 -2
- package/admin/js/views/navigation.js +77 -11
- package/admin/js/views/page-editor.js +207 -25
- package/admin/js/views/pages.js +14 -6
- package/admin/js/views/settings.js +137 -2
- package/admin/js/views/users.js +10 -7
- package/bin/cli.js +53 -17
- package/config/auth.json +2 -1
- package/config/content.json +1 -0
- package/config/navigation.json +14 -4
- package/config/plugins.json +0 -18
- package/config/presets.json +4 -8
- package/config/site.json +44 -3
- package/package.json +6 -2
- package/plugins/domma-effects/admin/templates/domma-effects.html +92 -3
- package/plugins/domma-effects/plugin.js +125 -0
- package/plugins/domma-effects/public/inject-body.html +19 -0
- package/plugins/example-analytics/admin/views/analytics.js +2 -2
- package/plugins/example-analytics/plugin.json +8 -0
- package/plugins/example-analytics/stats.json +15 -1
- package/plugins/form-builder/admin/templates/form-editor.html +19 -6
- package/plugins/form-builder/admin/views/form-editor.js +634 -9
- package/plugins/form-builder/admin/views/form-submissions.js +4 -4
- package/plugins/form-builder/admin/views/forms-list.js +5 -5
- package/plugins/form-builder/data/forms/consent.json +104 -0
- package/plugins/form-builder/data/forms/contacts.json +66 -0
- package/plugins/form-builder/data/submissions/consent.json +13 -0
- package/plugins/form-builder/data/submissions/contacts.json +26 -0
- package/plugins/form-builder/plugin.js +62 -11
- package/plugins/form-builder/plugin.json +12 -16
- package/plugins/form-builder/public/form-logic-engine.js +568 -0
- package/plugins/form-builder/public/inject-body.html +88 -6
- package/plugins/form-builder/public/inject-head.html +16 -0
- package/plugins/form-builder/public/package.json +1 -0
- package/public/css/site.css +113 -0
- package/public/js/btt.js +90 -0
- package/public/js/cookie-consent.js +61 -0
- package/public/js/site.js +129 -34
- package/scripts/build.js +129 -0
- package/scripts/seed.js +517 -7
- package/scripts/setup.js +12 -9
- package/server/routes/api/collections.js +301 -0
- package/server/routes/api/settings.js +66 -2
- package/server/server.js +19 -15
- package/server/services/collections.js +430 -0
- package/server/services/content.js +11 -2
- package/server/services/hooks.js +109 -0
- package/server/services/markdown.js +500 -149
- package/server/services/plugins.js +6 -1
- package/server/services/renderer.js +73 -7
- package/server/templates/page.html +38 -3
- package/plugins/back-to-top/admin/templates/back-to-top-settings.html +0 -55
- package/plugins/back-to-top/admin/views/back-to-top-settings.js +0 -44
- package/plugins/back-to-top/config.js +0 -10
- package/plugins/back-to-top/plugin.js +0 -24
- package/plugins/back-to-top/plugin.json +0 -36
- package/plugins/back-to-top/public/inject-body.html +0 -105
- package/plugins/cookie-consent/admin/templates/cookie-consent-settings.html +0 -113
- package/plugins/cookie-consent/admin/views/cookie-consent-settings.js +0 -73
- package/plugins/cookie-consent/config.js +0 -30
- package/plugins/cookie-consent/plugin.js +0 -24
- package/plugins/cookie-consent/plugin.json +0 -36
- package/plugins/cookie-consent/public/inject-body.html +0 -69
- package/plugins/custom-css/admin/templates/custom-css.html +0 -17
- package/plugins/custom-css/admin/views/custom-css.js +0 -35
- package/plugins/custom-css/config.js +0 -1
- package/plugins/custom-css/data/custom.css +0 -0
- package/plugins/custom-css/plugin.js +0 -63
- package/plugins/custom-css/plugin.json +0 -32
- package/plugins/custom-css/public/inject-head.html +0 -1
- package/plugins/form-builder/data/forms/contact.json +0 -52
- package/plugins/form-builder/data/submissions/contact.json +0 -14
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Settings View
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import {api, apiRequest} from '../api.js';
|
|
5
5
|
|
|
6
6
|
export const settingsView = {
|
|
7
7
|
templateUrl: '/admin/js/templates/settings.html',
|
|
8
8
|
|
|
9
9
|
async onMount($container) {
|
|
10
|
+
E.tabs($container.find('#settings-tabs').get(0));
|
|
11
|
+
|
|
10
12
|
const s = await api.settings.get().catch(() => ({}));
|
|
11
13
|
$container.find('#field-site-title').val(s.title || '');
|
|
12
14
|
$container.find('#field-tagline').val(s.tagline || '');
|
|
15
|
+
$container.find('#field-font-family').val(s.fontFamily || 'Roboto');
|
|
16
|
+
$container.find('#field-font-size').val(s.fontSize || 16);
|
|
13
17
|
$container.find('#field-theme').val(s.theme || 'charcoal-dark');
|
|
14
18
|
$container.find('#field-admin-theme').val(s.adminTheme || 'charcoal-dark');
|
|
15
19
|
$container.find('#field-seo-title').val(s.seo?.defaultTitle || '');
|
|
@@ -17,7 +21,14 @@ export const settingsView = {
|
|
|
17
21
|
$container.find('#field-seo-desc').val(s.seo?.defaultDescription || '');
|
|
18
22
|
$container.find('#field-footer-copy').val(s.footer?.copyright || '');
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
// Social fields
|
|
25
|
+
$container.find('#field-social-twitter').val(s.social?.twitter || '');
|
|
26
|
+
$container.find('#field-social-facebook').val(s.social?.facebook || '');
|
|
27
|
+
$container.find('#field-social-instagram').val(s.social?.instagram || '');
|
|
28
|
+
$container.find('#field-social-linkedin').val(s.social?.linkedin || '');
|
|
29
|
+
$container.find('#field-social-github').val(s.social?.github || '');
|
|
30
|
+
$container.find('#field-social-youtube').val(s.social?.youtube || '');
|
|
31
|
+
|
|
21
32
|
$container.find('#field-smtp-host').val(s.smtp?.host || '');
|
|
22
33
|
$container.find('#field-smtp-port').val(s.smtp?.port || 587);
|
|
23
34
|
$container.find('#field-smtp-user').val(s.smtp?.user || '');
|
|
@@ -26,11 +37,50 @@ export const settingsView = {
|
|
|
26
37
|
$container.find('#field-smtp-from-address').val(s.smtp?.fromAddress || '');
|
|
27
38
|
$container.find('#field-smtp-from-name').val(s.smtp?.fromName || '');
|
|
28
39
|
|
|
40
|
+
// Back to Top
|
|
41
|
+
const btt = s.backToTop || {};
|
|
42
|
+
$container.find('#field-btt-enabled').prop('checked', btt.enabled !== false);
|
|
43
|
+
$container.find('#field-btt-threshold').val(btt.scrollThreshold ?? 300);
|
|
44
|
+
$container.find('#field-btt-position').val(btt.position || 'bottom-right');
|
|
45
|
+
$container.find('#field-btt-offset').val(btt.offset ?? 32);
|
|
46
|
+
$container.find('#field-btt-label').val(btt.label || '');
|
|
47
|
+
$container.find('#field-btt-smooth').prop('checked', btt.smooth !== false);
|
|
48
|
+
|
|
49
|
+
// Cookie Consent
|
|
50
|
+
const cc = s.cookieConsent || {};
|
|
51
|
+
$container.find('#field-cc-enabled').prop('checked', cc.enabled !== false);
|
|
52
|
+
$container.find('#field-cc-message').val(cc.message || '');
|
|
53
|
+
$container.find('#field-cc-accept-all').val(cc.acceptAllText || 'Accept All');
|
|
54
|
+
$container.find('#field-cc-reject-all').val(cc.rejectAllText || 'Reject All');
|
|
55
|
+
$container.find('#field-cc-customize').val(cc.customizeText || 'Customize');
|
|
56
|
+
$container.find('#field-cc-save-prefs').val(cc.savePreferencesText || 'Save Preferences');
|
|
57
|
+
$container.find('#field-cc-privacy-text').val(cc.privacyPolicyText || 'Privacy Policy');
|
|
58
|
+
$container.find('#field-cc-privacy-url').val(cc.privacyPolicyUrl || '');
|
|
59
|
+
$container.find('#field-cc-cookie-text').val(cc.cookiePolicyText || 'Cookie Policy');
|
|
60
|
+
$container.find('#field-cc-cookie-url').val(cc.cookiePolicyUrl || '');
|
|
61
|
+
$container.find('#field-cc-position').val(cc.position || 'bottom');
|
|
62
|
+
$container.find('#field-cc-layout').val(cc.layout || 'bar');
|
|
63
|
+
$container.find('#field-cc-theme').val(cc.theme || 'dark');
|
|
64
|
+
$container.find('#field-cc-show-functional').prop('checked', cc.showFunctional !== false);
|
|
65
|
+
$container.find('#field-cc-show-analytics').prop('checked', cc.showAnalytics !== false);
|
|
66
|
+
$container.find('#field-cc-show-marketing').prop('checked', cc.showMarketing !== false);
|
|
67
|
+
$container.find('#field-cc-version').val(cc.consentVersion || '1.0');
|
|
68
|
+
|
|
69
|
+
// Custom CSS
|
|
70
|
+
try {
|
|
71
|
+
const { css } = await apiRequest('/settings/custom-css');
|
|
72
|
+
$container.find('#field-custom-css').val(css || '');
|
|
73
|
+
} catch {
|
|
74
|
+
// non-fatal — textarea stays empty
|
|
75
|
+
}
|
|
76
|
+
|
|
29
77
|
$container.find('#save-settings-btn').on('click', async () => {
|
|
30
78
|
const adminTheme = $container.find('#field-admin-theme').val();
|
|
31
79
|
const data = {
|
|
32
80
|
title: $container.find('#field-site-title').val().trim(),
|
|
33
81
|
tagline: $container.find('#field-tagline').val().trim(),
|
|
82
|
+
fontFamily: $container.find('#field-font-family').val() || 'Roboto',
|
|
83
|
+
fontSize: parseInt($container.find('#field-font-size').val(), 10) || 16,
|
|
34
84
|
theme: $container.find('#field-theme').val(),
|
|
35
85
|
adminTheme,
|
|
36
86
|
seo: {
|
|
@@ -42,6 +92,14 @@ export const settingsView = {
|
|
|
42
92
|
copyright: $container.find('#field-footer-copy').val().trim(),
|
|
43
93
|
links: s.footer?.links || []
|
|
44
94
|
},
|
|
95
|
+
social: {
|
|
96
|
+
twitter: $container.find('#field-social-twitter').val().trim(),
|
|
97
|
+
facebook: $container.find('#field-social-facebook').val().trim(),
|
|
98
|
+
instagram: $container.find('#field-social-instagram').val().trim(),
|
|
99
|
+
linkedin: $container.find('#field-social-linkedin').val().trim(),
|
|
100
|
+
github: $container.find('#field-social-github').val().trim(),
|
|
101
|
+
youtube: $container.find('#field-social-youtube').val().trim()
|
|
102
|
+
},
|
|
45
103
|
smtp: {
|
|
46
104
|
host: $container.find('#field-smtp-host').val().trim(),
|
|
47
105
|
port: parseInt($container.find('#field-smtp-port').val(), 10) || 587,
|
|
@@ -50,14 +108,91 @@ export const settingsView = {
|
|
|
50
108
|
secure: $container.find('#field-smtp-secure').prop('checked'),
|
|
51
109
|
fromAddress: $container.find('#field-smtp-from-address').val().trim(),
|
|
52
110
|
fromName: $container.find('#field-smtp-from-name').val().trim()
|
|
111
|
+
},
|
|
112
|
+
backToTop: {
|
|
113
|
+
enabled: $container.find('#field-btt-enabled').prop('checked'),
|
|
114
|
+
scrollThreshold: parseInt($container.find('#field-btt-threshold').val(), 10) || 300,
|
|
115
|
+
position: $container.find('#field-btt-position').val() || 'bottom-right',
|
|
116
|
+
offset: parseInt($container.find('#field-btt-offset').val(), 10) || 32,
|
|
117
|
+
label: $container.find('#field-btt-label').val().trim(),
|
|
118
|
+
smooth: $container.find('#field-btt-smooth').prop('checked')
|
|
119
|
+
},
|
|
120
|
+
cookieConsent: {
|
|
121
|
+
enabled: $container.find('#field-cc-enabled').prop('checked'),
|
|
122
|
+
message: $container.find('#field-cc-message').val().trim(),
|
|
123
|
+
acceptAllText: $container.find('#field-cc-accept-all').val().trim(),
|
|
124
|
+
rejectAllText: $container.find('#field-cc-reject-all').val().trim(),
|
|
125
|
+
customizeText: $container.find('#field-cc-customize').val().trim(),
|
|
126
|
+
savePreferencesText: $container.find('#field-cc-save-prefs').val().trim(),
|
|
127
|
+
privacyPolicyText: $container.find('#field-cc-privacy-text').val().trim(),
|
|
128
|
+
privacyPolicyUrl: $container.find('#field-cc-privacy-url').val().trim(),
|
|
129
|
+
cookiePolicyText: $container.find('#field-cc-cookie-text').val().trim(),
|
|
130
|
+
cookiePolicyUrl: $container.find('#field-cc-cookie-url').val().trim(),
|
|
131
|
+
position: $container.find('#field-cc-position').val(),
|
|
132
|
+
layout: $container.find('#field-cc-layout').val(),
|
|
133
|
+
theme: $container.find('#field-cc-theme').val(),
|
|
134
|
+
showFunctional: $container.find('#field-cc-show-functional').prop('checked'),
|
|
135
|
+
showAnalytics: $container.find('#field-cc-show-analytics').prop('checked'),
|
|
136
|
+
showMarketing: $container.find('#field-cc-show-marketing').prop('checked'),
|
|
137
|
+
consentVersion: $container.find('#field-cc-version').val().trim() || '1.0'
|
|
53
138
|
}
|
|
54
139
|
};
|
|
140
|
+
const $btn = $container.find('#save-settings-btn');
|
|
141
|
+
$btn.prop('disabled', true);
|
|
55
142
|
try {
|
|
56
143
|
await api.settings.save(data);
|
|
57
144
|
Domma.theme.set(adminTheme);
|
|
58
145
|
E.toast('Settings saved.', { type: 'success' });
|
|
59
146
|
} catch {
|
|
60
147
|
E.toast('Failed to save settings.', { type: 'error' });
|
|
148
|
+
} finally {
|
|
149
|
+
$btn.prop('disabled', false);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Send test email
|
|
154
|
+
$container.find('#send-test-email-btn').on('click', async () => {
|
|
155
|
+
const to = $container.find('#field-test-email-to').val().trim();
|
|
156
|
+
const resultEl = $container.find('#test-email-result').get(0);
|
|
157
|
+
const $btn = $container.find('#send-test-email-btn');
|
|
158
|
+
|
|
159
|
+
$btn.prop('disabled', true);
|
|
160
|
+
if (resultEl) { resultEl.textContent = 'Sending…'; resultEl.style.color = ''; }
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const res = await apiRequest('/settings/test-email', {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
body: JSON.stringify({ to: to || undefined })
|
|
166
|
+
});
|
|
167
|
+
if (resultEl) {
|
|
168
|
+
resultEl.textContent = res.message || 'Test email sent.';
|
|
169
|
+
resultEl.style.color = 'var(--success,#4ade80)';
|
|
170
|
+
}
|
|
171
|
+
} catch (err) {
|
|
172
|
+
if (resultEl) {
|
|
173
|
+
resultEl.textContent = err.message || 'Failed to send test email.';
|
|
174
|
+
resultEl.style.color = 'var(--danger,#f87171)';
|
|
175
|
+
}
|
|
176
|
+
} finally {
|
|
177
|
+
$btn.prop('disabled', false);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Save Custom CSS
|
|
182
|
+
$container.find('#save-css-btn').on('click', async () => {
|
|
183
|
+
const css = $container.find('#field-custom-css').val();
|
|
184
|
+
const $btn = $container.find('#save-css-btn');
|
|
185
|
+
$btn.prop('disabled', true);
|
|
186
|
+
try {
|
|
187
|
+
await apiRequest('/settings/custom-css', {
|
|
188
|
+
method: 'PUT',
|
|
189
|
+
body: JSON.stringify({ css })
|
|
190
|
+
});
|
|
191
|
+
E.toast('Custom CSS saved.', { type: 'success' });
|
|
192
|
+
} catch (err) {
|
|
193
|
+
E.toast(err.message || 'Failed to save CSS.', { type: 'error' });
|
|
194
|
+
} finally {
|
|
195
|
+
$btn.prop('disabled', false);
|
|
61
196
|
}
|
|
62
197
|
});
|
|
63
198
|
}
|
package/admin/js/views/users.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Users List View
|
|
3
3
|
* Shows all users in a Domma table with role badges.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import {api, getUser} from '../api.js';
|
|
6
6
|
|
|
7
7
|
const ROLE_BADGE = {
|
|
8
8
|
admin: 'badge-danger',
|
|
@@ -22,18 +22,21 @@ export const usersView = {
|
|
|
22
22
|
T.create('#users-table', {
|
|
23
23
|
data,
|
|
24
24
|
columns: [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
{key: 'name', title: 'Name'},
|
|
26
|
+
{key: 'email', title: 'Email'},
|
|
27
|
+
{
|
|
28
|
+
key: 'role', title: 'Role',
|
|
28
29
|
render: (val) => `<span class="badge ${ROLE_BADGE[val] || 'badge-secondary'}">${val}</span>` },
|
|
29
|
-
|
|
30
|
+
{
|
|
31
|
+
key: 'isActive', title: 'Status',
|
|
30
32
|
render: (val) => val
|
|
31
33
|
? '<span class="badge badge-success">Active</span>'
|
|
32
34
|
: '<span class="badge badge-secondary">Inactive</span>' },
|
|
33
|
-
|
|
35
|
+
{
|
|
36
|
+
key: 'lastLogin', title: 'Last login',
|
|
34
37
|
render: (val) => val ? D(val).format('DD MMM YYYY HH:mm') : 'Never' },
|
|
35
38
|
{
|
|
36
|
-
|
|
39
|
+
key: 'actions', title: 'Actions',
|
|
37
40
|
render: (_, row) => {
|
|
38
41
|
const isSelf = row.id === currentUser?.id;
|
|
39
42
|
return `
|
package/bin/cli.js
CHANGED
|
@@ -42,7 +42,7 @@ if (flags.has('--help') || args.includes('-h')) {
|
|
|
42
42
|
Options:
|
|
43
43
|
--no-install Skip npm install
|
|
44
44
|
--no-setup Skip the interactive setup wizard
|
|
45
|
-
--seed
|
|
45
|
+
--no-seed Skip seeding default pages, forms, and collections
|
|
46
46
|
--help Show this help message
|
|
47
47
|
|
|
48
48
|
Example:
|
|
@@ -82,7 +82,7 @@ if (existsSync(target)) {
|
|
|
82
82
|
|
|
83
83
|
const noInstall = flags.has('--no-install');
|
|
84
84
|
const noSetup = flags.has('--no-setup') || noInstall;
|
|
85
|
-
const
|
|
85
|
+
const noSeed = flags.has('--no-seed');
|
|
86
86
|
|
|
87
87
|
// ---------------------------------------------------------------------------
|
|
88
88
|
// Banner
|
|
@@ -152,8 +152,26 @@ done();
|
|
|
152
152
|
// ---------------------------------------------------------------------------
|
|
153
153
|
|
|
154
154
|
step('Resetting plugin data');
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
// Read scaffold.reset entries from each plugin.json and apply them
|
|
156
|
+
const copiedPluginsDir = path.join(target, 'plugins');
|
|
157
|
+
try {
|
|
158
|
+
const pluginDirs = readdirSync(copiedPluginsDir, {withFileTypes: true})
|
|
159
|
+
.filter(e => e.isDirectory())
|
|
160
|
+
.map(e => e.name);
|
|
161
|
+
for (const pluginName of pluginDirs) {
|
|
162
|
+
const manifestPath = path.join(copiedPluginsDir, pluginName, 'plugin.json');
|
|
163
|
+
try {
|
|
164
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
165
|
+
for (const {path: relPath, content} of (manifest.scaffold?.reset || [])) {
|
|
166
|
+
const absPath = path.join(copiedPluginsDir, pluginName, relPath);
|
|
167
|
+
mkdirSync(path.dirname(absPath), {recursive: true});
|
|
168
|
+
writeFileSync(absPath, content + '\n', 'utf8');
|
|
169
|
+
}
|
|
170
|
+
} catch { /* missing or invalid plugin.json — skip */
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch { /* plugins dir not found — skip */
|
|
174
|
+
}
|
|
157
175
|
done();
|
|
158
176
|
|
|
159
177
|
// ---------------------------------------------------------------------------
|
|
@@ -189,14 +207,29 @@ const DEFAULT_NAV = {
|
|
|
189
207
|
position: 'sticky'
|
|
190
208
|
};
|
|
191
209
|
|
|
192
|
-
//
|
|
193
|
-
const
|
|
194
|
-
'
|
|
195
|
-
'
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
};
|
|
210
|
+
// Plugins enabled by default in a fresh project
|
|
211
|
+
const ENABLED_BY_DEFAULT = new Set([
|
|
212
|
+
'domma-effects',
|
|
213
|
+
'form-builder',
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
// Discover all plugins and set enabled state accordingly
|
|
217
|
+
const DEFAULT_PLUGINS = {};
|
|
218
|
+
try {
|
|
219
|
+
const pluginsRoot = path.join(target, 'plugins');
|
|
220
|
+
const pluginDirs = readdirSync(pluginsRoot, {withFileTypes: true})
|
|
221
|
+
.filter(e => e.isDirectory())
|
|
222
|
+
.map(e => e.name);
|
|
223
|
+
for (const name of pluginDirs) {
|
|
224
|
+
const manifestPath = path.join(pluginsRoot, name, 'plugin.json');
|
|
225
|
+
try {
|
|
226
|
+
JSON.parse(readFileSync(manifestPath, 'utf8')); // validate manifest exists
|
|
227
|
+
DEFAULT_PLUGINS[name] = {enabled: ENABLED_BY_DEFAULT.has(name), settings: {}};
|
|
228
|
+
} catch { /* no valid plugin.json — skip */
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
} catch { /* plugins dir not found — leave empty */
|
|
232
|
+
}
|
|
200
233
|
|
|
201
234
|
step('Writing default config');
|
|
202
235
|
writeFileSync(path.join(target, 'config/site.json'), JSON.stringify(DEFAULT_SITE, null, 4) + '\n', 'utf8');
|
|
@@ -210,6 +243,7 @@ done();
|
|
|
210
243
|
|
|
211
244
|
step('Creating content directories');
|
|
212
245
|
mkdirSync(path.join(target, 'content/pages'), {recursive: true});
|
|
246
|
+
mkdirSync(path.join(target, 'content/collections'), {recursive: true});
|
|
213
247
|
mkdirSync(path.join(target, 'content/media'), {recursive: true});
|
|
214
248
|
mkdirSync(path.join(target, 'content/users'), {recursive: true});
|
|
215
249
|
done();
|
|
@@ -301,13 +335,15 @@ if (noSetup) {
|
|
|
301
335
|
}
|
|
302
336
|
|
|
303
337
|
// ---------------------------------------------------------------------------
|
|
304
|
-
//
|
|
338
|
+
// Seed default content (pages, forms, collections)
|
|
305
339
|
// ---------------------------------------------------------------------------
|
|
306
340
|
|
|
307
|
-
if (
|
|
308
|
-
console.log('
|
|
341
|
+
if (noSeed) {
|
|
342
|
+
console.log(' ⚠ Skipping seed (--no-seed).');
|
|
343
|
+
} else {
|
|
344
|
+
console.log(' Seeding default pages, forms and collections…');
|
|
309
345
|
console.log('');
|
|
310
|
-
const result = spawnSync(
|
|
346
|
+
const result = spawnSync(process.execPath, ['scripts/seed.js'], {cwd: target, stdio: 'inherit'});
|
|
311
347
|
if (result.status !== 0) {
|
|
312
348
|
console.error('\n ✗ Seed failed.\n');
|
|
313
349
|
process.exit(result.status ?? 1);
|
|
@@ -327,7 +363,7 @@ console.log('');
|
|
|
327
363
|
console.log(` Next steps:`);
|
|
328
364
|
console.log(` cd ${projectName}`);
|
|
329
365
|
if (noInstall) console.log(` npm install`);
|
|
330
|
-
if (noSetup) console.log(` npm run setup`);
|
|
366
|
+
if (noSetup && !noInstall) console.log(` npm run setup`);
|
|
331
367
|
console.log(` npm run dev`);
|
|
332
368
|
console.log('');
|
|
333
369
|
console.log(` Then open: http://localhost:3050/admin`);
|
package/config/auth.json
CHANGED
package/config/content.json
CHANGED
package/config/navigation.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"brand": {
|
|
3
|
-
"text": "My Site",
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"text": "My Dead Good Site",
|
|
4
|
+
"url": "/",
|
|
5
|
+
"icon": "home"
|
|
6
6
|
},
|
|
7
7
|
"items": [
|
|
8
8
|
{
|
|
@@ -54,10 +54,20 @@
|
|
|
54
54
|
"text": "Shortcode Reference",
|
|
55
55
|
"url": "/resources/shortcodes",
|
|
56
56
|
"icon": "code"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"text": "Effects",
|
|
60
|
+
"url": "/resources/effects",
|
|
61
|
+
"icon": "zap"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"text": "Interactive",
|
|
65
|
+
"url": "/resources/interactive",
|
|
66
|
+
"icon": "mouse-pointer"
|
|
57
67
|
}
|
|
58
68
|
]
|
|
59
69
|
}
|
|
60
70
|
],
|
|
61
|
-
"variant": "
|
|
71
|
+
"variant": "transparent",
|
|
62
72
|
"position": "sticky"
|
|
63
73
|
}
|
package/config/plugins.json
CHANGED
|
@@ -17,24 +17,6 @@
|
|
|
17
17
|
"fromName": "Website Forms"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
-
"back-to-top": {
|
|
21
|
-
"enabled": true,
|
|
22
|
-
"settings": {
|
|
23
|
-
"scrollThreshold": 150,
|
|
24
|
-
"position": "bottom-right",
|
|
25
|
-
"label": "",
|
|
26
|
-
"smooth": true,
|
|
27
|
-
"offset": 32
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
"cookie-consent": {
|
|
31
|
-
"enabled": true,
|
|
32
|
-
"settings": {}
|
|
33
|
-
},
|
|
34
|
-
"custom-css": {
|
|
35
|
-
"enabled": true,
|
|
36
|
-
"settings": {}
|
|
37
|
-
},
|
|
38
20
|
"domma-effects": {
|
|
39
21
|
"enabled": true,
|
|
40
22
|
"settings": {
|
package/config/presets.json
CHANGED
|
@@ -4,31 +4,27 @@
|
|
|
4
4
|
"description": "Standard page with navbar and footer",
|
|
5
5
|
"navbar": true,
|
|
6
6
|
"footer": true,
|
|
7
|
-
|
|
8
|
-
"features": ["back-to-top", "icon-scan"]
|
|
7
|
+
"sidebar": false
|
|
9
8
|
},
|
|
10
9
|
"with-sidebar": {
|
|
11
10
|
"label": "With Sidebar",
|
|
12
11
|
"description": "Page with navbar, sidebar, and footer",
|
|
13
12
|
"navbar": true,
|
|
14
13
|
"footer": true,
|
|
15
|
-
|
|
16
|
-
"features": ["back-to-top", "icon-scan", "scroll-spy"]
|
|
14
|
+
"sidebar": true
|
|
17
15
|
},
|
|
18
16
|
"minimal": {
|
|
19
17
|
"label": "Minimal",
|
|
20
18
|
"description": "Clean page with no navbar or footer",
|
|
21
19
|
"navbar": false,
|
|
22
20
|
"footer": false,
|
|
23
|
-
|
|
24
|
-
"features": []
|
|
21
|
+
"sidebar": false
|
|
25
22
|
},
|
|
26
23
|
"landing": {
|
|
27
24
|
"label": "Landing Page",
|
|
28
25
|
"description": "Full-width landing page layout",
|
|
29
26
|
"navbar": true,
|
|
30
27
|
"footer": true,
|
|
31
|
-
|
|
32
|
-
"features": ["back-to-top", "icon-scan"]
|
|
28
|
+
"sidebar": false
|
|
33
29
|
}
|
|
34
30
|
}
|
package/config/site.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"title": "My Boss Site",
|
|
3
|
-
"tagline": "My Dead Good
|
|
4
|
-
"
|
|
3
|
+
"tagline": "My Dead Good Site",
|
|
4
|
+
"fontFamily": "Roboto",
|
|
5
|
+
"fontSize": 16,
|
|
6
|
+
"theme": "charcoal-dark",
|
|
5
7
|
"adminTheme": "charcoal-dark",
|
|
6
8
|
"seo": {
|
|
7
9
|
"defaultTitle": "My Boss Site",
|
|
@@ -9,7 +11,7 @@
|
|
|
9
11
|
"defaultDescription": "A site built with Domma CMS"
|
|
10
12
|
},
|
|
11
13
|
"footer": {
|
|
12
|
-
"copyright": "© 2026 DCBW Consulting Ltd
|
|
14
|
+
"copyright": "© 2026 DCBW Consulting Ltd",
|
|
13
15
|
"links": [
|
|
14
16
|
{
|
|
15
17
|
"text": "Privacy Policy",
|
|
@@ -18,9 +20,21 @@
|
|
|
18
20
|
{
|
|
19
21
|
"text": "Contact",
|
|
20
22
|
"url": "/contact"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"text": "GDPR",
|
|
26
|
+
"url": "/gdpr"
|
|
21
27
|
}
|
|
22
28
|
]
|
|
23
29
|
},
|
|
30
|
+
"social": {
|
|
31
|
+
"twitter": "",
|
|
32
|
+
"facebook": "",
|
|
33
|
+
"instagram": "",
|
|
34
|
+
"linkedin": "",
|
|
35
|
+
"github": "https://github.com/pinpointzero73/",
|
|
36
|
+
"youtube": ""
|
|
37
|
+
},
|
|
24
38
|
"smtp": {
|
|
25
39
|
"host": "localhost",
|
|
26
40
|
"port": 1025,
|
|
@@ -29,5 +43,32 @@
|
|
|
29
43
|
"secure": false,
|
|
30
44
|
"fromAddress": "",
|
|
31
45
|
"fromName": ""
|
|
46
|
+
},
|
|
47
|
+
"backToTop": {
|
|
48
|
+
"enabled": true,
|
|
49
|
+
"scrollThreshold": 150,
|
|
50
|
+
"position": "bottom-right",
|
|
51
|
+
"label": "",
|
|
52
|
+
"smooth": true,
|
|
53
|
+
"offset": 48
|
|
54
|
+
},
|
|
55
|
+
"cookieConsent": {
|
|
56
|
+
"enabled": true,
|
|
57
|
+
"message": "We use cookies to enhance your browsing experience, serve personalised content, and analyse our traffic. By clicking \"Accept All\", you consent to our use of cookies.",
|
|
58
|
+
"acceptAllText": "Accept All",
|
|
59
|
+
"rejectAllText": "Reject All",
|
|
60
|
+
"customizeText": "Customize",
|
|
61
|
+
"savePreferencesText": "Save Preferences",
|
|
62
|
+
"privacyPolicyText": "Privacy Policy",
|
|
63
|
+
"privacyPolicyUrl": "/privacy-policy",
|
|
64
|
+
"cookiePolicyText": "Cookie Policy",
|
|
65
|
+
"cookiePolicyUrl": "",
|
|
66
|
+
"position": "bottom",
|
|
67
|
+
"layout": "bar",
|
|
68
|
+
"theme": "dark",
|
|
69
|
+
"showFunctional": true,
|
|
70
|
+
"showAnalytics": true,
|
|
71
|
+
"showMarketing": true,
|
|
72
|
+
"consentVersion": "1.0"
|
|
32
73
|
}
|
|
33
74
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domma-cms",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/server.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"scripts/"
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
|
+
"build": "node scripts/build.js",
|
|
20
21
|
"start": "pm2 start server/server.js -i max --name domma-cms",
|
|
21
22
|
"dev": "PORT=3050 node --watch server/server.js",
|
|
22
23
|
"prod": "node server/server.js",
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"@fastify/multipart": "^9.3.0",
|
|
55
56
|
"@fastify/static": "^8.1.0",
|
|
56
57
|
"bcryptjs": "^3.0.3",
|
|
57
|
-
"domma-js": "^0.
|
|
58
|
+
"domma-js": "^0.19.1",
|
|
58
59
|
"dotenv": "^17.2.3",
|
|
59
60
|
"fastify": "^5.7.3",
|
|
60
61
|
"gray-matter": "^4.0.3",
|
|
@@ -63,5 +64,8 @@
|
|
|
63
64
|
"sanitize-html": "^2.17.0",
|
|
64
65
|
"sharp": "^0.34.5",
|
|
65
66
|
"uuid": "^13.0.0"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"esbuild": "^0.27.3"
|
|
66
70
|
}
|
|
67
71
|
}
|