feat(import): restore MindSift data and import Obsidian vaults
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m28s

- Two separate paths: POST /import/mindsift (JSON backup or the Markdown
  archive) and POST /import/obsidian (vault ZIP into a folder named after it)
- Everything is planned and validated first; ?preview=1 returns what would
  be created and what is skipped (with reason), nothing is written
- MindSift data round-trips: folders, note structure, task lists with
  subtasks/due dates/priorities/notes, lists with quantity/category/done,
  triage thoughts from the JSON backup
- Markdown to outliner: nested lists, heading sections, callouts, code,
  paragraphs; front matter tags become hashtags; .obsidian/attachments skipped
- Limits against zip bombs and path tricks (100 MB upload, 10 MB per note,
  500 MB unpacked, 20k files, no .. escapes)
- Settings: two import cards with drag & drop, preview, confirm, result;
  touch-sized buttons; mobile download/upload verified

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-29 06:10:21 +01:00
1 parent d8a16c6775
commit 1601a6a2d5
9 files changed
+1314 -1

No files matched your search

+17
View File
@@ -19,6 +19,7 @@ import {
SystemSettings,
SharingMode,
UserTheme,
ImportSummary,
Team,
TaskList,
TaskListShare,
@@ -1076,6 +1077,22 @@ export const api = {
return request<any>('/api/v1/export');
},
// Import of MindSift data (JSON backup or Markdown archive) or an Obsidian vault (ZIP).
// preview = true only reports what would be created.
async importData(source: 'mindsift' | 'obsidian', file: File, preview: boolean): Promise<ImportSummary> {
const token = getToken();
const form = new FormData();
form.append('file', file);
const res = await fetch(`/api/v1/import/${source}${preview ? '?preview=1' : ''}`, {
method: 'POST',
headers: token ? { Authorization: `Bearer ${token}` } : {},
body: form,
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || `Import fehlgeschlagen (HTTP ${res.status})`);
return data as ImportSummary;
},
// Obsidian-compatible ZIP of the workspace; binary and possibly large, so no JSON helper and no timeout
async exportMarkdownVault(): Promise<Blob> {
const token = getToken();