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,89 +69,66 @@
69
69
  <div class="col-12">
70
70
  <div class="docs-body">
71
71
 
72
+ <p>Media files live in <code>content/media/</code> and are served publicly at <code>/media/&lt;name&gt;</code>. The
73
+ endpoints need the <code>media</code> permission for the action named (read, create, update, delete).</p>
74
+
72
75
  <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/media</span></h3>
73
- <p class="auth-note">Requires Bearer token + <code>media</code> permission.</p>
74
- <p>List all media files in the uploads directory.</p>
75
- <pre class="code-block"><code>// Response 200
76
- [ { "name": "hero.jpg", "url": "/media/hero.jpg", "size": 204800, "mime": "image/jpeg" } ]</code></pre>
77
-
78
- <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/media</span>
79
- </h3>
80
- <p class="auth-note">Requires Bearer token + <code>media</code> permission. Content-Type: <code>multipart/form-data</code>.
81
- </p>
82
- <p>Upload one or more files. Returns a single object for one file, or an array for multiple.</p>
83
- <pre class="code-block"><code>// Response 201 (single)
84
- { "name": "photo.jpg", "url": "/media/photo.jpg", "size": 98304, "mime": "image/jpeg" }</code></pre>
85
-
86
- <h3><span class="method-badge method-patch">PATCH</span><span
87
- class="endpoint-path">/api/media/:name</span></h3>
88
- <p class="auth-note">Requires Bearer token + <code>media</code> permission.</p>
89
- <p>Rename a media file. Fails with 409 if the new name already exists.</p>
90
- <table class="table table-sm">
91
- <thead>
92
- <tr>
93
- <th>Field</th>
94
- <th>Type</th>
95
- <th>Description</th>
96
- </tr>
97
- </thead>
98
- <tbody>
99
- <tr>
100
- <td><code>newName</code></td>
101
- <td>string</td>
102
- <td>New filename (will be sanitised)</td>
103
- </tr>
104
- </tbody>
105
- </table>
106
- <pre class="code-block"><code>// Response 200
107
- { "name": "new-photo.jpg", "url": "/media/new-photo.jpg", ... }</code></pre>
108
-
109
- <h3><span class="method-badge method-delete">DELETE</span><span
110
- class="endpoint-path">/api/media/:name</span></h3>
111
- <p class="auth-note">Requires Bearer token + <code>media</code> permission.</p>
112
- <p>Delete a media file from the uploads directory.</p>
113
- <pre class="code-block"><code>// Response 200
114
- { "success": true }</code></pre>
115
-
116
- <h3><span class="method-badge method-get">GET</span><span
117
- class="endpoint-path">/api/media/:name/info</span></h3>
118
- <p class="auth-note">Requires Bearer token + <code>media</code> permission.</p>
119
- <p>Return image metadata (dimensions, format, file size) for editable image formats (JPEG, PNG, WebP,
120
- GIF, TIFF).</p>
121
- <pre class="code-block"><code>// Response 200
122
- { "width": 1920, "height": 1080, "format": "jpeg", "size": 204800 }</code></pre>
123
-
124
- <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/media/:name/transform</span>
125
- </h3>
126
- <p class="auth-note">Requires Bearer token + <code>media</code> permission.</p>
127
- <p>Apply image transformations (resize, crop, rotate, watermark, etc.) and optionally save to a new
128
- filename.</p>
129
- <table class="table table-sm">
130
- <thead>
131
- <tr>
132
- <th>Field</th>
133
- <th>Type</th>
134
- <th>Description</th>
135
- </tr>
136
- </thead>
137
- <tbody>
138
- <tr>
139
- <td><code>operations</code></td>
140
- <td>object</td>
141
- <td>Transformation operations to apply</td>
142
- </tr>
143
- <tr>
144
- <td><code>saveAs</code></td>
145
- <td>string</td>
146
- <td>Optional. Output filename. Defaults to overwriting the source.</td>
147
- </tr>
148
- </tbody>
149
- </table>
150
- <pre class="code-block"><code>// Request body
76
+ <p class="auth-note">Requires Bearer token + <code>media</code> read permission.</p>
77
+ <p>Every file in the library, newest first.</p>
78
+ <pre class="code-block"><code>// Response 200
79
+ [ { "name": "hero.jpg", "url": "/media/hero.jpg", "size": 204800, "createdAt": "2026-09-27T10:30:00.000Z" } ]</code></pre>
80
+
81
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/media</span></h3>
82
+ <p class="auth-note">Requires Bearer token + <code>media</code> create permission. Content-Type:
83
+ <code>multipart/form-data</code>.</p>
84
+ <p>Upload one or more files. Names are made safe and may be at most 200 characters. Images (JPEG, PNG, GIF, WebP,
85
+ SVG, ICO, BMP, TIFF), PDF, text, CSV, Word and Excel documents, video and audio are accepted; anything else is
86
+ refused with 400. The size limit is <code>uploads.maxFileSize</code> in <code>config/server.json</code>
87
+ (10 MB by default). One file answers with an object, several with an array.</p>
88
+ <pre class="code-block"><code>// Response 201 (one file)
89
+ { "name": "photo.jpg", "url": "/media/photo.jpg" }
90
+ // Error 400
91
+ { "error": "File type 'application/zip' is not allowed. Allowed types: images, documents, audio, video." }</code></pre>
92
+
93
+ <h3><span class="method-badge method-patch">PATCH</span><span class="endpoint-path">/api/media/:name</span></h3>
94
+ <p class="auth-note">Requires Bearer token + <code>media</code> update permission.</p>
95
+ <p>Rename a file. Pages that link to the old name are not changed. Answers 409 if the new name is taken.</p>
96
+ <pre class="code-block"><code>// Request body
97
+ { "newName": "new-photo.jpg" }
98
+ // Response 200
99
+ { "name": "new-photo.jpg", "url": "/media/new-photo.jpg" }</code></pre>
100
+
101
+ <h3><span class="method-badge method-delete">DELETE</span><span class="endpoint-path">/api/media/:name</span></h3>
102
+ <p class="auth-note">Requires Bearer token + <code>media</code> delete permission.</p>
103
+ <p>Delete a file.</p>
104
+ <pre class="code-block"><code>// Response 200
105
+ { "success": true }
106
+ // Error 404
107
+ { "error": "File not found" }</code></pre>
108
+
109
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/media/:name/info</span></h3>
110
+ <p class="auth-note">Requires Bearer token + <code>media</code> read permission.</p>
111
+ <p>An image's size and format. Only for images that can be edited (JPEG, PNG, WebP, GIF, TIFF); others answer 400.</p>
112
+ <pre class="code-block"><code>// Response 200
113
+ { "width": 1920, "height": 1080, "format": "jpeg" }</code></pre>
114
+
115
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/media/:name/transform</span></h3>
116
+ <p class="auth-note">Requires Bearer token + <code>media</code> update permission.</p>
117
+ <p>Edit an image - the Media screen's image editor - and save it over the original or as a new file.</p>
118
+ <table class="table table-sm">
119
+ <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
120
+ <tbody>
121
+ <tr><td><code>operations</code></td><td>object</td><td>Any of <code>rotate</code>, <code>flip</code>,
122
+ <code>flop</code>, <code>crop</code>, <code>resize</code>, <code>preset</code>, <code>adjustments</code>,
123
+ <code>watermark</code>, <code>border</code>, <code>format</code></td></tr>
124
+ <tr><td><code>saveAs</code></td><td>string</td><td>Optional. New file name; left out, the original is
125
+ overwritten</td></tr>
126
+ </tbody>
127
+ </table>
128
+ <pre class="code-block"><code>// Request body
151
129
  { "operations": { "resize": { "width": 800, "height": 600 }, "format": "webp" }, "saveAs": "hero-800.webp" }
