domma-cms 0.25.15 → 0.30.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 (67) hide show
  1. package/admin/js/app.js +2 -2
  2. package/admin/js/lib/sidebar-renderer.js +4 -4
  3. package/admin/js/templates/api-reference.html +68 -1233
  4. package/admin/js/templates/docs/api-actions.html +146 -0
  5. package/admin/js/templates/docs/api-authentication.html +202 -0
  6. package/admin/js/templates/docs/api-collections.html +347 -0
  7. package/admin/js/templates/docs/api-layouts.html +123 -0
  8. package/admin/js/templates/docs/api-media.html +158 -0
  9. package/admin/js/templates/docs/api-navigation.html +99 -0
  10. package/admin/js/templates/docs/api-pages.html +214 -0
  11. package/admin/js/templates/docs/api-plugins.html +136 -0
  12. package/admin/js/templates/docs/api-settings.html +148 -0
  13. package/admin/js/templates/docs/api-users.html +189 -0
  14. package/admin/js/templates/docs/api-views.html +142 -0
  15. package/admin/js/templates/docs/components-howto.html +175 -0
  16. package/admin/js/templates/docs/components-reference.html +259 -0
  17. package/admin/js/templates/docs/components-rules.html +167 -0
  18. package/admin/js/templates/docs/components-walkthrough.html +167 -0
  19. package/admin/js/templates/docs/tutorial-crud.html +344 -0
  20. package/admin/js/templates/docs/tutorial-forms.html +93 -0
  21. package/admin/js/templates/docs/tutorial-plugin.html +234 -0
  22. package/admin/js/templates/docs/usage-actions.html +122 -0
  23. package/admin/js/templates/docs/usage-cta-shortcode.html +124 -0
  24. package/admin/js/templates/docs/usage-dconfig.html +148 -0
  25. package/admin/js/templates/docs/usage-media.html +25 -0
  26. package/admin/js/templates/docs/usage-navigation.html +31 -0
  27. package/admin/js/templates/docs/usage-pages.html +68 -0
  28. package/admin/js/templates/docs/usage-plugins.html +36 -0
  29. package/admin/js/templates/docs/usage-shortcodes.html +810 -0
  30. package/admin/js/templates/docs/usage-site-settings.html +52 -0
  31. package/admin/js/templates/docs/usage-users-roles.html +84 -0
  32. package/admin/js/templates/docs/usage-views.html +105 -0
  33. package/admin/js/templates/documentation.html +60 -1522
  34. package/admin/js/templates/effects.html +752 -752
  35. package/admin/js/templates/forms.html +17 -17
  36. package/admin/js/templates/my-profile.html +17 -17
  37. package/admin/js/templates/project-settings.html +7 -0
  38. package/admin/js/templates/role-editor.html +70 -70
  39. package/admin/js/templates/roles.html +10 -10
  40. package/admin/js/templates/tutorials.html +46 -659
  41. package/admin/js/views/api-reference.js +1 -1
  42. package/admin/js/views/doc-pages.js +1 -0
  43. package/admin/js/views/documentation.js +1 -1
  44. package/admin/js/views/index.js +1 -1
  45. package/admin/js/views/project-settings.js +1 -1
  46. package/admin/js/views/projects.js +3 -7
  47. package/admin/js/views/tutorials.js +1 -1
  48. package/bin/lib/config-merge.js +44 -44
  49. package/config/connections.json.bak +9 -0
  50. package/config/menus/admin-sidebar.json +76 -6
  51. package/package.json +1 -1
  52. package/public/js/site.js +1 -1
  53. package/scripts/migrate-docs.js +280 -0
  54. package/server/middleware/auth.js +253 -253
  55. package/server/routes/api/auth.js +309 -309
  56. package/server/routes/api/collections.js +10 -1
  57. package/server/routes/api/navigation.js +42 -42
  58. package/server/routes/api/projects.js +9 -1
  59. package/server/routes/api/settings.js +141 -141
  60. package/server/routes/public.js +9 -0
  61. package/server/server.js +6 -2
  62. package/server/services/email.js +167 -167
  63. package/server/services/presetCollections.js +1 -0
  64. package/server/services/projects.js +72 -3
  65. package/server/services/sidebar-migration.js +145 -3
  66. package/server/services/userProfiles.js +199 -199
  67. package/server/services/users.js +302 -302
