@a83/orbiter-admin 0.3.34 → 0.3.36
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/style.css +11 -3
- package/public/xfce.js +237 -19
- package/src/routes/terminal.js +42 -0
- package/src/server.js +2 -0
package/package.json
CHANGED
package/public/style.css
CHANGED
|
@@ -2102,9 +2102,17 @@ a.xfce-sb-logo:hover { opacity: .8; }
|
|
|
2102
2102
|
.xfce-pal-item:hover, .xfce-pal-item.pal-active {
|
|
2103
2103
|
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
|
2104
2104
|
}
|
|
2105
|
-
.xfce-pal-icon
|
|
2106
|
-
.xfce-pal-label
|
|
2107
|
-
.xfce-pal-
|
|
2105
|
+
.xfce-pal-icon { width: 20px; text-align: center; font-size: 13px; color: var(--accent); flex-shrink: 0; }
|
|
2106
|
+
.xfce-pal-label { font-size: 12px; color: var(--heading); font-family: var(--mono); flex: 1; }
|
|
2107
|
+
.xfce-pal-hint-r { font-size: 10px; color: var(--muted); font-family: var(--mono); white-space: nowrap; }
|
|
2108
|
+
.xfce-pal-empty { padding: 20px 14px; color: var(--muted); font-size: 11px; font-family: var(--mono); }
|
|
2109
|
+
.xfce-pal-output { padding: 8px 14px; font-size: 11px; font-family: var(--mono); color: var(--body); line-height: 1.6; }
|
|
2110
|
+
.xfce-pal-output code { color: var(--accent); }
|
|
2111
|
+
.xfce-pal-dim { color: var(--muted); }
|
|
2112
|
+
.xfce-pal-ok { color: var(--success, #5dc97e); }
|
|
2113
|
+
.xfce-pal-err { color: var(--danger, #e06c6c); }
|
|
2114
|
+
.xfce-pal-muted { color: var(--muted); opacity: .7; }
|
|
2115
|
+
.xfce-pal-cmd-hint { color: var(--accent); }
|
|
2108
2116
|
|
|
2109
2117
|
/* ── Toast host (above dock) ─────────────────────────────── */
|
|
2110
2118
|
.xfce-toast-host {
|
package/public/xfce.js
CHANGED
|
@@ -28,10 +28,11 @@
|
|
|
28
28
|
{ icon: '☑', label: 'To-do', pane: 'todos' },
|
|
29
29
|
];
|
|
30
30
|
|
|
31
|
-
// palette items
|
|
31
|
+
// palette items — nav + tools pre-seeded; collections appended after /api/info
|
|
32
32
|
var _palItems = NAV.concat(TOOLS).map(function (n) {
|
|
33
33
|
return { icon: n.icon, label: n.label, href: n.href, group: n.key in { schema:1, build:1, import:1 } ? 'Tools' : 'Nav' };
|
|
34
34
|
});
|
|
35
|
+
var _termCols = []; // collection metadata for palette commands
|
|
35
36
|
|
|
36
37
|
// ── Helpers ───────────────────────────────────────────────────────────
|
|
37
38
|
function el(tag, cls, html) {
|
|
@@ -215,6 +216,12 @@
|
|
|
215
216
|
// ── Command Palette ───────────────────────────────────────────────────
|
|
216
217
|
var palette, paletteInp, paletteResults, palActive = -1;
|
|
217
218
|
|
|
219
|
+
var NAV_DEST = {
|
|
220
|
+
dashboard: '/dashboard.html', media: '/media.html', settings: '/settings.html',
|
|
221
|
+
users: '/users.html', schema: '/schema.html', build: '/build.html',
|
|
222
|
+
import: '/import.html', account: '/account.html',
|
|
223
|
+
};
|
|
224
|
+
|
|
218
225
|
function buildPalette() {
|
|
219
226
|
palette = el('div', 'xfce-palette');
|
|
220
227
|
palette.id = 'xfce-palette';
|
|
@@ -222,7 +229,7 @@
|
|
|
222
229
|
'<div class="xfce-palette-inner">',
|
|
223
230
|
'<div class="xfce-palette-bar">',
|
|
224
231
|
'<span class="xfce-palette-cmd">⌘</span>',
|
|
225
|
-
'<input id="xfce-palette-inp" class="xfce-palette-inp" placeholder="Go to page or collection…" autocomplete="off" spellcheck="false" />',
|
|
232
|
+
'<input id="xfce-palette-inp" class="xfce-palette-inp" placeholder="Go to page or collection… › type > for commands" autocomplete="off" spellcheck="false" />',
|
|
226
233
|
'<span class="xfce-palette-hint">ESC to close</span>',
|
|
227
234
|
'</div>',
|
|
228
235
|
'<div id="xfce-palette-results" class="xfce-palette-results"></div>',
|
|
@@ -239,22 +246,29 @@
|
|
|
239
246
|
});
|
|
240
247
|
|
|
241
248
|
paletteInp.addEventListener('keydown', function (e) {
|
|
242
|
-
var
|
|
243
|
-
if (e.key === '
|
|
249
|
+
var val = paletteInp.value.trim();
|
|
250
|
+
if (e.key === 'Enter') {
|
|
251
|
+
if (val.startsWith('>')) {
|
|
252
|
+
e.preventDefault();
|
|
253
|
+
execPaletteCmd(val.slice(1).trim());
|
|
254
|
+
} else {
|
|
255
|
+
var active = paletteResults.querySelector('.xfce-pal-item.pal-active');
|
|
256
|
+
if (active && active.dataset.href) { location.href = active.dataset.href; closePalette(); }
|
|
257
|
+
else {
|
|
258
|
+
var first = paletteResults.querySelector('.xfce-pal-item[data-href]');
|
|
259
|
+
if (first) { location.href = first.dataset.href; closePalette(); }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
} else if (e.key === 'ArrowDown') {
|
|
244
263
|
e.preventDefault();
|
|
264
|
+
var items = paletteResults.querySelectorAll('.xfce-pal-item[data-href]');
|
|
245
265
|
palActive = Math.min(palActive + 1, items.length - 1);
|
|
246
266
|
updatePalActive(items);
|
|
247
267
|
} else if (e.key === 'ArrowUp') {
|
|
248
268
|
e.preventDefault();
|
|
269
|
+
var items = paletteResults.querySelectorAll('.xfce-pal-item[data-href]');
|
|
249
270
|
palActive = Math.max(palActive - 1, 0);
|
|
250
271
|
updatePalActive(items);
|
|
251
|
-
} else if (e.key === 'Enter') {
|
|
252
|
-
var active = paletteResults.querySelector('.xfce-pal-item.pal-active');
|
|
253
|
-
if (active) { location.href = active.dataset.href; closePalette(); }
|
|
254
|
-
else {
|
|
255
|
-
var first = paletteResults.querySelector('.xfce-pal-item');
|
|
256
|
-
if (first) { location.href = first.dataset.href; closePalette(); }
|
|
257
|
-
}
|
|
258
272
|
} else if (e.key === 'Escape') {
|
|
259
273
|
closePalette();
|
|
260
274
|
}
|
|
@@ -273,7 +287,17 @@
|
|
|
273
287
|
}
|
|
274
288
|
|
|
275
289
|
function renderPalette(q) {
|
|
276
|
-
q = (q || '').
|
|
290
|
+
q = (q || '').trim();
|
|
291
|
+
|
|
292
|
+
if (q.startsWith('>')) {
|
|
293
|
+
var cmd = q.slice(1).trim();
|
|
294
|
+
paletteResults.innerHTML = cmd
|
|
295
|
+
? '<div class="xfce-pal-output xfce-pal-cmd-hint">↵ run: ' + escHtml(cmd) + '</div>'
|
|
296
|
+
: '<div class="xfce-pal-output xfce-pal-dim">commands: ls · go <page> · new <col> · search <term> · build · export <col> · info</div>';
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
q = q.toLowerCase();
|
|
277
301
|
var filtered = q
|
|
278
302
|
? _palItems.filter(function (it) { return it.label.toLowerCase().includes(q); })
|
|
279
303
|
: _palItems;
|
|
@@ -283,11 +307,11 @@
|
|
|
283
307
|
return;
|
|
284
308
|
}
|
|
285
309
|
|
|
286
|
-
// Group items
|
|
287
310
|
var groups = {};
|
|
288
311
|
filtered.forEach(function (it) {
|
|
289
|
-
|
|
290
|
-
groups[
|
|
312
|
+
var g = it.group || 'Nav';
|
|
313
|
+
if (!groups[g]) groups[g] = [];
|
|
314
|
+
groups[g].push(it);
|
|
291
315
|
});
|
|
292
316
|
|
|
293
317
|
var html = '';
|
|
@@ -296,25 +320,218 @@
|
|
|
296
320
|
groups[g].forEach(function (it) {
|
|
297
321
|
html += '<div class="xfce-pal-item" data-href="' + it.href + '">'
|
|
298
322
|
+ '<span class="xfce-pal-icon">' + it.icon + '</span>'
|
|
299
|
-
+ '<span class="xfce-pal-label">' + it.label + '</span>'
|
|
323
|
+
+ '<span class="xfce-pal-label">' + escHtml(it.label) + '</span>'
|
|
324
|
+
+ (it.meta ? '<span class="xfce-pal-hint-r">' + escHtml(it.meta) + '</span>' : '')
|
|
300
325
|
+ '</div>';
|
|
301
326
|
});
|
|
302
327
|
});
|
|
303
328
|
paletteResults.innerHTML = html;
|
|
304
329
|
|
|
305
|
-
paletteResults.querySelectorAll('.xfce-pal-item').forEach(function (item) {
|
|
330
|
+
paletteResults.querySelectorAll('.xfce-pal-item[data-href]').forEach(function (item) {
|
|
306
331
|
item.addEventListener('click', function () {
|
|
307
332
|
location.href = item.dataset.href;
|
|
308
333
|
closePalette();
|
|
309
334
|
});
|
|
310
335
|
item.addEventListener('mouseenter', function () {
|
|
311
|
-
var items = paletteResults.querySelectorAll('.xfce-pal-item');
|
|
336
|
+
var items = paletteResults.querySelectorAll('.xfce-pal-item[data-href]');
|
|
337
|
+
palActive = Array.from(items).indexOf(item);
|
|
338
|
+
updatePalActive(items);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function palPrint(html, cls) {
|
|
344
|
+
paletteResults.insertAdjacentHTML('beforeend',
|
|
345
|
+
'<div class="xfce-pal-output' + (cls ? ' xfce-pal-' + cls : '') + '">' + html + '</div>');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function palSetItems(html) {
|
|
349
|
+
paletteResults.innerHTML = html;
|
|
350
|
+
paletteResults.querySelectorAll('.xfce-pal-item[data-href]').forEach(function (item) {
|
|
351
|
+
item.addEventListener('click', function () { location.href = item.dataset.href; closePalette(); });
|
|
352
|
+
item.addEventListener('mouseenter', function () {
|
|
353
|
+
var items = paletteResults.querySelectorAll('.xfce-pal-item[data-href]');
|
|
312
354
|
palActive = Array.from(items).indexOf(item);
|
|
313
355
|
updatePalActive(items);
|
|
314
356
|
});
|
|
315
357
|
});
|
|
316
358
|
}
|
|
317
359
|
|
|
360
|
+
function execPaletteCmd(raw) {
|
|
361
|
+
if (!raw) return;
|
|
362
|
+
var parts = raw.trim().split(/\s+/);
|
|
363
|
+
var cmd = parts[0].toLowerCase();
|
|
364
|
+
var args = parts.slice(1);
|
|
365
|
+
paletteResults.innerHTML = '';
|
|
366
|
+
|
|
367
|
+
switch (cmd) {
|
|
368
|
+
case 'help': palHelp(); break;
|
|
369
|
+
case 'info': palInfo(); break;
|
|
370
|
+
case 'ls': palLs(args); break;
|
|
371
|
+
case 'go': palGo(args); break;
|
|
372
|
+
case 'new': palNew(args); break;
|
|
373
|
+
case 'search': palSearch(args.join(' ')); break;
|
|
374
|
+
case 'build': palBuild(); break;
|
|
375
|
+
case 'export': palExport(args); break;
|
|
376
|
+
default: palPrint('unknown: <b>' + escHtml(cmd) + '</b> — try <b>> help</b>', 'err');
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function palHelp() {
|
|
381
|
+
palPrint([
|
|
382
|
+
'<code>go <page|collection></code> — navigate',
|
|
383
|
+
'<code>new <collection></code> — new entry',
|
|
384
|
+
'<code>ls [collection]</code> — list collections or entries',
|
|
385
|
+
'<code>search <term></code> — full-text search',
|
|
386
|
+
'<code>info</code> — pod & version info',
|
|
387
|
+
'<code>build</code> — trigger deploy',
|
|
388
|
+
'<code>export <col> [--md] [--drafts]</code> — download',
|
|
389
|
+
].join('<br>'), 'dim');
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function palInfo() {
|
|
393
|
+
fetch('/api/info', { credentials: 'include' })
|
|
394
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
395
|
+
.then(function (d) {
|
|
396
|
+
if (!d) { palPrint('error fetching info', 'err'); return; }
|
|
397
|
+
var total = (d.collections || []).reduce(function (s, c) { return s + (c.total || 0); }, 0);
|
|
398
|
+
palPrint([
|
|
399
|
+
'Pod: <b>' + escHtml(d.podPath.split('/').pop()) + '</b>',
|
|
400
|
+
'Format: v' + d.formatVersion + ' · Admin: v' + d.adminVersion,
|
|
401
|
+
'Collections: ' + (d.collections || []).length + ' · Published: ' + total,
|
|
402
|
+
].join('<br>'), 'dim');
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function palLs(args) {
|
|
407
|
+
if (!args.length) {
|
|
408
|
+
if (!_termCols.length) { palPrint('no collections loaded yet', 'muted'); return; }
|
|
409
|
+
var html = _termCols.map(function (col) {
|
|
410
|
+
var href = col.singleton
|
|
411
|
+
? '/editor.html?collection=' + encodeURIComponent(col.id) + '&singleton=1'
|
|
412
|
+
: '/entries.html?col=' + encodeURIComponent(col.id) + '&label=' + encodeURIComponent(col.label);
|
|
413
|
+
var abbr = col.label.substring(0, 2).toUpperCase();
|
|
414
|
+
var meta = col.total + (col.total === 1 ? ' entry' : ' entries') + (col.drafts > 0 ? ', ' + col.drafts + ' draft' + (col.drafts === 1 ? '' : 's') : '');
|
|
415
|
+
return '<div class="xfce-pal-item" data-href="' + href + '">'
|
|
416
|
+
+ '<span class="xfce-pal-icon">' + abbr + '</span>'
|
|
417
|
+
+ '<span class="xfce-pal-label">' + escHtml(col.label) + '</span>'
|
|
418
|
+
+ '<span class="xfce-pal-hint-r">' + meta + '</span>'
|
|
419
|
+
+ '</div>';
|
|
420
|
+
}).join('');
|
|
421
|
+
palSetItems(html);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
var colId = args[0];
|
|
425
|
+
palPrint('loading…', 'muted');
|
|
426
|
+
fetch('/api/collections/' + encodeURIComponent(colId) + '/entries?status=published&limit=20', { credentials: 'include' })
|
|
427
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
428
|
+
.then(function (d) {
|
|
429
|
+
paletteResults.innerHTML = '';
|
|
430
|
+
if (!d) { palPrint('collection not found: ' + escHtml(colId), 'err'); return; }
|
|
431
|
+
var entries = d.entries || (Array.isArray(d) ? d : []);
|
|
432
|
+
if (!entries.length) { palPrint('(no published entries)', 'muted'); return; }
|
|
433
|
+
var html = entries.map(function (e) {
|
|
434
|
+
var href = '/editor.html?collection=' + encodeURIComponent(colId) + '&id=' + encodeURIComponent(e.id || '');
|
|
435
|
+
var date = (e.updated_at || e.created_at || '').substring(0, 10);
|
|
436
|
+
return '<div class="xfce-pal-item" data-href="' + href + '">'
|
|
437
|
+
+ '<span class="xfce-pal-icon">✎</span>'
|
|
438
|
+
+ '<span class="xfce-pal-label">' + escHtml(e.slug || e.id || '') + '</span>'
|
|
439
|
+
+ '<span class="xfce-pal-hint-r">' + date + '</span>'
|
|
440
|
+
+ '</div>';
|
|
441
|
+
}).join('');
|
|
442
|
+
palSetItems(html);
|
|
443
|
+
if (d.total > entries.length) palPrint('… ' + (d.total - entries.length) + ' more', 'muted');
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function palGo(args) {
|
|
448
|
+
if (!args.length) { palPrint('usage: go <page|collection>', 'err'); return; }
|
|
449
|
+
var dest = args[0].toLowerCase();
|
|
450
|
+
if (NAV_DEST[dest]) { closePalette(); setTimeout(function () { location.href = NAV_DEST[dest]; }, 120); return; }
|
|
451
|
+
var col = _termCols.find(function (c) { return c.id === dest || c.label.toLowerCase() === dest; });
|
|
452
|
+
if (col) {
|
|
453
|
+
closePalette();
|
|
454
|
+
var href = col.singleton
|
|
455
|
+
? '/editor.html?collection=' + encodeURIComponent(col.id) + '&singleton=1'
|
|
456
|
+
: '/entries.html?col=' + encodeURIComponent(col.id) + '&label=' + encodeURIComponent(col.label);
|
|
457
|
+
setTimeout(function () { location.href = href; }, 120);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
palPrint('not found: ' + escHtml(dest), 'err');
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function palNew(args) {
|
|
464
|
+
if (!args.length) { palPrint('usage: new <collection>', 'err'); return; }
|
|
465
|
+
var dest = args[0].toLowerCase();
|
|
466
|
+
var col = _termCols.find(function (c) { return c.id === dest || c.label.toLowerCase() === dest; });
|
|
467
|
+
if (!col) { palPrint('collection not found: ' + escHtml(dest), 'err'); return; }
|
|
468
|
+
closePalette();
|
|
469
|
+
setTimeout(function () { location.href = '/editor.html?collection=' + encodeURIComponent(col.id); }, 120);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function palSearch(term) {
|
|
473
|
+
if (!term) { palPrint('usage: search <term>', 'err'); return; }
|
|
474
|
+
palPrint('searching…', 'muted');
|
|
475
|
+
fetch('/api/search?q=' + encodeURIComponent(term), { credentials: 'include' })
|
|
476
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
477
|
+
.then(function (d) {
|
|
478
|
+
paletteResults.innerHTML = '';
|
|
479
|
+
if (!d) { palPrint('search error', 'err'); return; }
|
|
480
|
+
var results = d.results || (Array.isArray(d) ? d : []);
|
|
481
|
+
if (!results.length) { palPrint('no results for “' + escHtml(term) + '”', 'muted'); return; }
|
|
482
|
+
var html = results.slice(0, 15).map(function (r) {
|
|
483
|
+
var href = '/editor.html?collection=' + encodeURIComponent(r.collection || '') + '&id=' + encodeURIComponent(r.id || '');
|
|
484
|
+
return '<div class="xfce-pal-item" data-href="' + href + '">'
|
|
485
|
+
+ '<span class="xfce-pal-icon">⌕</span>'
|
|
486
|
+
+ '<span class="xfce-pal-label">' + escHtml(r.title || r.slug || r.id || '') + '</span>'
|
|
487
|
+
+ '<span class="xfce-pal-hint-r">' + escHtml(r.collection || '') + '</span>'
|
|
488
|
+
+ '</div>';
|
|
489
|
+
}).join('');
|
|
490
|
+
palSetItems(html);
|
|
491
|
+
if (results.length > 15) palPrint('… ' + (results.length - 15) + ' more', 'muted');
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function palBuild() {
|
|
496
|
+
palPrint('triggering build…', 'muted');
|
|
497
|
+
fetch('/api/build', { method: 'POST', credentials: 'include' })
|
|
498
|
+
.then(function (r) { return r.json(); })
|
|
499
|
+
.then(function (d) {
|
|
500
|
+
paletteResults.innerHTML = '';
|
|
501
|
+
if (d.ok || d.message) { palPrint('✓ ' + (d.message || 'build triggered'), 'ok'); setTimeout(closePalette, 900); }
|
|
502
|
+
else palPrint('build error: ' + escHtml(d.error || JSON.stringify(d)), 'err');
|
|
503
|
+
})
|
|
504
|
+
.catch(function () { paletteResults.innerHTML = ''; palPrint('build request failed', 'err'); });
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function palExport(args) {
|
|
508
|
+
if (!args.length) { palPrint('usage: export <collection> [--md] [--drafts]', 'err'); return; }
|
|
509
|
+
var colId = args[0];
|
|
510
|
+
var format = args.indexOf('--md') !== -1 ? 'md' : 'json';
|
|
511
|
+
var drafts = args.indexOf('--drafts') !== -1 ? '1' : '0';
|
|
512
|
+
var col = _termCols.find(function (c) { return c.id === colId; });
|
|
513
|
+
if (!col) { palPrint('collection not found: ' + escHtml(colId), 'err'); return; }
|
|
514
|
+
palPrint('exporting…', 'muted');
|
|
515
|
+
fetch('/api/terminal/export?col=' + encodeURIComponent(colId) + '&format=' + format + '&drafts=' + drafts, { credentials: 'include' })
|
|
516
|
+
.then(function (r) {
|
|
517
|
+
if (!r.ok) return r.json().then(function (e) { throw new Error(e.error || r.status); });
|
|
518
|
+
return r.blob().then(function (blob) {
|
|
519
|
+
var a = document.createElement('a');
|
|
520
|
+
a.href = URL.createObjectURL(blob);
|
|
521
|
+
a.download = colId + '.' + format;
|
|
522
|
+
document.body.appendChild(a); a.click(); a.remove();
|
|
523
|
+
paletteResults.innerHTML = '';
|
|
524
|
+
palPrint('✓ downloaded ' + colId + '.' + format, 'ok');
|
|
525
|
+
setTimeout(closePalette, 900);
|
|
526
|
+
});
|
|
527
|
+
})
|
|
528
|
+
.catch(function (err) { paletteResults.innerHTML = ''; palPrint('export failed: ' + escHtml(err.message), 'err'); });
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function escHtml(s) {
|
|
532
|
+
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
533
|
+
}
|
|
534
|
+
|
|
318
535
|
function openPalette() {
|
|
319
536
|
if (!palette) buildPalette();
|
|
320
537
|
palette.style.display = 'flex';
|
|
@@ -821,8 +1038,9 @@
|
|
|
821
1038
|
|
|
822
1039
|
colGroup.appendChild(item);
|
|
823
1040
|
|
|
824
|
-
// Add to palette
|
|
1041
|
+
// Add to palette fuzzy search and command list
|
|
825
1042
|
_palItems.push({ icon: abbr, label: col.label, href: href, group: 'Collections' });
|
|
1043
|
+
_termCols.push({ id: col.id, label: col.label, total: col.total || 0, drafts: col.drafts || 0, singleton: !!col.singleton });
|
|
826
1044
|
});
|
|
827
1045
|
}
|
|
828
1046
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { openPod } from '@a83/orbiter-core';
|
|
3
|
+
|
|
4
|
+
export const terminalRoutes = new Hono();
|
|
5
|
+
|
|
6
|
+
// GET /api/terminal/export?col=<id>&format=json|md&drafts=0|1
|
|
7
|
+
terminalRoutes.get('/export', (c) => {
|
|
8
|
+
const podPath = c.get('podPath');
|
|
9
|
+
const colId = c.req.query('col');
|
|
10
|
+
const format = c.req.query('format') || 'json';
|
|
11
|
+
const inclDrafts = c.req.query('drafts') === '1';
|
|
12
|
+
|
|
13
|
+
if (!colId) return c.json({ error: 'col is required' }, 400);
|
|
14
|
+
|
|
15
|
+
const db = openPod(podPath);
|
|
16
|
+
const col = db.getCollections().find(col => col.id === colId);
|
|
17
|
+
if (!col) { db.close(); return c.json({ error: 'Collection not found' }, 404); }
|
|
18
|
+
|
|
19
|
+
const entries = inclDrafts
|
|
20
|
+
? db.getEntries(colId)
|
|
21
|
+
: db.getEntries(colId, { status: 'published' });
|
|
22
|
+
db.close();
|
|
23
|
+
|
|
24
|
+
if (format === 'md') {
|
|
25
|
+
const md = entries.map(e =>
|
|
26
|
+
`---\nslug: ${JSON.stringify(e.slug)}\nstatus: ${JSON.stringify(e.status)}\n---\n\n${e.content || ''}`
|
|
27
|
+
).join('\n\n---\n\n');
|
|
28
|
+
return new Response(md, {
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'text/markdown; charset=utf-8',
|
|
31
|
+
'Content-Disposition': `attachment; filename="${colId}.md"`,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return new Response(JSON.stringify({ collection: colId, entries }, null, 2), {
|
|
37
|
+
headers: {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
'Content-Disposition': `attachment; filename="${colId}.json"`,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
});
|
package/src/server.js
CHANGED
|
@@ -24,6 +24,7 @@ import { infoRoutes } from './routes/info.js';
|
|
|
24
24
|
import { importRoutes } from './routes/import.js';
|
|
25
25
|
import { commentRoutes } from './routes/comments.js';
|
|
26
26
|
import { lockRoutes } from './routes/locks.js';
|
|
27
|
+
import { terminalRoutes } from './routes/terminal.js';
|
|
27
28
|
import { requireAuth } from './middleware/auth.js';
|
|
28
29
|
import { csrfMiddleware } from './middleware/csrf.js';
|
|
29
30
|
|
|
@@ -78,6 +79,7 @@ export function createApp(podPath) {
|
|
|
78
79
|
api.route('/collections', commentRoutes);
|
|
79
80
|
api.route('/', commentRoutes);
|
|
80
81
|
api.route('/locks', lockRoutes);
|
|
82
|
+
api.route('/terminal', terminalRoutes);
|
|
81
83
|
|
|
82
84
|
app.route('/api', api);
|
|
83
85
|
|