Soapbox: statisk blogg med mobilvennlig markdown-editor

Flask-app som genererer statiske sider til public/ og serverer dem
sammen med et admin-grensesnitt (/admin). Innhold ligger som markdown
og bilder i et git-repo (data/content), brukere i sqlite.

- Editor med toolbar, live server-side forhåndsvisning, bildeopplasting
  via knapp, dra-og-slipp og lim inn. Bilder nedskaleres og EXIF fjernes.
- Hovedbruker publiserer under /<slug>/, gjester under /<bruker>/<slug>/.
  Slug lages fra tittelen og fryses ved publisering.
- Alle interne lenker er relative, så siden kan flyttes mellom domener og
  sub-paths (SOAPBOX_BASE_PATH) uten rebuild.
- Tema 'green' med CSS-variabler, mørk modus og WCAG-kontrast.
- Containerfile (python:alpine + git), compose.yaml og Caddyfile-eksempel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
This commit is contained in:
Ole-Morten Duesund 2026-08-26 12:12:33 +02:00
commit 7ab2320be6
35 changed files with 2726 additions and 0 deletions

View file

@ -0,0 +1,36 @@
<!doctype html>
<html lang="nb">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Admin{% endblock %} {{ site.title }}</title>
<link rel="stylesheet" href="{{ url_for('admin.static', filename='admin.css') }}">
</head>
<body>
<a class="skip" href="#main">Hopp til innhold</a>
<header class="topbar">
<a class="brand" href="{{ url_for('admin.index') }}">{{ site.title }}</a>
{% if user %}
<nav aria-label="Admin-meny">
<a href="{{ url_for('admin.index') }}">Innlegg</a>
{% if user.is_owner %}
<a href="{{ url_for('admin.users') }}">Brukere</a>
<a href="{{ url_for('admin.settings') }}">Innstillinger</a>
{% endif %}
<a href="{{ url_for('admin.password') }}">Passord</a>
<a href="{{ url_for('public') }}">Se bloggen</a>
<form method="post" action="{{ url_for('admin.logout') }}" class="inline">
<input type="hidden" name="csrf" value="{{ csrf }}">
<button type="submit" class="link">Logg ut ({{ user.username }})</button>
</form>
</nav>
{% endif %}
</header>
<main id="main">
{% with messages = get_flashed_messages(with_categories=true) %}
{% for cat, msg in messages %}<p class="flash {{ cat }}" role="status">{{ msg }}</p>{% endfor %}
{% endwith %}
{% block content %}{% endblock %}
</main>
</body>
</html>