@@ -0,0 +1,99 @@
1
+ <div class="view-header">
2
+ <h1><span data-icon="code"></span> API · Navigation</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: #d1fae5;
20
+ color: #065f46;
21
+ }
22
+
23
+ .method-post {
24
+ background: #dbeafe;
25
+ color: #1e40af;
26
+ }
27
+
28
+ .method-put {
29
+ background: #fef3c7;
30
+ color: #92400e;
31
+ }
32
+
33
+ .method-patch {
34
+ background: #ccfbf1;
35
+ color: #134e4a;
36
+ }
37
+
38
+ .method-delete {
39
+ background: #fee2e2;
40
+ color: #991b1b;
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-color-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
+ <div class="row">
69
+ <div class="col-12">
70
+ <div class="docs-body">
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
+ {
78
+ "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
95
+ { "success": true }</code></pre>
96
+
97
+ </div>
98
+ </div>
99
+ </div>
@@ -0,0 +1,214 @@
1
+ <div class="view-header">
2
+ <h1><span data-icon="code"></span> API · Pages</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: #d1fae5;
20
+ color: #065f46;
21
+ }
22
+
23
+ .method-post {
24
+ background: #dbeafe;
25
+ color: #1e40af;
26
+ }
27
+
28
+ .method-put {
29
+ background: #fef3c7;
30
+ color: #92400e;
31
+ }
32
+
33
+ .method-patch {
34
+ background: #ccfbf1;
35
+ color: #134e4a;
36
+ }
37
+
38
+ .method-delete {
39
+ background: #fee2e2;
40
+ color: #991b1b;
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-color-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
+ <div class="row">
69
+ <div class="col-12">
70
+ <div class="docs-body">
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
127
+ { "urlPath": "/about", "title": "About Us", "body": "## Our Story\n\nWe started...", ... }
128
+
129
+ // Error 404
130
+ { "error": "Page not found" }</code></pre>
131
+
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
+
196
+ // Error 404
197
+ { "error": "Page not found" }
198
+
199
+ // Error 409
200
+ { "error": "A page already exists at that path" }</code></pre>
201
+
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 }
208
+
209
+ // Error 404
210
+ { "error": "Page not found" }</code></pre>
211
+
212
+ </div>
213
+ </div>
214
+ </div>
@@ -0,0 +1,136 @@
1
+ <div class="view-header">
2
+ <h1><span data-icon="code"></span> API · Plugins</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: #d1fae5;
20
+ color: #065f46;
21
+ }
22
+
23
+ .method-post {
24
+ background: #dbeafe;
25
+ color: #1e40af;
26
+ }
27
+
28
+ .method-put {
29
+ background: #fef3c7;
30
+ color: #92400e;
31
+ }
32
+
33
+ .method-patch {
34
+ background: #ccfbf1;
35
+ color: #134e4a;
36
+ }
37
+
38
+ .method-delete {
39
+ background: #fee2e2;
40
+ color: #991b1b;
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-color-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
+ <div class="row">
69
+ <div class="col-12">
70
+ <div class="docs-body">
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>
133
+
134
+ </div>
135
+ </div>
136
+ </div>
@@ -0,0 +1,148 @@
1
+ <div class="view-header">
2
+ <h1><span data-icon="code"></span> API · Settings</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: #d1fae5;
20
+ color: #065f46;
21
+ }
22
+
23
+ .method-post {
24
+ background: #dbeafe;
25
+ color: #1e40af;
26
+ }
27
+
28
+ .method-put {
29
+ background: #fef3c7;
30
+ color: #92400e;
31
+ }
32
+
33
+ .method-patch {
34
+ background: #ccfbf1;
35
+ color: #134e4a;
36
+ }
37
+
38
+ .method-delete {
39
+ background: #fee2e2;
40
+ color: #991b1b;
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-color-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
+ <div class="row">
69
+ <div class="col-12">
70
+ <div class="docs-body">
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
+
87
+ // Response 200
88
+ { "success": true }</code></pre>
89
+
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
111
+ { "success": true, "message": "Test email sent to alice@example.com" }
112
+
113
+ // Error 400
114
+ { "error": "SMTP is not configured. Save your SMTP settings first." }</code></pre>
115
+
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
121
+ { "css": "body { font-family: sans-serif; }" }</code></pre>
122
+
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
144
+ { "success": true }</code></pre>
145
+
146
+ </div>
147
+ </div>
148
+ </div>