domma-cms 0.92.1 → 0.94.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 (177) hide show
  1. package/CLAUDE.md +5 -3
  2. package/admin/css/admin.css +1 -1
  3. package/admin/js/app.js +2 -2
  4. package/admin/js/lib/action-editor-arrange.js +1 -1
  5. package/admin/js/lib/api-tokens-arrange.js +2 -2
  6. package/admin/js/lib/block-editor-arrange.js +1 -1
  7. package/admin/js/lib/blocks-arrange.js +1 -1
  8. package/admin/js/lib/collection-entries-arrange.js +1 -1
  9. package/admin/js/lib/components-arrange.js +1 -1
  10. package/admin/js/lib/dashboard-arrange.js +1 -1
  11. package/admin/js/lib/dates.js +1 -0
  12. package/admin/js/lib/forms-arrange.js +1 -1
  13. package/admin/js/lib/media-arrange.js +1 -1
  14. package/admin/js/lib/notifications-arrange.js +1 -1
  15. package/admin/js/lib/pages-arrange.js +1 -1
  16. package/admin/js/lib/related.js +1 -1
  17. package/admin/js/lib/timeline-builder.js +2 -2
  18. package/admin/js/templates/action-editor.html +6 -5
  19. package/admin/js/templates/actions-list.html +1 -1
  20. package/admin/js/templates/contacts.html +1 -1
  21. package/admin/js/templates/docs/api-actions.html +86 -60
  22. package/admin/js/templates/docs/api-authentication.html +159 -123
  23. package/admin/js/templates/docs/api-builder.html +197 -0
  24. package/admin/js/templates/docs/api-collections.html +199 -259
  25. package/admin/js/templates/docs/api-external.html +225 -0
  26. package/admin/js/templates/docs/api-forms.html +268 -0
  27. package/admin/js/templates/docs/api-layouts.html +70 -45
  28. package/admin/js/templates/docs/api-media.html +57 -80
  29. package/admin/js/templates/docs/api-navigation.html +66 -22
  30. package/admin/js/templates/docs/api-pages.html +109 -129
  31. package/admin/js/templates/docs/api-plugins.html +123 -61
  32. package/admin/js/templates/docs/api-scaffold.html +185 -0
  33. package/admin/js/templates/docs/api-settings.html +72 -64
  34. package/admin/js/templates/docs/api-users.html +74 -107
  35. package/admin/js/templates/docs/api-views.html +68 -54
  36. package/admin/js/templates/docs/components-howto.html +20 -17
  37. package/admin/js/templates/docs/components-reference.html +13 -16
  38. package/admin/js/templates/docs/components-rules.html +7 -6
  39. package/admin/js/templates/docs/components-walkthrough.html +19 -19
  40. package/admin/js/templates/docs/tutorial-crud.html +71 -40
  41. package/admin/js/templates/docs/tutorial-forms.html +51 -35
  42. package/admin/js/templates/docs/tutorial-plugin.html +132 -56
  43. package/admin/js/templates/docs/usage-actions.html +61 -15
  44. package/admin/js/templates/docs/usage-collections.html +108 -0
  45. package/admin/js/templates/docs/usage-cta-shortcode.html +14 -3
  46. package/admin/js/templates/docs/usage-dconfig.html +0 -3
  47. package/admin/js/templates/docs/usage-editions.html +213 -0
  48. package/admin/js/templates/docs/usage-media.html +22 -6
  49. package/admin/js/templates/docs/usage-navigation.html +74 -18
  50. package/admin/js/templates/docs/usage-pages.html +60 -20
  51. package/admin/js/templates/docs/usage-plugins.html +89 -17
  52. package/admin/js/templates/docs/usage-shortcodes.html +123 -70
  53. package/admin/js/templates/docs/usage-site-settings.html +50 -18
  54. package/admin/js/templates/docs/usage-tools.html +73 -0
  55. package/admin/js/templates/docs/usage-users-roles.html +99 -20
  56. package/admin/js/templates/docs/usage-views.html +36 -19
  57. package/admin/js/templates/documentation.html +153 -32
  58. package/admin/js/templates/page-editor.html +0 -5
  59. package/admin/js/templates/plugin-guide.html +15 -0
  60. package/admin/js/templates/plugin-guides.html +21 -0
  61. package/admin/js/templates/pro-docs.html +53 -234
  62. package/admin/js/templates/tutorials.html +5 -4
  63. package/admin/js/views/actions-list.js +3 -3
  64. package/admin/js/views/analytics.js +5 -5
  65. package/admin/js/views/api-endpoint-editor.js +2 -2
  66. package/admin/js/views/block-editor.js +4 -4
  67. package/admin/js/views/blocks.js +4 -4
  68. package/admin/js/views/collection-editor.js +4 -4
  69. package/admin/js/views/collection-entries.js +7 -7
  70. package/admin/js/views/component-editor.js +2 -2
  71. package/admin/js/views/contacts.js +22 -20
  72. package/admin/js/views/context-menu-editor.js +5 -5
  73. package/admin/js/views/doc-pages.js +1 -1
  74. package/admin/js/views/form-editor.js +4 -4
  75. package/admin/js/views/form-submissions.js +2 -2
  76. package/admin/js/views/index.js +1 -1
  77. package/admin/js/views/media.js +3 -3
  78. package/admin/js/views/menu-editor.js +13 -13
  79. package/admin/js/views/menu-locations.js +2 -2
  80. package/admin/js/views/my-profile.js +1 -1
  81. package/admin/js/views/page-editor.js +8 -8
  82. package/admin/js/views/plugin-guides.js +5 -0
  83. package/admin/js/views/project-detail.js +2 -2
  84. package/admin/js/views/project-settings.js +1 -1
  85. package/admin/js/views/role-editor.js +4 -4
  86. package/admin/js/views/search.js +2 -2
  87. package/admin/js/views/seo.js +17 -17
  88. package/admin/js/views/settings.js +3 -3
  89. package/admin/js/views/theme.js +3 -3
  90. package/admin/js/views/user-editor.js +1 -1
  91. package/admin/js/views/users.js +2 -2
  92. package/admin/js/views/view-editor.js +1 -1
  93. package/bin/cli.js +13 -13
  94. package/bin/lib/node-version.js +29 -0
  95. package/package.json +1 -1
  96. package/plugins/_lib/admin/mail/compose-window.js +3 -2
  97. package/plugins/_lib/admin/mail/reader-view.js +7 -6
  98. package/plugins/_lib/admin/mail/scheduling.js +4 -2
  99. package/plugins/_lib/admin/mail/templates.js +4 -4
  100. package/plugins/_lib/admin/ui/dates.js +85 -0
  101. package/plugins/blog/CLAUDE.md +31 -22
  102. package/plugins/blog/admin/views/blog.js +3 -2
  103. package/plugins/blog/admin/views/comments.js +2 -1
  104. package/plugins/blog/admin/views/post-editor.js +4 -4
  105. package/plugins/blog/blocks/blog-card-row.html +1 -1
  106. package/plugins/blog/blocks/blog-card.html +2 -2
  107. package/plugins/blog/blocks/blog-post-classic.html +2 -2
  108. package/plugins/blog/blocks/blog-post-essay.html +2 -2
  109. package/plugins/blog/blocks/blog-post-feature.html +2 -2
  110. package/plugins/blog/blocks/blog-post-minimal.html +2 -2
  111. package/plugins/blog/blocks/blog-post-sidebar.html +2 -2
  112. package/plugins/blog/blocks/blog-post-split.html +2 -2
  113. package/plugins/blog/docs/guide.md +205 -0
  114. package/plugins/blog/lib/layouts.js +3 -3
  115. package/plugins/blog/lib/page.js +2 -1
  116. package/plugins/blog/plugin.js +3 -3
  117. package/plugins/blog/plugin.json +4 -4
  118. package/plugins/blog/tests/layouts.test.js +6 -0
  119. package/plugins/feedback/CLAUDE.md +22 -3
  120. package/plugins/feedback/admin/lib/kit.js +6 -7
  121. package/plugins/feedback/admin/views/feedback.js +79 -10
  122. package/plugins/feedback/admin/views/send.js +28 -6
  123. package/plugins/feedback/docs/guide.md +95 -0
  124. package/plugins/feedback/lib/receiver.js +9 -2
  125. package/plugins/feedback/lib/sender.js +3 -2
  126. package/plugins/feedback/plugin.js +54 -6
  127. package/plugins/feedback/plugin.json +4 -4
  128. package/plugins/feedback/tests/api.test.js +74 -2
  129. package/plugins/free-tier.lock.json +49 -44
  130. package/plugins/mail-reader/CLAUDE.md +33 -18
  131. package/plugins/mail-reader/docs/guide.md +147 -0
  132. package/plugins/mail-reader/plugin.json +1 -1
  133. package/plugins/security/CLAUDE.md +4 -1
  134. package/plugins/security/admin/views/security.js +5 -5
  135. package/plugins/security/docs/guide.md +170 -0
  136. package/plugins/security/plugin.js +2 -1
  137. package/plugins/security/plugin.json +2 -1
  138. package/plugins/shopping-cart/CLAUDE.md +7 -1
  139. package/plugins/shopping-cart/admin/lib/kit.js +5 -2
  140. package/plugins/shopping-cart/admin/views/orders.js +4 -4
  141. package/plugins/shopping-cart/admin/views/overview.js +2 -2
  142. package/plugins/shopping-cart/docs/guide.md +191 -0
  143. package/plugins/shopping-cart/lib/render.js +2 -1
  144. package/plugins/shopping-cart/plugin.json +3 -3
  145. package/public/js/collection-browser.js +2 -2
  146. package/public/js/site.js +1 -1
  147. package/scripts/gen-instance-secret.js +3 -1
  148. package/scripts/setup.js +3 -1
  149. package/server/middleware/auth.js +2 -1
  150. package/server/routes/api/actions.js +47 -27
  151. package/server/routes/api/blocks.js +2 -1
  152. package/server/routes/api/collections.js +16 -52
  153. package/server/routes/api/contacts.js +66 -3
  154. package/server/routes/api/documentation.js +42 -0
  155. package/server/routes/api/notifications.js +3 -2
  156. package/server/routes/api/users.js +10 -6
  157. package/server/server.js +16 -1
  158. package/server/services/actions.js +110 -34
  159. package/server/services/adapterRegistry.js +169 -16
  160. package/server/services/adapters/FileAdapter.js +25 -0
  161. package/server/services/adapters/MongoAdapter.js +23 -0
  162. package/server/services/collections.js +104 -1
  163. package/server/services/connectionManager.js +12 -0
  164. package/server/services/dates.js +81 -0
  165. package/server/services/docs.js +13 -2
  166. package/server/services/markdown.js +75 -26
  167. package/server/services/notification-sources.js +6 -5
  168. package/server/services/passwordReset.js +2 -1
  169. package/server/services/permissionRegistry.js +3 -2
  170. package/server/services/pluginGuides.js +255 -0
  171. package/server/services/pluginInstaller.js +54 -13
  172. package/server/services/plugins.js +29 -1
  173. package/server/services/presetCollections.js +31 -5
  174. package/server/services/renderer.js +2 -2
  175. package/server/services/sidebarBadges.js +3 -1
  176. package/server/services/tools.js +4 -2
  177. package/server/templates/page.html +2 -2
