Add Obsidian vault import feature (#8)

- New 'Import Obsidian' action on site settings page
- Upload a zip file of an Obsidian vault to import all notes as posts
- Markdown notes imported with title from filename, published date from
  file timestamp, and body with front-matter stripped
- Images and other attachments saved as Upload records
- New obsimport service handles zip traversal and import logic
- Unit tests for front-matter stripping

Co-authored-by: Shelley <shelley@exe.dev>

Co-authored-by: exe.dev user <exedev@kernel-leviathan.exe.xyz>
Reviewed-on: #8
This commit is contained in:
lmika 2026-04-09 11:40:52 +00:00
parent d21aeadd56
commit a3197f9b11
8 changed files with 376 additions and 0 deletions

21
views/obsimport/form.html Normal file
View file

@ -0,0 +1,21 @@
<main class="container">
<div>
<h5 class="my-4">Import from Obsidian</h5>
<p>Select an Obsidian vault exported as a Zip file. All Markdown notes will be imported as posts, and any images or attachments will be imported as uploads.</p>
<form method="post" action="/sites/{{ .site.ID }}/import/obsidian" enctype="multipart/form-data">
<div class="row mb-3">
<label for="zipfile" class="col-sm-3 col-form-label text-end">Zip File</label>
<div class="col-sm-6">
<input type="file" class="form-control" id="zipfile" name="zipfile" accept=".zip">
</div>
</div>
<div class="row mb-3">
<div class="col-sm-3"></div>
<div class="col-sm-9">
<button type="submit" class="btn btn-primary">Import</button>
<a href="/sites/{{ .site.ID }}/settings" class="btn btn-secondary ms-2">Cancel</a>
</div>
</div>
</form>
</div>
</main>

View file

@ -0,0 +1,10 @@
<main class="container">
<div>
<h5 class="my-4">Import Complete</h5>
<div class="alert alert-success">
<p class="mb-1">Successfully imported <strong>{{ .result.PostsImported }}</strong> post(s) and <strong>{{ .result.UploadsImported }}</strong> upload(s).</p>
</div>
<a href="{{ .siteURL }}" class="btn btn-primary">Go to Posts</a>
<a href="/sites/{{ .site.ID }}/settings" class="btn btn-secondary ms-2">Back to Settings</a>
</div>
</main>

View file

@ -66,5 +66,12 @@
</div>
</div>
</form>
<div class="row mb-3">
<div class="col-sm-3"></div>
<div class="col-sm-9">
<a href="/sites/{{ .site.ID }}/import/obsidian" class="btn btn-secondary">Import Obsidian</a>
<span class="form-text mx-3">Import posts and attachments from an Obsidian vault zip file.</span>
</div>
</div>
</div>
</main>