@a83/orbiter-admin 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/editor.html +21 -4
- package/public/entries.html +54 -16
- package/public/settings.html +19 -0
- package/src/routes/entries.js +52 -7
- package/src/routes/meta.js +1 -1
package/package.json
CHANGED
package/public/editor.html
CHANGED
|
@@ -480,10 +480,11 @@
|
|
|
480
480
|
document.getElementById('collection-id-display').textContent = COLLECTION;
|
|
481
481
|
|
|
482
482
|
// Load collection schema + entry
|
|
483
|
-
const [colData, entryData, versionsData, mediaData] = await Promise.all([
|
|
483
|
+
const [colData, entryData, versionsData, activityData, mediaData] = await Promise.all([
|
|
484
484
|
fetch(`/api/collections/${COLLECTION}`,{credentials:'include'}).then(r=>r.ok?r.json():null),
|
|
485
485
|
IS_NEW ? null : fetch(`/api/collections/${COLLECTION}/entries/${SLUG}`,{credentials:'include'}).then(r=>r.ok?r.json():null),
|
|
486
486
|
IS_NEW ? [] : fetch(`/api/collections/${COLLECTION}/entries/${SLUG}/versions`,{credentials:'include'}).then(r=>r.ok?r.json():[]).catch(()=>[]),
|
|
487
|
+
IS_NEW ? [] : fetch(`/api/collections/${COLLECTION}/entries/${SLUG}/activity`,{credentials:'include'}).then(r=>r.ok?r.json():[]).catch(()=>[]),
|
|
487
488
|
fetch('/api/media',{credentials:'include'}).then(r=>r.json()).catch(()=>[]),
|
|
488
489
|
]);
|
|
489
490
|
|
|
@@ -506,14 +507,20 @@
|
|
|
506
507
|
}
|
|
507
508
|
}
|
|
508
509
|
|
|
509
|
-
// Fetch preview URL for this collection
|
|
510
|
-
const previewMeta = await
|
|
510
|
+
// Fetch preview URL + token for this collection
|
|
511
|
+
const [previewMeta, previewTokenMeta] = await Promise.all([
|
|
512
|
+
fetch(`/api/meta/preview_url~${COLLECTION}`,{credentials:'include'}).then(r=>r.json()).catch(()=>null),
|
|
513
|
+
fetch('/api/meta/preview~token',{credentials:'include'}).then(r=>r.json()).catch(()=>null),
|
|
514
|
+
]);
|
|
511
515
|
const previewUrlTemplate = previewMeta?.value || '';
|
|
516
|
+
const previewToken = previewTokenMeta?.value || '';
|
|
512
517
|
function updatePreviewLink() {
|
|
513
518
|
const btn = document.getElementById('btn-preview-link');
|
|
514
519
|
if (!btn || !previewUrlTemplate) return;
|
|
515
520
|
const slug = document.getElementById('slug-input')?.value || currentSlug || SLUG;
|
|
516
|
-
|
|
521
|
+
const base = previewUrlTemplate.replace('{slug}', slug);
|
|
522
|
+
const sep = base.includes('?') ? '&' : '?';
|
|
523
|
+
btn.href = previewToken ? base + sep + 'preview_token=' + previewToken : base;
|
|
517
524
|
btn.style.display = '';
|
|
518
525
|
}
|
|
519
526
|
|
|
@@ -625,6 +632,15 @@
|
|
|
625
632
|
</div>
|
|
626
633
|
`).join('');
|
|
627
634
|
|
|
635
|
+
const actionLabel = { create:'Created', update:'Saved', publish:'Published', unpublish:'Unpublished', delete:'Moved to trash', restore:'Restored' };
|
|
636
|
+
const actHtml = activityData.slice(0,10).map(a=>`
|
|
637
|
+
<div style="display:flex;align-items:baseline;gap:8px;padding:4px 0;border-bottom:1px solid var(--line2);font-size:10px;">
|
|
638
|
+
<span style="color:var(--text);font-weight:500;flex-shrink:0">${actionLabel[a.action]??a.action}</span>
|
|
639
|
+
<span style="color:var(--muted);flex:1;text-align:right">${a.username}</span>
|
|
640
|
+
<span style="color:var(--muted);font-family:var(--mono);flex-shrink:0">${new Date(a.created_at).toLocaleDateString()}</span>
|
|
641
|
+
</div>
|
|
642
|
+
`).join('');
|
|
643
|
+
|
|
628
644
|
const hasSeo = !!(seoFields.titleKey || seoFields.descKey);
|
|
629
645
|
const serpHtml = hasSeo ? `<div class="serp-preview" id="serp-preview">
|
|
630
646
|
<div class="serp-url" id="serp-url">${location.host}/${COLLECTION}/<span id="serp-slug-part">${escHtml(IS_NEW?'…':SLUG)}</span></div>
|
|
@@ -658,6 +674,7 @@
|
|
|
658
674
|
${updatedAt ? `<div class="meta-field"><div class="field-label">Modified</div><div class="field-readonly">${new Date(updatedAt).toLocaleDateString()}</div></div>` : ''}
|
|
659
675
|
</div>
|
|
660
676
|
${versionsData.length ? `<div class="meta-section"><div class="meta-label">History</div>${versHtml}</div>` : ''}
|
|
677
|
+
${activityData.length ? `<div class="meta-section"><div class="meta-label">Activity</div>${actHtml}</div>` : ''}
|
|
661
678
|
`;
|
|
662
679
|
|
|
663
680
|
// Wire up weekday toggles
|
package/public/entries.html
CHANGED
|
@@ -100,15 +100,18 @@
|
|
|
100
100
|
<button class="filter-tab active" data-status="">All</button>
|
|
101
101
|
<button class="filter-tab" data-status="published">Published</button>
|
|
102
102
|
<button class="filter-tab" data-status="draft">Drafts</button>
|
|
103
|
+
<button class="filter-tab" data-status="trash" style="margin-left:auto">🗑 Trash</button>
|
|
103
104
|
</div>
|
|
104
105
|
|
|
105
106
|
<!-- Bulk action bar -->
|
|
106
107
|
<div class="bulk-bar" id="bulk-bar">
|
|
107
108
|
<span><span class="bulk-bar-count" id="bulk-count">0</span> selected</span>
|
|
108
|
-
<button class="btn btn-ghost btn-sm" id="bulk-publish">Publish</button>
|
|
109
|
-
<button class="btn btn-ghost btn-sm" id="bulk-draft">Unpublish</button>
|
|
109
|
+
<button class="btn btn-ghost btn-sm bulk-normal" id="bulk-publish">Publish</button>
|
|
110
|
+
<button class="btn btn-ghost btn-sm bulk-normal" id="bulk-draft">Unpublish</button>
|
|
111
|
+
<button class="btn btn-ghost btn-sm bulk-trash" id="bulk-restore" style="display:none">Restore</button>
|
|
110
112
|
<span class="bulk-bar-spacer"></span>
|
|
111
|
-
<button class="btn btn-danger btn-sm" id="bulk-delete">
|
|
113
|
+
<button class="btn btn-danger btn-sm bulk-normal" id="bulk-delete">Move to Trash</button>
|
|
114
|
+
<button class="btn btn-danger btn-sm bulk-trash" id="bulk-permanent" style="display:none">Delete forever</button>
|
|
112
115
|
</div>
|
|
113
116
|
|
|
114
117
|
<div class="table-wrap" id="entries-wrap">
|
|
@@ -140,7 +143,10 @@
|
|
|
140
143
|
let selected = new Set();
|
|
141
144
|
|
|
142
145
|
function updateBulkBar() {
|
|
143
|
-
const bar
|
|
146
|
+
const bar = document.getElementById('bulk-bar');
|
|
147
|
+
const trash = activeFilter === 'trash';
|
|
148
|
+
document.querySelectorAll('.bulk-normal').forEach(el => el.style.display = trash ? 'none' : '');
|
|
149
|
+
document.querySelectorAll('.bulk-trash').forEach(el => el.style.display = trash ? '' : 'none');
|
|
144
150
|
if (selected.size > 0) {
|
|
145
151
|
bar.classList.add('visible');
|
|
146
152
|
document.getElementById('bulk-count').textContent = selected.size;
|
|
@@ -165,6 +171,7 @@
|
|
|
165
171
|
wrap.innerHTML = '<div class="empty"><div class="empty-icon">◈</div>No entries yet</div>';
|
|
166
172
|
return;
|
|
167
173
|
}
|
|
174
|
+
const isTrash = activeFilter === 'trash';
|
|
168
175
|
const canSort = !activeFilter;
|
|
169
176
|
wrap.innerHTML = `
|
|
170
177
|
<table>
|
|
@@ -172,13 +179,14 @@
|
|
|
172
179
|
<tr>
|
|
173
180
|
${canSort ? '<th class="drag-col"></th>' : ''}
|
|
174
181
|
<th class="cb-col"><input type="checkbox" id="check-all" title="Select all" /></th>
|
|
175
|
-
<th>Title / Slug</th><th
|
|
182
|
+
<th>Title / Slug</th><th>${isTrash ? 'Deleted' : 'Status'}</th><th>${isTrash ? '' : 'Updated'}</th><th></th>
|
|
176
183
|
</tr>
|
|
177
184
|
</thead>
|
|
178
185
|
<tbody>
|
|
179
186
|
${entries.map(e => {
|
|
180
187
|
const title = e.data?.title || e.slug;
|
|
181
188
|
const updated = e.updated_at ? e.updated_at.split(' ')[0] : '—';
|
|
189
|
+
const deleted = e.deleted_at ? e.deleted_at.split(' ')[0] : '—';
|
|
182
190
|
const nextStatus = e.status === 'published' ? 'draft' : 'published';
|
|
183
191
|
const toggleLabel = e.status === 'published' ? 'Unpublish' : 'Publish';
|
|
184
192
|
return `<tr data-slug="${e.slug}"${canSort ? ' draggable="true"' : ''}>
|
|
@@ -188,14 +196,18 @@
|
|
|
188
196
|
<div style="color:var(--heading);font-weight:500">${title}</div>
|
|
189
197
|
<div style="font-family:var(--mono);font-size:10px;color:var(--muted);margin-top:2px">${e.slug}</div>
|
|
190
198
|
</td>
|
|
191
|
-
<td
|
|
192
|
-
<td style="font-family:var(--mono);font-size:11px;color:var(--muted)">${updated}</td>
|
|
199
|
+
<td>${isTrash ? `<span style="font-family:var(--mono);font-size:11px;color:var(--muted)">${deleted}</span>` : `<span class="badge badge-${e.status}">${e.status}</span>`}</td>
|
|
200
|
+
<td style="font-family:var(--mono);font-size:11px;color:var(--muted)">${isTrash ? '' : updated}</td>
|
|
193
201
|
<td style="width:1%;white-space:nowrap">
|
|
194
202
|
<div class="row-actions">
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
203
|
+
${isTrash
|
|
204
|
+
? `<button class="btn-row restore-btn" data-slug="${e.slug}">Restore</button>
|
|
205
|
+
<button class="btn-row btn-row-danger perm-del-btn" data-slug="${e.slug}">Delete forever</button>`
|
|
206
|
+
: `<a class="btn-row" href="/editor.html?collection=${colId}&slug=${e.slug}">Edit</a>
|
|
207
|
+
<button class="btn-row btn-row-toggle status-toggle" data-slug="${e.slug}" data-next="${nextStatus}">${toggleLabel}</button>
|
|
208
|
+
<button class="btn-row btn-row-icon dup-btn" data-slug="${e.slug}" title="Duplicate">⧉</button>
|
|
209
|
+
<button class="btn-row btn-row-danger delete-btn" data-slug="${e.slug}">Trash</button>`
|
|
210
|
+
}
|
|
199
211
|
</div>
|
|
200
212
|
</td>
|
|
201
213
|
</tr>`;
|
|
@@ -288,16 +300,36 @@
|
|
|
288
300
|
});
|
|
289
301
|
});
|
|
290
302
|
|
|
291
|
-
// Delete
|
|
303
|
+
// Delete → Trash
|
|
292
304
|
wrap.querySelectorAll('.delete-btn').forEach(btn => {
|
|
293
305
|
btn.addEventListener('click', async () => {
|
|
294
|
-
if (!confirm(`Delete "${btn.dataset.slug}"?`)) return;
|
|
295
306
|
await fetch(`/api/collections/${colId}/entries/${btn.dataset.slug}`, {
|
|
296
307
|
method: 'DELETE', credentials: 'include',
|
|
297
308
|
});
|
|
298
309
|
loadEntries();
|
|
299
310
|
});
|
|
300
311
|
});
|
|
312
|
+
|
|
313
|
+
// Restore from Trash
|
|
314
|
+
wrap.querySelectorAll('.restore-btn').forEach(btn => {
|
|
315
|
+
btn.addEventListener('click', async () => {
|
|
316
|
+
await fetch(`/api/collections/${colId}/entries/${btn.dataset.slug}/restore`, {
|
|
317
|
+
method: 'POST', credentials: 'include',
|
|
318
|
+
});
|
|
319
|
+
loadEntries();
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Permanent delete
|
|
324
|
+
wrap.querySelectorAll('.perm-del-btn').forEach(btn => {
|
|
325
|
+
btn.addEventListener('click', async () => {
|
|
326
|
+
if (!confirm(`Permanently delete "${btn.dataset.slug}"? This cannot be undone.`)) return;
|
|
327
|
+
await fetch(`/api/collections/${colId}/entries/${btn.dataset.slug}/permanent`, {
|
|
328
|
+
method: 'DELETE', credentials: 'include',
|
|
329
|
+
});
|
|
330
|
+
loadEntries();
|
|
331
|
+
});
|
|
332
|
+
});
|
|
301
333
|
}
|
|
302
334
|
|
|
303
335
|
// Filter buttons
|
|
@@ -322,9 +354,15 @@
|
|
|
322
354
|
});
|
|
323
355
|
loadEntries();
|
|
324
356
|
}
|
|
325
|
-
document.getElementById('bulk-publish').addEventListener('click',
|
|
326
|
-
document.getElementById('bulk-draft').addEventListener('click',
|
|
327
|
-
document.getElementById('bulk-delete').addEventListener('click',
|
|
357
|
+
document.getElementById('bulk-publish').addEventListener('click', () => bulkAction('publish'));
|
|
358
|
+
document.getElementById('bulk-draft').addEventListener('click', () => bulkAction('draft'));
|
|
359
|
+
document.getElementById('bulk-delete').addEventListener('click', () => bulkAction('delete'));
|
|
360
|
+
document.getElementById('bulk-restore').addEventListener('click', () => bulkAction('restore'));
|
|
361
|
+
document.getElementById('bulk-permanent').addEventListener('click', async () => {
|
|
362
|
+
const n = selected.size;
|
|
363
|
+
if (!confirm(`Permanently delete ${n} entr${n !== 1 ? 'ies' : 'y'}? This cannot be undone.`)) return;
|
|
364
|
+
await bulkAction('permanent');
|
|
365
|
+
});
|
|
328
366
|
|
|
329
367
|
// New entry modal
|
|
330
368
|
const overlay = document.getElementById('modal-overlay');
|
package/public/settings.html
CHANGED
|
@@ -362,6 +362,13 @@
|
|
|
362
362
|
<div><div class="setting-label">API token</div><div class="setting-desc">Optional bearer token to restrict access</div></div>
|
|
363
363
|
<input class="input" type="password" name="api.token" value="${get('api.token')}" autocomplete="off" placeholder="Leave blank for open access" />
|
|
364
364
|
</div>
|
|
365
|
+
<div class="setting-row">
|
|
366
|
+
<div><div class="setting-label">Preview token</div><div class="setting-desc">Appended to Preview URLs — allows viewing draft entries on your site</div></div>
|
|
367
|
+
<div style="display:flex;gap:8px;align-items:center">
|
|
368
|
+
<input class="input" type="text" id="preview-token-display" value="${get('preview.token')??''}" readonly style="flex:1;font-family:var(--mono);font-size:11px;" placeholder="No token generated" />
|
|
369
|
+
<button type="button" class="btn btn-ghost btn-sm" id="btn-gen-preview-token">Generate</button>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
365
372
|
</div>
|
|
366
373
|
|
|
367
374
|
<div class="settings-group">
|
|
@@ -651,10 +658,22 @@
|
|
|
651
658
|
['media.s3_public_url', fd.get('media.s3_public_url')],
|
|
652
659
|
['api.enabled', fd.get('api.enabled') ? '1' : '0'],
|
|
653
660
|
['api.token', fd.get('api.token')],
|
|
661
|
+
['preview.token', document.getElementById('preview-token-display').value || null],
|
|
654
662
|
]);
|
|
655
663
|
showBanner('site-banner','banner-ok','Settings saved');
|
|
656
664
|
});
|
|
657
665
|
|
|
666
|
+
// Preview token generator
|
|
667
|
+
document.getElementById('btn-gen-preview-token')?.addEventListener('click', async () => {
|
|
668
|
+
const token = Array.from(crypto.getRandomValues(new Uint8Array(24))).map(b=>b.toString(16).padStart(2,'0')).join('');
|
|
669
|
+
document.getElementById('preview-token-display').value = token;
|
|
670
|
+
await fetch('/api/meta/preview.token', {
|
|
671
|
+
method: 'PUT', credentials: 'include',
|
|
672
|
+
headers: { 'Content-Type': 'application/json' },
|
|
673
|
+
body: JSON.stringify({ value: token }),
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
|
|
658
677
|
// GitHub push (conditional: only wired if section rendered)
|
|
659
678
|
const ghPushBtn = document.getElementById('github-push-btn');
|
|
660
679
|
if (ghPushBtn) ghPushBtn.addEventListener('click', async () => {
|
package/src/routes/entries.js
CHANGED
|
@@ -16,11 +16,12 @@ entryRoutes.post('/:collectionId/entries/bulk', async (c) => {
|
|
|
16
16
|
const { collectionId } = c.req.param();
|
|
17
17
|
const { action, slugs } = await c.req.json();
|
|
18
18
|
if (!Array.isArray(slugs) || !slugs.length) return c.json({ error: 'slugs required' }, 400);
|
|
19
|
-
if (!['publish', 'draft', 'delete'].includes(action))
|
|
19
|
+
if (!['publish', 'draft', 'delete', 'restore', 'permanent'].includes(action)) return c.json({ error: 'Invalid action' }, 400);
|
|
20
20
|
const db = openPod(c.get('podPath'));
|
|
21
|
-
if (action === 'delete')
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if (action === 'delete') { slugs.forEach(slug => db.deleteEntry(collectionId, slug)); }
|
|
22
|
+
else if (action === 'restore') { slugs.forEach(slug => db.restoreEntry(collectionId, slug)); }
|
|
23
|
+
else if (action === 'permanent') { slugs.forEach(slug => db.permanentDeleteEntry(collectionId, slug)); }
|
|
24
|
+
else {
|
|
24
25
|
const status = action === 'publish' ? 'published' : 'draft';
|
|
25
26
|
slugs.forEach(slug => {
|
|
26
27
|
const entry = db.getEntry(collectionId, slug);
|
|
@@ -78,6 +79,7 @@ entryRoutes.post('/:collectionId/entries', async (c) => {
|
|
|
78
79
|
|
|
79
80
|
const id = db.createEntry(collectionId, slug, data, status);
|
|
80
81
|
const entry = db.getEntry(collectionId, slug);
|
|
82
|
+
db.logAudit(id, c.get('user')?.username ?? 'unknown', 'create');
|
|
81
83
|
db.close();
|
|
82
84
|
return c.json({ ...entry, id }, 201);
|
|
83
85
|
});
|
|
@@ -91,7 +93,15 @@ entryRoutes.put('/:collectionId/entries/:slug', async (c) => {
|
|
|
91
93
|
const before = db.getEntry(collectionId, slug);
|
|
92
94
|
const ok = db.updateEntry(collectionId, slug, body);
|
|
93
95
|
if (!ok) { db.close(); return c.json({ error: 'Not found' }, 404); }
|
|
94
|
-
const updated
|
|
96
|
+
const updated = db.getEntry(collectionId, body.slug ?? slug);
|
|
97
|
+
const username = c.get('user')?.username ?? 'unknown';
|
|
98
|
+
if (body.status === 'published' && before?.status !== 'published') {
|
|
99
|
+
db.logAudit(updated.id, username, 'publish');
|
|
100
|
+
} else if (body.status === 'draft' && before?.status === 'published') {
|
|
101
|
+
db.logAudit(updated.id, username, 'unpublish');
|
|
102
|
+
} else {
|
|
103
|
+
db.logAudit(updated.id, username, 'update');
|
|
104
|
+
}
|
|
95
105
|
db.close();
|
|
96
106
|
|
|
97
107
|
if (body.status === 'published' && before?.status !== 'published') {
|
|
@@ -100,16 +110,51 @@ entryRoutes.put('/:collectionId/entries/:slug', async (c) => {
|
|
|
100
110
|
return c.json(updated);
|
|
101
111
|
});
|
|
102
112
|
|
|
103
|
-
// DELETE /api/collections/:id/entries/:slug
|
|
113
|
+
// DELETE /api/collections/:id/entries/:slug → soft delete (trash)
|
|
104
114
|
entryRoutes.delete('/:collectionId/entries/:slug', (c) => {
|
|
115
|
+
const { collectionId, slug } = c.req.param();
|
|
116
|
+
const db = openPod(c.get('podPath'));
|
|
117
|
+
const entry = db.getEntry(collectionId, slug);
|
|
118
|
+
if (!entry) { db.close(); return c.json({ error: 'Not found' }, 404); }
|
|
119
|
+
db.deleteEntry(collectionId, slug);
|
|
120
|
+
db.logAudit(entry.id, c.get('user')?.username ?? 'unknown', 'delete');
|
|
121
|
+
db.close();
|
|
122
|
+
return c.json({ ok: true });
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// POST /api/collections/:id/entries/:slug/restore — move from trash back to draft
|
|
126
|
+
entryRoutes.post('/:collectionId/entries/:slug/restore', (c) => {
|
|
127
|
+
const { collectionId, slug } = c.req.param();
|
|
128
|
+
const db = openPod(c.get('podPath'));
|
|
129
|
+
const row = db.db.prepare('SELECT * FROM _entries WHERE collection_id = ? AND slug = ? AND deleted_at IS NOT NULL').get(collectionId, slug);
|
|
130
|
+
const ok = db.restoreEntry(collectionId, slug);
|
|
131
|
+
if (row) db.logAudit(row.id, c.get('user')?.username ?? 'unknown', 'restore');
|
|
132
|
+
db.close();
|
|
133
|
+
if (!ok) return c.json({ error: 'Not found in trash' }, 404);
|
|
134
|
+
return c.json({ ok: true });
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// DELETE /api/collections/:id/entries/:slug/permanent — hard delete from trash
|
|
138
|
+
entryRoutes.delete('/:collectionId/entries/:slug/permanent', (c) => {
|
|
105
139
|
const { collectionId, slug } = c.req.param();
|
|
106
140
|
const db = openPod(c.get('podPath'));
|
|
107
|
-
const ok = db.
|
|
141
|
+
const ok = db.permanentDeleteEntry(collectionId, slug);
|
|
108
142
|
db.close();
|
|
109
143
|
if (!ok) return c.json({ error: 'Not found' }, 404);
|
|
110
144
|
return c.json({ ok: true });
|
|
111
145
|
});
|
|
112
146
|
|
|
147
|
+
// GET /api/collections/:id/entries/:slug/activity
|
|
148
|
+
entryRoutes.get('/:collectionId/entries/:slug/activity', (c) => {
|
|
149
|
+
const { collectionId, slug } = c.req.param();
|
|
150
|
+
const db = openPod(c.get('podPath'));
|
|
151
|
+
const entry = db.db.prepare('SELECT * FROM _entries WHERE collection_id = ? AND slug = ?').get(collectionId, slug);
|
|
152
|
+
if (!entry) { db.close(); return c.json({ error: 'Not found' }, 404); }
|
|
153
|
+
const log = db.getAuditLog(entry.id);
|
|
154
|
+
db.close();
|
|
155
|
+
return c.json(log);
|
|
156
|
+
});
|
|
157
|
+
|
|
113
158
|
// GET /api/collections/:id/entries/:slug/versions
|
|
114
159
|
entryRoutes.get('/:collectionId/entries/:slug/versions', (c) => {
|
|
115
160
|
const { collectionId, slug } = c.req.param();
|
package/src/routes/meta.js
CHANGED
|
@@ -10,7 +10,7 @@ const ALLOWED_KEYS = [
|
|
|
10
10
|
'media.backend', 'media.local_path',
|
|
11
11
|
'media.github_token', 'media.github_repo', 'media.github_branch', 'media.github_dir',
|
|
12
12
|
'media.s3_bucket', 'media.s3_region', 'media.s3_endpoint', 'media.s3_access_key', 'media.s3_secret_key', 'media.s3_public_url',
|
|
13
|
-
'api.enabled', 'api.token',
|
|
13
|
+
'api.enabled', 'api.token', 'preview.token',
|
|
14
14
|
'dashboard.notes', 'dashboard.todos',
|
|
15
15
|
'ui.theme',
|
|
16
16
|
'format_version',
|