@@ -69,67 +69,129 @@
69
69
  <div class="col-12">
70
70
  <div class="docs-body">
71
71
 
72
- <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/plugins</span>
73
- </h3>
74
- <p class="auth-note">Requires Bearer token + admin role.</p>
75
- <p>List all discovered plugins with their current enabled state and settings.</p>
76
- <pre class="code-block"><code>// Response 200
77
- [
78
- {
79
- "name": "form-builder",
80
- "displayName": "Form Builder",
81
- "version": "1.0.0",
82
- "description": "Build and manage contact forms.",
83
- "author": "Domma Team",
84
- "date": "2024-01-01",
85
- "icon": "clipboard",
86
- "enabled": true,
87
- "settings": {}
88
- }
89
- ]</code></pre>
90
-
91
- <h3><span class="method-badge method-put">PUT</span><span
92
- class="endpoint-path">/api/plugins/:name</span></h3>
93
- <p class="auth-note">Requires Bearer token + admin role.</p>
94
- <p>Enable or disable a plugin, or update its settings.</p>
95
- <table class="table table-sm">
96
- <thead>
97
- <tr>
98
- <th>Field</th>
99
- <th>Type</th>
100
- <th>Description</th>
101
- </tr>
102
- </thead>
103
- <tbody>
104
- <tr>
105
- <td><code>enabled</code></td>
106
- <td>boolean</td>
107
- <td>Whether the plugin is active</td>
108
- </tr>
109
- <tr>
110
- <td><code>settings</code></td>
111
- <td>object</td>
112
- <td>Plugin-specific settings object</td>
113
- </tr>
114
- </tbody>
115
- </table>
116
- <pre class="code-block"><code>// Response 200
117
- { "success": true }
118
-
119
- // Error 404
120
- { "error": "Plugin not found" }</code></pre>
121
-
122
- <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/plugins/admin-config</span>
123
- </h3>
124
- <p class="auth-note">Requires Bearer token (any role).</p>
125
- <p>Return the merged admin configuration for all enabled plugins - sidebar items, SPA routes, and view
126
- entry points.</p>
127
- <pre class="code-block"><code>// Response 200
128
- {
129
- "sidebar": [ { "id": "form-builder", "text": "Form Settings", "icon": "clipboard", "url": "#/form-settings" } ],
130
- "routes": [ { "path": "/form-settings", "view": "formSettings", "title": "Form Settings - Domma CMS" } ],
131
- "views": { "formSettings": { "entry": "form-builder/admin/views/settings.js", "exportName": "formSettingsView" } }
132
- }</code></pre>
72
+ <p>These endpoints drive the <strong>Marketplace</strong> screen (System &gt; Plugins, <code>#/plugins</code>). They
73
+ need the <code>plugins</code> permission for the action named: <code>read</code>, <code>create</code> (install),
74
+ <code>update</code> (switch on or off, settings) or <code>develop</code> (the source editor, export and restart).
75
+ Installing from, and uninstalling through, the managed catalogue needs an admin (role level 0 or 1).</p>
76
+ <p><strong>Restarts.</strong> A plugin's routes and screens are read when the server starts, so switching a plugin
77
+ or a built-in Tool on or off restarts the server by itself, about a second and a half after the reply, when
78
+ something will bring it back (the fleet manager, or pm2) and <code>restartOnPluginToggle</code> in
79
+ <code>config/server.json</code> is not <code>false</code>. The reply says what happened:
80
+ <code>restartRequired</code>, <code>supervised</code>, <code>restarting</code>. Installing, updating and
81
+ uninstalling do not restart; the new code runs after the next restart.</p>
82
+
83
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/plugins</span></h3>
84
+ <p class="auth-note">Requires Bearer token + <code>plugins</code> read permission.</p>
85
+ <p>Every installed plugin, with its state.</p>
86
+ <pre class="code-block"><code>// Response 200
87
+ [ { "name": "blog", "displayName": "Blog", "version": "1.9.0", "description": "...", "author": "...", "icon": "edit",
88
+ "core": false, "enabled": true, "closedSource": false, "licence": "...",
89
+ "entitlement": null, // for a licensed plugin: valid, grace, expired, support-ended, unlicensed...
90
+ "supersededBy": null, // enabled but replaced by another plugin (a Pro edition)
91
+ "blockedBy": null, // enabled but not running: something it requires is off
92
+ "locked": false, // the site's manager holds this switch
93
+ "requires": [], "uses": {},
94
+ "settings": {}, "settingsSchema": null, "effectiveSettings": {} } ]</code></pre>
95
+ <p>Secret settings come back masked; sending the mask back keeps the stored value.</p>
96
+
97
+ <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/plugins/:name</span></h3>
98
+ <p class="auth-note">Requires Bearer token + <code>plugins</code> update permission.</p>
99
+ <p>Switch a plugin on or off, or save its settings. A switch goes through the same check as the screen: a switch
100
+ the site's manager has locked is refused with 423, and one that changes other Tools or plugins (a plugin that
101
+ requires this one goes off with it; switching one on brings on what it requires) answers 409 with the
102
+ <code>plan</code> until repeated with <code>"cascade": true</code>. A built-in plugin cannot be switched off.</p>
103
+ <table class="table table-sm">
104
+ <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
105
+ <tbody>
106
+ <tr><td><code>enabled</code></td><td>boolean</td><td>On or off</td></tr>
107
+ <tr><td><code>settings</code></td><td>object</td><td>The plugin's settings</td></tr>
108
+ <tr><td><code>cascade</code></td><td>boolean</td><td>Confirm the knock-on changes in a 409's plan</td></tr>
109
+ </tbody>
110
+ </table>
111
+ <pre class="code-block"><code>// Response 200 - a switch
112
+ { "success": true, "restartRequired": true, "supervised": true, "restarting": true }
113
+ // Error 409 - confirm first
114
+ { "error": "This switch changes other Tools too.", "needsConfirm": true, "plan": { ... } }</code></pre>
115
+
116
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/tools</span></h3>
117
+ <p class="auth-note">Requires Bearer token + <code>plugins</code> read permission.</p>
118
+ <p>The built-in Tools that can be switched off - Contacts, Notes, Todo, Analytics and SEO - with their state and the
119
+ plugins that depend on each. <code>GET /api/tools/enabled</code> (any signed-in user) answers just
120
+ <code>{"notes": true, ...}</code>.</p>
121
+ <pre class="code-block"><code>// Response 200
122
+ [ { "name": "contacts", "displayName": "Contacts", "enabled": true, "locked": false,
123
+ "requiredBy": [ { "name": "contacts-pro", "displayName": "Contacts Pro" } ],
124
+ "usedBy": [ { "name": "calendar", "displayName": "Calendar", "reason": "Invite a contact group" } ] } ]</code></pre>
125
+
126
+ <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/tools/:name</span></h3>
127
+ <p class="auth-note">Requires Bearer token + <code>plugins</code> update permission.</p>
128
+ <p>Switch a built-in Tool on or off (<code>{"enabled": false}</code>). Nothing is deleted; switched back on,
129
+ everything is where it was. The same 423 / 409-with-plan / <code>cascade</code> rules and restart as a plugin.</p>
130
+
131
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/plugins/install-upload</span></h3>
132
+ <p class="auth-note">Requires Bearer token + <code>plugins</code> create permission. Content-Type:
133
+ <code>multipart/form-data</code>.</p>
134
+ <p>Install or update a plugin from a <code>.dcmsplugin</code> file (Install from file). The plugin is named by the
135
+ manifest inside, not the file name. Refusals come back as questions to confirm with form fields set to
136
+ <code>true</code>: <code>confirmUpgrade</code> (it is already installed - 409, <code>code: "already-installed"</code>),
137
+ <code>confirmDowngrade</code> and <code>confirmUnsigned</code> (a file not signed by the Marketplace; a site
138
+ with <code>requireSignedPlugins</code> refuses those outright). A plugin whose <code>minCmsVersion</code> is newer
139
+ than this site is refused, naming both versions, and an update leaves the installed version in place. A
140
+ licensed plugin's licence travels in the file. A new plugin arrives switched off.</p>
141
+ <pre class="code-block"><code>// Response 200
142
+ { "success": true, "name": "invoices", "action": "install", "from": null, "version": "1.6.1", "restartRequired": true }
143
+ // Error 400
144
+ { "error": "Invoices needs Domma CMS 0.93.0 or later; this site runs 0.92.2. Update the CMS first." }</code></pre>
145
+
146
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/plugins/marketplace</span></h3>
147
+ <p class="auth-note">Requires Bearer token + an admin role (level 0 or 1).</p>
148
+ <p>The Browse tab: what this site can get from its manager, with what is installed and what can be updated. A
149
+ site that is not managed answers <code>{"available": false, "catalogue": []}</code> and installs from a file
150
+ instead.</p>
151
+ <pre class="code-block"><code>// Response 200
152
+ { "available": true, "site": "my-site",
153
+ "catalogue": [ { "slug": "invoices", "version": "1.6.1", "installed": true, "installedVersion": "1.6.0", "updatable": true, ... } ] }</code></pre>
154
+
155
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/plugins/marketplace/install</span></h3>
156
+ <p class="auth-note">Requires Bearer token + an admin role (level 0 or 1). Managed sites only.</p>
157
+ <p>Fetch a plugin from the manager and install it, licensed to this site; <code>"update": true</code> updates one
158
+ already installed. If the update fails, the installed version is put back. A refusal from the manager (no
159
+ licence) answers 403 with its reason; 503 when the manager cannot be reached.</p>
160
+ <pre class="code-block"><code>// Request body
161
+ { "slug": "invoices", "version": "1.6.1", "update": true }
162
+ // Response 200
163
+ { "success": true, "name": "invoices", "action": "upgrade", "from": "1.6.0", "version": "1.6.1", "restartRequired": true }</code></pre>
164
+
165
+ <h3><span class="method-badge method-delete">DELETE</span><span class="endpoint-path">/api/plugins/marketplace/:slug</span></h3>
166
+ <p class="auth-note">Requires Bearer token + an admin role (level 0 or 1).</p>
167
+ <p>Uninstall a plugin. Its files and its <code>data/</code> folder are removed, roles it added are removed and their
168
+ users moved to <code>user</code>. Restart to finish.</p>
169
+ <pre class="code-block"><code>// Response 200
170
+ { "success": true }</code></pre>
171
+
172
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/plugins/admin-config</span></h3>
173
+ <p class="auth-note">Requires Bearer token (any role).</p>
174
+ <p>What the admin needs from the running plugins: sidebar items, screens (routes) and the scripts behind them.</p>
175
+ <pre class="code-block"><code>// Response 200
176
+ { "sidebar": [ { "id": "blog", "text": "Blog", "icon": "edit", "url": "#/plugins/blog" } ],
177
+ "routes": [ { "path": "/plugins/blog", "view": "plugin-blog", "title": "Blog - Domma CMS" } ],
178
+ "views": { "plugin-blog": { "entry": "blog/admin/views/blog.js", "exportName": "blogView" } } }</code></pre>
179
+
180
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/plugins/scaffold</span></h3>
181
+ <p class="auth-note">Requires Bearer token + <code>plugins</code> create permission.</p>
182
+ <p>Start a new plugin from the template (New plugin). It is created switched on and runs after the next restart.</p>
183
+ <pre class="code-block"><code>// Request body
184
+ { "slug": "my-plugin", "displayName": "My Plugin", "description": "...", "author": "...", "icon": "package" }
185
+ // Response 200
186
+ { "success": true, "name": "my-plugin", "files": [ ... ], "restartRequired": true }</code></pre>
187
+
188
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/plugins/:name/export</span></h3>
189
+ <p class="auth-note">Requires Bearer token + <code>plugins</code> develop permission.</p>
190
+ <p>Download a plugin as an unsigned <code>.dcmsplugin</code> file (<code>?includeData=1</code> adds its data).
191
+ Licensed plugins cannot be exported (403). The source editor uses <code>GET /api/plugins/:name/files</code> and
192
+ <code>GET</code>, <code>PUT</code>, <code>DELETE /api/plugins/:name/file</code>, and
193
+ <code>POST /api/plugins/restart</code> restarts the server to run edited code; a licensed plugin's source is open
194
+ only to the level-0 role.</p>
133
195
 