152
-
153
130
  // Response 200
154
- { "name": "hero-800.webp", "url": "/media/hero-800.webp", ... }</code></pre>
131
+ { "name": "hero-800.webp", "url": "/media/hero-800.webp", "width": 800, "height": 600 }</code></pre>
155
132
 
156
133
  </div>
157
134
  </div>
@@ -69,31 +69,75 @@
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/navigation</span>
73
- </h3>
74
- <p class="auth-note">Requires Bearer token + <code>navigation</code> permission.</p>
75
- <p>Return the navigation configuration from <code>config/navigation.json</code>.</p>
76
- <pre class="code-block"><code>// Response 200
77
- {
72
+ <p>The site's navigation is made of <strong>menus</strong>: each is a file in <code>config/menus/&lt;slug&gt;.json</code>,
73
+ edited at Content &gt; Menus, and shown by mapping it to a slot (the navbar, the footer) or placing it on a page
74
+ with <code>[menu slug="..." /]</code>. The endpoints need the <code>menus</code> permission for the action named.
75
+ The old single navigation file (<code>config/navigation.json</code>) was retired when menus arrived - see
76
+ Legacy navigation at the end.</p>
77
+
78
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/menus</span></h3>
79
+ <p class="auth-note">Requires Bearer token + <code>menus</code> read permission.</p>
80
+ <p>Every menu you may see (a user confined to some projects sees their projects' menus), without items.</p>
81
+
82
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/menus/:slug</span></h3>
83
+ <p class="auth-note">Requires Bearer token + <code>menus</code> read permission.</p>
84
+ <p>One menu, with its items. Sub-items go under <code>items</code>.</p>
85
+ <pre class="code-block"><code>// Response 200
86
+ { "slug": "main", "name": "Main navigation", "variant": "dark", "position": "sticky",
78
87
  "items": [
79
- { "label": "Home", "url": "/" },
80
- { "label": "About", "url": "/about" },
81
- { "label": "Resources", "items": [ { "label": "Blog", "url": "/blog" } ] }
82
- ]
83
- }</code></pre>
84
-
85
- <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/navigation</span>
86
- </h3>
87
- <p class="auth-note">Requires Bearer token + <code>navigation</code> permission.</p>
88
- <p>Replace the navigation config. Sub-items must use the <code>items</code> key (the server normalises
89
- <code>children</code>
90
- to <code>items</code> automatically).</p>
91
- <pre class="code-block"><code>// Request body
92
- { "items": [ { "label": "Home", "url": "/" }, { "label": "About", "url": "/about" } ] }
93
-
94
- // Response 200
88
+ { "text": "Home", "url": "/", "icon": "home" },
89
+ { "text": "Services", "items": [ { "text": "Design", "url": "/design" } ] }
90
+ ],
91
+ "meta": { "createdAt": "...", "updatedAt": "..." } }</code></pre>
92
+
93
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/menus</span></h3>
94
+ <p class="auth-note">Requires Bearer token + <code>menus</code> create permission.</p>
95
+ <p>Create a menu. <code>slug</code> is required; <code>name</code>, <code>description</code>, <code>items</code> and
96
+ the display options (<code>variant</code>, <code>position</code>, <code>orientation</code>, <code>align</code>,
97
+ <code>side</code>, <code>float</code>, <code>highlight</code>, <code>binding</code>) are optional. An item may
98
+ carry <code>visibility</code> to show it only to some roles.</p>
99
+ <pre class="code-block"><code>// Response 201 - the created menu
100
+ // Error 409
101
+ { "error": "Menu \"main\" already exists" }</code></pre>
102
+
103
+ <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/menus/:slug</span></h3>
104
+ <p class="auth-note">Requires Bearer token + <code>menus</code> update permission.</p>
105
+ <p>Save a menu. The public pages that show it are refreshed.</p>
106
+
107
+ <h3><span class="method-badge method-delete">DELETE</span><span class="endpoint-path">/api/menus/:slug</span></h3>
108
+ <p class="auth-note">Requires Bearer token + <code>menus</code> delete permission.</p>
109
+ <p>Delete a menu. Refused while it is mapped to a slot.</p>
110
+ <pre class="code-block"><code>// Error 409
111
+ { "error": "Cannot delete - menu is mapped to slot \"navbar\". Unmap it first." }</code></pre>
112
+ <p><code>POST /api/menus/:slug/duplicate</code> (create permission) makes a copy.</p>
113
+
114
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/menu-locations</span></h3>
115
+ <p class="auth-note">Requires Bearer token + <code>menus</code> read permission.</p>
116
+ <p>Which menu fills each slot. <code>GET /api/menu-locations/registry</code> lists the slots there are
117
+ (<code>navbar</code>, <code>footer-primary</code>, <code>footer-legal</code>, <code>overlay</code>,
118
+ <code>admin-sidebar</code>, and any a plugin adds).</p>
119
+ <pre class="code-block"><code>// Response 200
120
+ { "navbar": "main", "footer-primary": "footer", "admin-sidebar": "admin-sidebar" }</code></pre>
121
+
122
+ <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/menu-locations</span></h3>
123
+ <p class="auth-note">Requires Bearer token + <code>menus</code> update permission.</p>
124
+ <p>Save the slot map (same shape as above).</p>
125
+ <pre class="code-block"><code>// Response 200
95
126
  { "success": true }</code></pre>
