Commit graph

28 commits

Author SHA1 Message Date
42b296ca06 Cache-busting: immutable bare på versjonerte URL-er, streng asset()
Fra code review av ea07f34:
- /theme/* fikk max-age=1 år + immutable også uten ?v=; url("favicon.svg")
  inne i CSS ville da sittet fast etter en temaendring. Nå bare med hash.
- asset() feiler ved bygging på ukjent temafil i stedet for å gi en
  uversjonert URL i stillhet.
- rel() defineres én gang og gjenbrukes av asset(); dødt is_dir-guard
  fjernet; docstring peker på asset() for temafiler.
- Tester: uversjonert forespørsel skal ikke være immutable; hash-format
  sjekkes på nestede sider; ukjent asset gir feil.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 15:59:53 +02:00
ea07f346a9 Cache-busting for temafiler
Malene får asset('theme/style.css') som gir en relativ URL med
innholdshash (?v=…), beregnet ved bygging. Temafiler serveres nå med
max-age=1 år + immutable; URL-en endres når innholdet gjør det, så
besøkende får ny stil umiddelbart etter oppgradering.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 15:49:43 +02:00
5263d3011d Brukersider: /brukere/ og /brukere/<brukernavn>/ med beskrivelse
Alle brukere kan skrive en valgfri beskrivelse (Markdown, uten rå HTML)
under Profil. /brukere/ lister hovedbruker øverst og gjester alfabetisk
etter visningsnavn; brukersiden viser navn, beskrivelse, e-post og
innlegg. Signaturen under innlegg lenker til brukersiden. «brukere» er
reservert. Siden bygges nå også når brukere opprettes/slettes.
Retter også ugyldig pattern-regex for brukernavn i admin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 15:48:15 +02:00
f0fcb875c5 Tagger på innlegg med tagg-sider
Kommaseparert tagg-felt i editoren, lagret som liste i frontmatter
(dedup case-ufølsomt). /tag/<slug>/ lister alle innlegg med taggen,
også gjesters; /tag/ lister alle tagger med antall. Tagger vises som
piller på innlegg og i lister, som <category> i feeden og i sitemap.
«tag» er reservert som slug/brukernavn.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 15:42:36 +02:00
259465342b Visningsnavn og e-post i profil, signatur under innlegg
Alle brukere kan sette visningsnavn og valgfri e-post under Profil i
admin. Innlegg avsluttes med «– Visningsnavn», med e-post som mailto-
lenke hvis satt. Frontmatter lagrer nå brukernavnet (stabilt), og
visningsnavn/e-post slås opp i brukertabellen ved bygging, så en
endring gjelder alle innlegg. Eldre innlegg med visningsnavn i author-
feltet vises uendret. Brukertabellen migreres automatisk (email).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 15:37:27 +02:00
14bb9f97ab Lytt på alle adresser (IPv4 og IPv6) som standard
SOAPBOX_BIND er nå tom som standard = alle adresser, så Caddy på en
annen maskin når appen over Tailscale/MagicDNS uansett om navnet løses
til v4 eller v6. Waitress bandt bare 0.0.0.0 inne i containeren, så
IPv6-tilkoblinger via pasta ble avvist; nå brukes listen="*:8080"
(dual-stack). Verifisert mot maskinens Tailscale-IPv4 og -IPv6.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 15:13:32 +02:00
a0b48bc39a SOAPBOX_BIND: velg adresse containeren eksponeres på (Caddy på annen maskin)
Standard 127.0.0.1 (Caddy lokalt). Kjører Caddy på en annen maskin over
Tailscale, settes SOAPBOX_BIND til denne maskinens Tailscale-IP så appen
bare lytter på tailnettet. Brukes av run.sh og compose.yaml; nytt
Caddyfile-eksempel for oppsettet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 15:09:39 +02:00
7e79084dd9 Test: base-path utledes fra SOAPBOX_SITE_URL
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 15:04:33 +02:00
73d1e46467 Utled SOAPBOX_BASE_PATH fra stien i SOAPBOX_SITE_URL
https://domene.no/blog gir automatisk base-path /blog. SOAPBOX_BASE_PATH
beholdes kun som overstyring (tom verdi) for proxyer som stripper
prefikset. Dokumentasjon og Caddyfile-eksempel oppdatert; README hadde
også utdatert slug-eksempel og beskrivelse av SITE_URL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 15:04:13 +02:00
4faeeeaf81 Forklar SOAPBOX_SITE_URL i .env.example (Open Graph, feed, sitemap)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 14:59:07 +02:00
4d9b6f9d90 run.sh: automatisk backup med rotasjon, og --restart=always
Før hver oppstart eksporteres datavolumet til
SOAPBOX_BACKUP_DIR/soapbox-data-<UTC-tidsstempel>.tar.gz; de
SOAPBOX_BACKUP_KEEP nyeste beholdes (standard 5, 0 = av). Bind-mounts
pakkes med tar, navngitte volum med podman volume export.

--restart unless-stopped byttet til --restart=always i run.sh og
compose.yaml: podman håndterer ikke unless-stopped som docker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 14:41:16 +02:00
2f74de4a32 Sitemap, Open Graph-metadata og --follow-historikk
- sitemap.xml med forside, innlegg (lastmod) og gjesteoversikter.
- Open Graph/Twitter-kort på alle sider; innlegg får article-type,
  utdrag som beskrivelse, første bilde som og:image og canonical.
  Absolutte URL-er via SOAPBOX_SITE_URL.
- Historikk per innlegg kombinerer `git log --follow` på index.md med
  loggen for mappen, så utkast-lagringer fra før slug-omdøpingen vises.
  Diffen begrenses ikke lenger til nåværende sti av samme grunn.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 14:39:29 +02:00
77a8f4451f Logoen i admin lenker til bloggens forside
Merket øverst til venstre går nå til bloggens rot både i admin og på
selve bloggen (der var det allerede relativt til roten). «Innlegg» i
menyen dekker admin-listen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 14:33:39 +02:00
50722bf7c2 Utdrag på forsiden og forenklet favicon
Forsiden viser første avsnitt av hvert innlegg som ren tekst (nytt
Jinja-filter `excerpt`, tilgjengelig for egne temaer). Favicon har
færre og større former med tykkere streker så det holder seg ved 16 px.
Siste to punkter fra UI-gjennomgangen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 14:14:25 +02:00
ae3d6a4c0c UX, tilgjengelighet og design etter gjennomgang
WCAG:
- Mørk modus: --on-accent-token gir mørk tekst på lys aksent (knapper,
  badges, valgt fane, skip-lenke) – var 1.8:1, nå ~11:1.
- Editor-feltet har label og aria-describedby; forhåndsvisningen er
  ikke lenger aria-live (leste opp hele innlegget for hvert tastetrykk).
- Bilder settes inn med alt-plassholder markert for overskriving, som
  eget avsnitt etter gjeldende linje.
- Berøringsmål ≥ 44px (nav, verktøylinje, lenkeknapper, select).
- Faner: roving tabindex + piltaster. Knapper med tekst i stedet for
  emoji, aria-keyshortcuts på fet/kursiv. h1 på forsiden.
- code bryter lange stier (innstillinger rullet horisontalt på mobil).

UX:
- Ulagret-vakt dekker også tittelen, med synlig «Ulagrede endringer».
- 413 ved for stor fil gir lesbar melding; brukernavn beholdes ved
  feil innlogging; full publiserings-URL vises i editoren; historikk-
  lenke i meta-linjen; norsk datoformat i historikken; passord-hint.

Design:
- Header med favicon, større brødtekst/overskrifter, smalere måltekst,
  bedre avstander i innlegg og liste, footer som flex uten løse «·»,
  nøytral sekundærknapp, innrammet faresone, editor-textarea fyller
  panelet, valgt fane henger sammen med panelet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 14:12:18 +02:00
d47558c6c1 Sikkerhet: tett path traversal i post-id og herd innlogging/upload
Funn fra sikkerhetsgjennomgang:
- HØY: post_id ble ikke validert, så en gjest kunne nå andres innlegg
  via gjest/../../posts/<slug> (lese utkast, laste opp/slette bilder,
  publisere eierens utkast). Slug og gjestenavn valideres nå mot samme
  alfabet de lages med, og stien sjekkes etter oppslag.
- Bilder: pikselantall sjekkes før dekoding (maks 40 MP), og
  DecompressionBombError håndteres.
- Innlogging: CSRF-token kreves også på login (login-CSRF), og
  innloggingsbrems etter 5 feil per brukernavn.
- Passordbytte ugyldiggjør alle andre sesjoner (stempel i cookien).
- Content-Security-Policy og X-Frame-Options på alle sider; admin har
  ingen inline script lenger (admin.js).
- Gjestenavn og eier-slugs kan ikke lenger kollidere på /navn/.
- Healthcheck respekterer SOAPBOX_BASE_PATH; login-redirect beholder
  base path; run.sh dropper capabilities og setter minnegrense; uv
  pinnet til 0.11.

Rettet også en reell feil: existing_slugs() listet feil katalog
(Path /  er identitet), så slug-unikhet virket ikke.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 14:07:14 +02:00
d52dea8227 Bygg siden alltid ved oppstart
public/ ble bare bygd hvis mappen manglet, så etter en oppgradering med
endrede maler var siden utdatert til noen lagret et innlegg.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 14:02:30 +02:00
1218c5d650 Unicode i slugs: «Grønt er skjønt» → grønt-er-skjønt
Bokstaver og tall i alle skriftsystemer beholdes, alt annet blir
bindestrek. NFC-normalisering gir samme slug uansett om ø/å kommer
prekomponert eller som base + kombinerende tegn (macOS). URL-er i
feed.xml percent-encodes siden RSS-lesere krever ASCII.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 14:02:07 +02:00
9eb375e339 Egne temaer i innholdsrepoet, temavalg i innstillinger
Temaer slås opp i content/themes/<navn>/ før de innebygde, slik at et
eget tema følger med i git, backup og ved flytting – uten ny container.
Et tema kan være delvis: manglende maler/filer arves fra green (Jinja
ChoiceLoader, static kopieres lagvis). Valget lagres i site.toml;
SOAPBOX_THEME er bare standardverdi.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 13:45:04 +02:00
8d40a39187 Favicon: såpestykke på en trekasse (SVG)
Lenkes relativt fra temaet (theme/favicon.svg) og fra admin-basen, så
nettleseren slipper å gjette på /favicon.ico.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 13:34:18 +02:00
fa8b980edd Historikk per innlegg i admin, og presisert flytte-avsnitt i README
Redigeringssiden lenker til /admin/history/<id> som lister commits som
har rørt innleggets mappe; klikk på en commit viser diffen fargelagt.
Samme tilgangsregler som editoren (gjester ser bare egne innlegg).

README skiller nå tydelig mellom å kopiere hele data-volumet (ingen nye
brukere) og å klone bare innholdsrepoet (brukere må opprettes på nytt).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 13:21:32 +02:00
6fef73793f Lisensier under AGPL-3.0-or-later og lenk til kildekoden i footeren
AGPL §13 krever at brukere av en nettverkstjeneste kan hente kildekoden;
temaet viser derfor en lenke til repoet på hver side.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 13:13:25 +02:00
5f6fc252a7 Legg til build.sh, run.sh og .env.example
build.sh bygger imaget med build-dato og git-revisjon, run.sh starter
containeren idempotent (podman run --replace) med innstillinger fra .env.
compose.yaml leser nå samme .env. Alle variabler har standardverdier;
.env.example dokumenterer dem.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 12:36:12 +02:00
d6e5243d25 Test: sjekk på attributt (onsubmit=), ikke ordet i kommentar
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 12:19:07 +02:00
4794db76b0 Fjern inline onsubmit med innholdstekst (XSS i JS-attributt)
Jinja escaper for HTML, men nettleseren dekoder attributtverdien før
JS-parseren ser den, så en tittel med ' kunne bryte ut av strengen.
Bekreftelser går nå via data-confirm og én delt handler i base.html.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 12:18:47 +02:00
227faff373 Tett open redirect i login-next
next-parameteren godtok "//evil.com", som nettlesere tolker som en
ekstern URL. Nå godtas bare stier som starter med én skråstrek.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 12:16:44 +02:00
d2e45fb6c9 Containerfile: STOPSIGNAL SIGINT så waitress avslutter rent
podman stop tok 10 s og endte i SIGKILL fordi waitress ignorerer SIGTERM
som PID 1. Med SIGINT stopper containeren på under et halvt sekund.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcEy43fNYpwg6K6oTKakWR
2026-08-26 12:14:10 +02:00
7ab2320be6 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
2026-08-26 12:12:33 +02:00