134
196
  </div>
135
197
  </div>
@@ -0,0 +1,185 @@
1
+ <div class="view-header">
2
+ <h1><span data-icon="code"></span> API · Scaffold API</h1>
3
+ <a href="#/api-reference" class="btn btn-ghost btn-sm"><span data-icon="arrow-left"></span> API overview</a>
4
+ </div>
5
+ <style>
6
+ .method-badge {
7
+ display: inline-block;
8
+ font-size: 11px;
9
+ font-weight: 700;
10
+ letter-spacing: 0.5px;
11
+ padding: 2px 7px;
12
+ border-radius: 4px;
13
+ font-family: var(--dm-font-mono, monospace);
14
+ margin-right: 6px;
15
+ vertical-align: middle;
16
+ }
17
+
18
+ .method-get {
19
+ background: color-mix(in srgb, var(--dm-success) 15%, transparent);
20
+ color: var(--dm-success);
21
+ }
22
+
23
+ .method-post {
24
+ background: color-mix(in srgb, var(--dm-primary) 15%, transparent);
25
+ color: var(--dm-primary);
26
+ }
27
+
28
+ .method-put {
29
+ background: color-mix(in srgb, var(--dm-warning) 15%, transparent);
30
+ color: var(--dm-warning-dark);
31
+ }
32
+
33
+ .method-patch {
34
+ background: color-mix(in srgb, var(--dm-info) 15%, transparent);
35
+ color: var(--dm-info);
36
+ }
37
+
38
+ .method-delete {
39
+ background: color-mix(in srgb, var(--dm-danger) 15%, transparent);
40
+ color: var(--dm-danger);
41
+ }
42
+
43
+ .endpoint-path {
44
+ font-family: var(--dm-font-mono, monospace);
45
+ font-size: 14px;
46
+ }
47
+
48
+ .auth-note {
49
+ font-size: 12px;
50
+ color: var(--dm-text-muted, #6b7280);
51
+ margin: 4px 0 12px;
52
+ }
53
+
54
+ .auth-note code {
55
+ font-size: 11px;
56
+ }
57
+
58
+ .docs-body h3 {
59
+ margin-top: 24px;
60
+ margin-bottom: 8px;
61
+ }
62
+
63
+ .docs-body h3:first-child {
64
+ margin-top: 0;
65
+ }
66
+ </style>
67
+
68
+
69
+ <div class="row">
70
+ <div class="col-12">
71
+ <div class="docs-body">
72
+
73
+ <p>The scaffolder builds a working system - collection, form, Actions and optionally a project, roles, users, menus,
74
+ API tokens and API Builder endpoints - from a bundled <strong>recipe</strong> in one call. In the admin it is
75
+ the "scaffold a working system in one click" panel in the page editor's CRUD shortcut slideover and on the
76
+ Building a CRUD App tutorial. Full recipe format: <code>docs/scaffolding.md</code>.</p>
77
+
78
+ <h2>Endpoints</h2>
79
+
80
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/scaffold/recipes</span></h3>
81
+ <p class="auth-note">Requires: <code>collections.create</code></p>
82
+ <p>List the bundled recipes with just what a picker needs.</p>
83
+ <pre class="code-block"><code class="language-json">// Response 200
84
+ { "recipes": [
85
+ { "slug": "contact-list", "name": "Contact list (CRM-lite)", "description": "...", "icon": "...",
86
+ "options": [
87
+ { "name": "collectionSlug", "label": "Collection slug", "default": "contacts", "hint": "..." },
88
+ { "name": "formSlug", "label": "Form slug", "default": "contact-quick-add" },
89
+ { "name": "actionPrefix", "label": "Action slug prefix", "default": "contact" }
90
+ ] }
91
+ ] }</code></pre>
92
+
93
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/scaffold/recipes/:slug</span></h3>
94
+ <p class="auth-note">Requires: <code>collections.create</code></p>
95
+ <p>The whole recipe document, unresolved (placeholders such as <code>&#123;&#123;collectionSlug&#125;&#125;</code> still in place) -
96
+ the preview of what apply will create. <code>404</code> for an unknown recipe. There is no separate dry-run
97
+ endpoint.</p>
98
+
99
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/scaffold/apply</span></h3>
100
+ <p class="auth-note">Requires: <code>collections.create</code></p>
101
+ <p>Apply a recipe. <code>options</code> overrides the recipe's option defaults by name; anything omitted or blank
102
+ keeps its default.</p>
103
+ <pre class="code-block"><code class="language-bash">curl -X POST https://example.com/api/scaffold/apply \
104
+ -H 'Authorization: Bearer &lt;access_token&gt;' \
105
+ -H 'Content-Type: application/json' \
106
+ -d '{"recipe":"contact-list","options":{"collectionSlug":"leads","formSlug":"lead-form"&#125;&#125;'</code></pre>
107
+ <pre class="code-block"><code class="language-json">// Response 200
108
+ {
109
+ "created": {
110
+ "collection": "leads",
111
+ "form": "lead-form",
112
+ "actions": ["contact-followup"],
113
+ "roles": [], "users": [], "menus": [],
114
+ "apiTokens": [],
115
+ "apiEndpoints": []
116
+ },
117
+ "skipped": [],
118
+ "warnings": [],
119
+ "snippet": "[form name=\"lead-form\" /]\n\n## Contacts\n\n[collection slug=\"leads\" ...]"
120
+ }
121
+ // Error 400
122
+ { "error": "recipe slug is required" }
123
+ // Error 409 - something the recipe would create already exists
124
+ { "error": "Cannot apply recipe - conflicts: ...", "conflicts": ["Collection \"leads\" already exists"] }</code></pre>
125
+ <p><code>snippet</code> is Markdown ready to paste into a page - usually the form embed plus a collection
126
+ display.</p>
127
+
128
+ <h2>What apply creates</h2>
129
+ <p>In this order, with every option value substituted into the recipe first:</p>
130
+ <ol>
131
+ <li><strong>Project</strong> - when the recipe has a <code>project</code> block, the <code>namespace</code>
132
+ option is the project slug. Created if missing, left alone if it exists. A failure here aborts with
133
+ <code>400</code>.</li>
134
+ <li><strong>Pre-flight</strong> - the collection, form, each Action and each menu must not exist yet;
135
+ otherwise <code>409</code> with the full list and nothing below runs.</li>
136
+ <li><strong>Roles</strong> - an existing role name is skipped with a warning (its permissions are not
137
+ changed).</li>
138
+ <li><strong>Menus</strong> - written, and mapped to their <code>locations</code> slots unless a slot is already
139
+ taken (warning) or the menu says <code>force: true</code>.</li>
140
+ <li><strong>Collection</strong> - schema, API access rules and <code>rowAccess</code>.</li>
141
+ <li><strong>Form</strong> - its collection action points at the new collection.</li>
142
+ <li><strong>Users</strong> - only when the recipe supplies a password; an existing email or a missing password
143
+ is skipped with a warning. Users in a project recipe get <code>projects: [namespace]</code>.</li>
144
+ <li><strong>Actions</strong> - need MongoDB; without it each is skipped with the warning
145
+ <code>MongoDB not configured - action "..." skipped (Pro feature)</code>. When the form's
146
+ <code>settings.actionSlug</code> is <code>""</code>, it is wired to the first Action created.</li>
147
+ <li><strong>API tokens</strong> (<code>apiTokens: [{name, scopes?, expiresAt?}]</code>) - bound to the recipe's
148
+ project (else the <code>namespace</code> option, else <code>core</code>). The plaintext is returned once
149
+ in <code>created.apiTokens[].token</code>; a token with the same name in that project is skipped and never
150
+ re-issued. See <a href="#/docs/api/external">External API</a>.</li>
151
+ <li><strong>API endpoints</strong> (<code>apiEndpoints: [{path, collection, filter, ...}]</code>) - same project
152
+ rule; a definition with the same path shape is skipped. See
153
+ <a href="#/docs/api/builder">API Builder</a>.</li>
154
+ </ol>
155
+ <p>Everything a project recipe creates is tagged <code>meta.project: &lt;namespace&gt;</code>. Recipes cannot create
156
+ pages, blocks or views.</p>
157
+
158
+ <h2>Partial results</h2>
159
+ <p>Apply is not a transaction. Pre-flight keeps the main pieces from colliding, but after it a failing role, menu,
160
+ user, Action, token or endpoint becomes a warning and the rest carries on. Read <code>skipped</code> (entries
161
+ such as <code>role:hr</code>, <code>user:hr@example.com</code>, <code>apiToken:mobile</code>,
162
+ <code>apiEndpoint:/latest</code> or an Action slug) and <code>warnings</code> to see what did not land. A
163
+ project created in step 1 stays even when pre-flight then refuses.</p>
164
+
165
+ <h2>Recipes and options</h2>
166
+ <p>Recipes are JSON files in <code>server/services/recipes/</code> (bundled: <code>contact-list</code> and
167
+ <code>onboarding</code>); a new file is listed on the next request. Each <code>options[]</code> entry has a
168
+ <code>name</code>, <code>label</code>, <code>default</code> and <code>hint</code>. A default may refer to an
169
+ earlier option (<code>"default": "&#123;&#123;namespace&#125;&#125;-form"</code>).</p>
170
+ <ul>
171
+ <li><code>&#123;&#123;optionName&#125;&#125;</code> is replaced throughout the recipe at apply time.</li>
172
+ <li>Runtime placeholders such as <code>&#123;&#123;entry.data.email&#125;&#125;</code>, <code>&#123;&#123;user.id&#125;&#125;</code> and
173
+ <code>&#123;&#123;now&#125;&#125;</code> are left for the Action to resolve when it runs.</li>
174
+ <li>Every value supplied in <code>options</code> is slugified (lower case, anything other than letters and
175
+ digits becomes a hyphen) - including options that are not slugs.</li>
176
+ </ul>
177
+
178
+ <h2>Permissions</h2>
179
+ <p>All three endpoints need a signed-in user whose role holds <code>collections.create</code>. No other permission
180
+ is checked, although a recipe can also create roles, users, menus, API tokens and endpoints - grant
181
+ <code>collections.create</code> only to roles you would trust with those.</p>
182
+
183
+ </div>
184
+ </div>
185
+ </div>
@@ -69,80 +69,88 @@
69
69
  <div class="col-12">
70
70
  <div class="docs-body">
71
71
 
72
- <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/settings</span>
73
- </h3>
74
- <p class="auth-note">Requires Bearer token + <code>settings</code> permission.</p>
75
- <p>Return the full site settings object from <code>config/site.json</code>.</p>
76
- <pre class="code-block"><code>// Response 200
77
- { "siteName": "My Site", "adminTheme": "charcoal-dark", "smtp": { ... }, ... }</code></pre>
78
-
79
- <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/settings</span>
80
- </h3>
81
- <p class="auth-note">Requires Bearer token + <code>settings</code> permission.</p>
82
- <p>Replace the site settings object. Send the full merged object - partial updates overwrite the
83
- entire config.</p>
84
- <pre class="code-block"><code>// Request body - full site settings object
85
- { "siteName": "My Site", "adminTheme": "ocean-dark", ... }
86
-
72
+ <p>Site settings live in <code>config/site.json</code> and are edited at System &gt; Site Settings. They need the
73
+ <code>settings</code> permission (read or update). The theme has its own endpoints, under Theme below.</p>
74
+
75
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/settings</span></h3>
76
+ <p class="auth-note">Requires Bearer token + <code>settings</code> read permission.</p>
77
+ <p>The site settings. The SMTP password is always sent back empty.</p>
78
+ <pre class="code-block"><code>// Response 200
79
+ { "title": "My Site", "tagline": "...", "baseUrl": "https://example.com", "adminHome": "/",
80
+ "smtp": { "host": "...", "pass": "", ... }, "footer": { ... }, "seo": { ... }, ... }</code></pre>
81
+
82
+ <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/settings</span></h3>
83
+ <p class="auth-note">Requires Bearer token + <code>settings</code> update permission.</p>
84
+ <p>Save settings. Send only what changes: top-level keys are merged into the stored settings, and objects such as
85
+ <code>smtp</code> or <code>footer</code> are merged one level deep. An empty <code>smtp.pass</code> keeps the
86
+ stored password. An unknown key is refused with 400, so a typo cannot be saved silently. The public pages'
87
+ cache is cleared.</p>
88
+ <table class="table table-sm">
89
+ <thead><tr><th>Key</th><th>What it is</th></tr></thead>
90
+ <tbody>
91
+ <tr><td><code>title</code>, <code>tagline</code>, <code>description</code>, <code>logo</code>,
92
+ <code>favicon</code></td><td>The site's name and identity</td></tr>
93
+ <tr><td><code>baseUrl</code></td><td>Site URL - an origin only (<code>https://example.com</code>, no path). Canonical
94
+ links, the sitemap and password reset links are built on it.</td></tr>
95
+ <tr><td><code>adminHome</code></td><td>The admin screen <code>#/</code> opens, e.g. <code>/plugins/blog</code>;
96
+ empty for the Dashboard</td></tr>
97
+ <tr><td><code>smtp</code></td><td>Mail server: <code>host</code>, <code>port</code>, <code>user</code>,
98
+ <code>pass</code>, <code>secure</code>, <code>fromAddress</code>, <code>fromName</code></td></tr>
99
+ <tr><td><code>seo</code>, <code>footer</code>, <code>social</code>, <code>backToTop</code>,
100
+ <code>cookieConsent</code>, <code>breadcrumbs</code>, <code>layoutOptions</code></td><td>The Site Settings
101
+ tabs of the same names</td></tr>
102
+ <tr><td><code>theme</code>, <code>adminTheme</code>, <code>autoTheme</code>, <code>fontFamily</code>,
103
+ <code>fontSize</code>, <code>adminBrand</code>, <code>locale</code>, <code>analytics</code>, <code>brand</code>,
104
+ <code>url</code>, <code>baseTheme</code></td><td>Also accepted; most are set from Theme or kept for older
105
+ sites</td></tr>
106
+ </tbody>
107
+ </table>
108
+ <pre class="code-block"><code>// Request body
109
+ { "tagline": "Fresh bread daily", "smtp": { "port": 465, "secure": true } }
87
110
  // Response 200
88
- { "success": true }</code></pre>
111
+ { "success": true }
112
+ // Error 400
113
+ { "error": "Unknown settings keys: siteName" }</code></pre>
89
114
 
90
- <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/settings/test-email</span>
91
- </h3>
92
- <p class="auth-note">Requires Bearer token + <code>settings</code> permission.</p>
93
- <p>Send a test email using the stored SMTP configuration.</p>
94
- <table class="table table-sm">
95
- <thead>
96
- <tr>
97
- <th>Field</th>
98
- <th>Type</th>
99
- <th>Description</th>
100
- </tr>
101
- </thead>
102
- <tbody>
103
- <tr>
104
- <td><code>to</code></td>
105
- <td>string</td>
106
- <td>Optional. Recipient address. Defaults to the configured From Address.</td>
107
- </tr>
108
- </tbody>
109
- </table>
110
- <pre class="code-block"><code>// Response 200
115
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/settings/test-email</span></h3>
116
+ <p class="auth-note">Requires Bearer token + <code>settings</code> read permission.</p>
117
+ <p>Send a test email with the saved SMTP settings.</p>
118
+ <pre class="code-block"><code>// Request body (optional - defaults to the From address)
119
+ { "to": "alice@example.com" }
120
+ // Response 200
111
121
  { "success": true, "message": "Test email sent to alice@example.com" }
112
-
113
122
  // Error 400
114
123
  { "error": "SMTP is not configured. Save your SMTP settings first." }</code></pre>
115
124
 
116
- <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/settings/custom-css</span>
117
- </h3>
118
- <p class="auth-note">Requires Bearer token + <code>settings</code> permission.</p>
119
- <p>Return the current custom CSS from <code>content/custom.css</code>.</p>
120
- <pre class="code-block"><code>// Response 200
125
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/settings/db-status</span></h3>
126
+ <p class="auth-note">Requires Bearer token + <code>settings</code> read permission.</p>
127
+ <p>Whether any MongoDB connection is set up (<code>config/connections.json</code>).</p>
128
+ <pre class="code-block"><code>// Response 200
129
+ { "configured": true, "connections": ["default"] }</code></pre>
130
+
131
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/settings/custom-css</span></h3>
132
+ <p class="auth-note">Requires Bearer token + <code>settings</code> update permission.</p>
133
+ <p>The site-wide custom CSS (<code>content/custom.css</code>), which is inlined into every public page.</p>
134
+ <pre class="code-block"><code>// Response 200
121
135
  { "css": "body { font-family: sans-serif; }" }</code></pre>
122
136
 
123
- <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/settings/custom-css</span>
124
- </h3>
125
- <p class="auth-note">Requires Bearer token + <code>settings</code> permission.</p>
126
- <p>Write CSS to <code>content/custom.css</code>. Maximum size is 100 KB.</p>
127
- <table class="table table-sm">
128
- <thead>
129
- <tr>
130
- <th>Field</th>
131
- <th>Type</th>
132
- <th>Description</th>
133
- </tr>
134
- </thead>
135
- <tbody>
136
- <tr>
137
- <td><code>css</code></td>
138
- <td>string</td>
139
- <td>CSS string (max 100 KB)</td>
140
- </tr>
141
- </tbody>
142
- </table>
143
- <pre class="code-block"><code>// Response 200
137
+ <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/settings/custom-css</span></h3>
138
+ <p class="auth-note">Requires Bearer token + <code>settings</code> update permission.</p>
139
+ <p>Replace the custom CSS (at most 100 KB). The public pages' cache is cleared.</p>
140
+ <pre class="code-block"><code>// Request body
141
+ { "css": "..." }
142
+ // Response 200
144
143
  { "success": true }</code></pre>
145
144
 
145
+ <h3 style="margin-top:24px;font-size:15px;text-transform:uppercase;letter-spacing:.5px;opacity:.6">Theme</h3>
146
+
147
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/theme</span></h3>
148
+ <p class="auth-note">Requires Bearer token + <code>theme</code> read permission.</p>
149
+ <p>The site's theme settings (<code>config/theme.json</code>): the public and admin themes, day/night switching,
150
+ fonts and any custom themes. <code>PUT /api/theme</code> (update permission) saves them, answering 400 with the
151
+ problems if they do not validate. <code>GET /api/theme/catalog</code> lists the themes and colour tokens
152
+ available, and <code>GET /api/theme/themes/:id</code> one theme's token values.</p>
153
+
146
154
  </div>
147
155
  </div>
148
156
  </div>