96
127
 
128
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/context-menus</span></h3>
129
+ <p class="auth-note">Requires Bearer token + <code>context-menus</code> read permission.</p>
130
+ <p>Right-click menus for the public site (Content &gt; Context Menus) are a separate kind of menu, with the same
131
+ shape of routes: <code>GET</code>, <code>POST /api/context-menus</code>, <code>GET</code>, <code>PUT</code>,
132
+ <code>DELETE /api/context-menus/:slug</code>, <code>POST /api/context-menus/:slug/duplicate</code>, and
133
+ <code>GET /api/context-menus/action-types</code> for what an item can do.</p>
134
+
135
+ <h3>Legacy navigation</h3>
136
+ <p><code>GET</code> and <code>PUT /api/navigation</code> (the <code>navigation</code> permission) still read and
137
+ write <code>config/navigation.json</code>, but the public site no longer reads that file: on first start after
138
+ menus arrived it was moved into the <code>main</code> menu and renamed <code>navigation.json.bak</code>. Use the
139
+ menus endpoints above.</p>
140
+
97
141
  </div>
98
142
  </div>
99
143
  </div>
@@ -69,145 +69,125 @@
69
69
  <div class="col-12">
70
70
  <div class="docs-body">
71
71
 
72
- <h3><span class="method-badge method-post">POST</span><span
73
- class="endpoint-path">/api/pages/preview</span></h3>
74
- <p class="auth-note">Requires Bearer token + <code>pages</code> permission.</p>
75
- <p>Render Markdown to HTML (shortcodes processed, no frontmatter). Useful for live editor
76
- previews.</p>
77
- <table class="table table-sm">
78
- <thead>
79
- <tr>
80
- <th>Field</th>
81
- <th>Type</th>
82
- <th>Description</th>
83
- </tr>
84
- </thead>
85
- <tbody>
86
- <tr>
87
- <td><code>markdown</code></td>
88
- <td>string</td>
89
- <td>Markdown string to render</td>
90
- </tr>
91
- </tbody>
92
- </table>
93
- <pre class="code-block"><code>// Response 200
94
- { "html": "&lt;p&gt;Hello &lt;strong&gt;world&lt;/strong&gt;&lt;/p&gt;" }</code></pre>
95
-
96
- <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/pages/tags</span>
97
- </h3>
98
- <p class="auth-note">Requires Bearer token + <code>pages</code> permission.</p>
99
- <p>Aggregate all unique tags across every page, sorted alphabetically.</p>
100
- <pre class="code-block"><code>// Response 200
101
- { "tags": ["guide", "news", "tutorial"] }</code></pre>
102
-
103
- <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/pages</span></h3>
104
- <p class="auth-note">Requires Bearer token + <code>pages</code> permission.</p>
105
- <p>List all pages with their metadata. Body content is excluded.</p>
106
- <pre class="code-block"><code>// Response 200
107
- [
108
- {
109
- "urlPath": "/about",
110
- "title": "About Us",
111
- "slug": "about",
112
- "status": "published",
113
- "layout": "default",
114
- "tags": [],
115
- "updatedAt": "2024-01-15T10:30:00.000Z",
116
- "createdAt": "2024-01-01T09:00:00.000Z"
117
- }
118
- ]</code></pre>
119
-
120
- <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/pages/*</span>
121
- </h3>
122
- <p class="auth-note">Requires Bearer token + <code>pages</code> permission. The <code>*</code> is the
123
- URL path,
124
- e.g. <code>/api/pages/about</code>.</p>
125
- <p>Retrieve a single page including its frontmatter and full body content.</p>
126
- <pre class="code-block"><code>// Response 200
72
+ <p>Pages are Markdown files in <code>content/pages/</code>, addressed by their URL path. All endpoints need the
73
+ <code>pages</code> permission for the action named (read, create, update, delete). In the paths below,
74
+ <code>*</code> is the page's URL path without the leading slash, e.g. <code>/api/pages/about</code>.</p>
75
+
76
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/pages</span></h3>
77
+ <p class="auth-note">Requires Bearer token + <code>pages</code> read permission.</p>
78
+ <p>List every page with its metadata (no body).</p>
79
+ <pre class="code-block"><code>// Response 200
80
+ [ { "urlPath": "/about", "title": "About Us", "slug": "about", "status": "published", "layout": "default",
81
+ "visibility": "public", "tags": [], "sortOrder": 0, "category": null, "project": null, "resolvedProject": "core",
82
+ "plugin": null, "bundled": false, "updatedAt": "2026-09-27T10:30:00.000Z", "createdAt": "...", "versionCount": 3 } ]</code></pre>
83
+ <p>A page may still carry a legacy <code>showInNav</code> value; nothing reads it - the site's navigation is edited in
84
+ Menus.</p>
85
+
86
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/pages/*</span></h3>
87
+ <p class="auth-note">Requires Bearer token + <code>pages</code> read permission.</p>
88
+ <p>One page, with its frontmatter fields and full Markdown body.</p>
89
+ <pre class="code-block"><code>// Response 200
127
90
  { "urlPath": "/about", "title": "About Us", "body": "## Our Story\n\nWe started...", ... }
128
-
129
91
  // Error 404
130
92
  { "error": "Page not found" }</code></pre>
131
93
 
132
- <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/pages</span>
133
- </h3>
134
- <p class="auth-note">Requires Bearer token + <code>pages</code> permission.</p>
135
- <p>Create a new page. Fails with 409 if a page already exists at the given path.</p>
136
- <table class="table table-sm">
137
- <thead>
138
- <tr>
139
- <th>Field</th>
140
- <th>Type</th>
141
- <th>Description</th>
142
- </tr>
143
- </thead>
144
- <tbody>
145
- <tr>
146
- <td><code>urlPath</code></td>
147
- <td>string</td>
148
- <td>Required. Public URL path e.g. <code>/about</code></td>
149
- </tr>
150
- <tr>
151
- <td><code>frontmatter</code></td>
152
- <td>object</td>
153
- <td>YAML frontmatter fields (title, status, etc.)</td>
154
- </tr>
155
- <tr>
156
- <td><code>body</code></td>
157
- <td>string</td>
158
- <td>Markdown content</td>
159
- </tr>
160
- </tbody>
161
- </table>
162
- <pre class="code-block"><code>// Response 201 - returns the created page object</code></pre>
163
-
164
- <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/pages/*</span>
165
- </h3>
166
- <p class="auth-note">Requires Bearer token + <code>pages</code> permission.</p>
167
- <p>Update an existing page. Optionally rename it to a new URL path.</p>
168
- <table class="table table-sm">
169
- <thead>
170
- <tr>
171
- <th>Field</th>
172
- <th>Type</th>
173
- <th>Description</th>
174
- </tr>
175
- </thead>
176
- <tbody>
177
- <tr>
178
- <td><code>frontmatter</code></td>
179
- <td>object</td>
180
- <td>Updated frontmatter fields</td>
181
- </tr>
182
- <tr>
183
- <td><code>body</code></td>
184
- <td>string</td>
185
- <td>Updated Markdown content</td>
186
- </tr>
187
- <tr>
188
- <td><code>newUrlPath</code></td>
189
- <td>string</td>
190
- <td>Optional. Rename the page to a different URL path</td>
191
- </tr>
192
- </tbody>
193
- </table>
194
- <pre class="code-block"><code>// Response 200 - returns the updated page object
195
-
94
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/pages</span></h3>
95
+ <p class="auth-note">Requires Bearer token + <code>pages</code> create permission.</p>
96
+ <p>Create a page.</p>
97
+ <table class="table table-sm">
98
+ <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
99
+ <tbody>
100
+ <tr><td><code>urlPath</code></td><td>string</td><td>Required. Public URL path, e.g. <code>/about</code></td></tr>
101
+ <tr><td><code>frontmatter</code></td><td>object</td><td>Frontmatter fields (title, status, visibility, layout,
102
+ ...)</td></tr>
103
+ <tr><td><code>body</code></td><td>string</td><td>Markdown content</td></tr>
104
+ </tbody>
105
+ </table>
106
+ <pre class="code-block"><code>// Response 201 - the created page
107
+ // Error 409 - names the project the existing page belongs to
108
+ { "error": "A page already exists at /about (project: core)", "urlPath": "/about", "project": "core" }</code></pre>
109
+
110
+ <h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/pages/*</span></h3>
111
+ <p class="auth-note">Requires Bearer token + <code>pages</code> update permission.</p>
112
+ <p>Update a page, and optionally move it to a new URL path. A move re-aims menu links that pointed at the old
113
+ path and drops the page's share links. Each save keeps a version (see Versions below).</p>
114
+ <table class="table table-sm">
115
+ <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
116
+ <tbody>
117
+ <tr><td><code>frontmatter</code></td><td>object</td><td>Frontmatter fields</td></tr>
118
+ <tr><td><code>body</code></td><td>string</td><td>Markdown content</td></tr>
119
+ <tr><td><code>newUrlPath</code></td><td>string</td><td>Optional. Move the page to this path</td></tr>
120
+ </tbody>
121
+ </table>
122
+ <pre class="code-block"><code>// Response 200 - the updated page
196
123
  // Error 404
197
124
  { "error": "Page not found" }
198
-
199
125
  // Error 409
200
126
  { "error": "A page already exists at that path" }</code></pre>
201
127
 
202
- <h3><span class="method-badge method-delete">DELETE</span><span
203
- class="endpoint-path">/api/pages/*</span></h3>
204
- <p class="auth-note">Requires Bearer token + <code>pages</code> permission.</p>
205
- <p>Delete a page and its source file.</p>
206
- <pre class="code-block"><code>// Response 200
207
- { "success": true }
128
+ <h3><span class="method-badge method-delete">DELETE</span><span class="endpoint-path">/api/pages/*</span></h3>
129
+ <p class="auth-note">Requires Bearer token + <code>pages</code> delete permission.</p>
130
+ <p>Delete a page and its share links.</p>
131
+ <pre class="code-block"><code>// Response 200
132
+ { "success": true }</code></pre>
208
133
 
209
- // Error 404
210
- { "error": "Page not found" }</code></pre>
134
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/pages/tags</span></h3>
135
+ <p class="auth-note">Requires Bearer token + <code>pages</code> read permission.</p>
136
+ <p>Every tag used on any page, sorted.</p>
137
+ <pre class="code-block"><code>// Response 200
138
+ { "tags": ["guide", "news", "tutorial"] }</code></pre>
139
+
140
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/pages/preview</span></h3>
141
+ <p class="auth-note">Requires Bearer token + <code>pages</code> read permission.</p>
142
+ <p>Render Markdown to HTML (shortcodes processed, no frontmatter), as the signed-in user sees it.</p>
143
+ <pre class="code-block"><code>// Request body
144
+ { "markdown": "Hello **world**" }
145
+ // Response 200
146
+ { "html": "&lt;p&gt;Hello &lt;strong&gt;world&lt;/strong&gt;&lt;/p&gt;" }</code></pre>
147
+
148
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/pages/preview/full</span></h3>
149
+ <p class="auth-note">Requires Bearer token + <code>pages</code> read permission.</p>
150
+ <p>Render unsaved editor content as the complete public page - layout, navbar, footer, theme and custom CSS - the
151
+ editor's Live Preview.</p>
152
+ <pre class="code-block"><code>// Request body
153
+ { "urlPath": "/about", "frontmatter": { "title": "About" }, "body": "..." }
154
+ // Response 200
155
+ { "html": "&lt;!doctype html&gt;..." }</code></pre>
156
+
157
+ <h3 style="margin-top:24px;font-size:15px;text-transform:uppercase;letter-spacing:.5px;opacity:.6">Share links</h3>
158
+
159
+ <h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/pages/preview-links</span></h3>
160
+ <p class="auth-note">Requires Bearer token + <code>pages</code> update permission.</p>
161
+ <p>Make a share link that lets anyone with it see one page, draft or not, until it expires.
162
+ <code>expiresIn</code> is <code>1h</code>, <code>24h</code>, <code>7d</code> (default) or <code>30d</code>, or a
163
+ number of seconds up to 30 days. <code>GET /api/pages/preview-links?urlPath=/about</code> lists them and
164
+ <code>DELETE /api/pages/preview-links/:id</code> withdraws one.</p>
165
+ <pre class="code-block"><code>// Request body
166
+ { "urlPath": "/about", "expiresIn": "7d", "label": "For the client" }
167
+ // Response 201
168
+ { "id": "...", "urlPath": "/about", "expiresAt": "...", "token": "...", "url": "https://example.com/_preview?token=..." }</code></pre>
169
+
170
+ <h3 style="margin-top:24px;font-size:15px;text-transform:uppercase;letter-spacing:.5px;opacity:.6">Versions</h3>
171
+
172
+ <h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/versions/list/*</span></h3>
173
+ <p class="auth-note">Requires Bearer token + <code>pages</code> read permission.</p>
174
+ <p>A page's saved versions (its History), newest first. The other version routes take a version's
175
+ <code>filename</code> from this list:</p>
176
+ <table class="table table-sm">
177
+ <thead><tr><th>Route</th><th>Permission</th><th>Does</th></tr></thead>
178
+ <tbody>
179
+ <tr><td><code>GET /api/versions/get/:filename/*</code></td><td>read</td><td>One version's content</td></tr>
180
+ <tr><td><code>POST /api/versions/create/*</code></td><td>update</td><td>Save a named version now
181
+ (<code>{"label": "..."}</code>)</td></tr>
182
+ <tr><td><code>POST /api/versions/restore/:filename/*</code></td><td>update</td><td>Put a version back as the
183
+ page</td></tr>
184
+ <tr><td><code>DELETE /api/versions/delete/:filename/*</code></td><td>delete</td><td>Delete one version</td></tr>
185
+ <tr><td><code>POST /api/versions/bulk-delete/*</code></td><td>delete</td><td>Delete several
186
+ (<code>{"filenames": [...]}</code>)</td></tr>
187
+ <tr><td><code>POST /api/versions/prune/*</code></td><td>delete</td><td>Keep only the most recent
188
+ (<code>{"keep": 10}</code>)</td></tr>
189
+ </tbody>
190
+ </table>
211
191
 
212
192
  </div>
213
193
  </div>