diff --git a/.gitignore b/.gitignore index 2b3721a..7da7645 100644 --- a/.gitignore +++ b/.gitignore @@ -15,10 +15,9 @@ *.out /vendor/ -# IDE / AI +# IDE .vscode/ .idea/ -.claude/ *.swp *.swo *~ diff --git a/CLAUDE.md b/CLAUDE.md index 494a620..9519306 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,24 +14,13 @@ make test # Run tests make container # Build container image ``` -### CLI Admin Commands - -```bash -favoritter user list # List all users -favoritter user set-password # Change a user's password -favoritter user promote # Promote user to admin -favoritter user demote # Remove admin role -favoritter user lock # Disable user account -favoritter user unlock # Enable user account -``` - ## Architecture Single Go binary serving HTML (server-rendered templates + HTMX) and a JSON API. SQLite for storage, filesystem for uploaded images. All templates and static assets are embedded via `go:embed`. ### Directory Layout -- `cmd/favoritter/` — Entry point, wiring, graceful shutdown, CLI admin commands (`cli_*.go`) +- `cmd/favoritter/` — Entry point, wiring, graceful shutdown - `internal/config/` — Environment variable configuration - `internal/database/` — SQLite connection, PRAGMAs, migration runner - `internal/model/` — Domain types (no logic, no DB) @@ -48,7 +37,7 @@ Single Go binary serving HTML (server-rendered templates + HTMX) and a JSON API. ### Key Design Decisions - **Go 1.22+ stdlib router** — no framework, `http.ServeMux` with method routing -- **Minimal dependencies** — modernc.org/sqlite (pure Go), golang.org/x/crypto (Argon2id), gorilla/feeds, google/uuid, golang.org/x/term +- **3 external dependencies** — modernc.org/sqlite (pure Go), golang.org/x/crypto (Argon2id), gorilla/feeds - **`SetMaxOpenConns(1)`** — SQLite works best with a single writer; PRAGMAs are set once on the single connection - **Templates embedded in binary** — `//go:embed` for single-binary deployment; dev mode reads from disk for live reload - **Middleware chain order matters** — Recovery → SecurityHeaders → BasePath → RealIP → Logger → SessionLoader → CSRF → MustResetGuard diff --git a/Makefile b/Makefile index fb02f84..2c64c94 100644 --- a/Makefile +++ b/Makefile @@ -29,30 +29,24 @@ build-all: $(DIST) ./cmd/favoritter; \ done -## Build .deb packages (requires nfpm and envsubst) +## Build .deb packages (requires nfpm) deb: build-all @for platform in $(PLATFORMS); do \ arch=$${platform##*/}; \ echo "Packaging deb for $$arch..."; \ - ARCH=$$arch VERSION=$(VERSION) envsubst < nfpm.yaml > $(DIST)/nfpm-$$arch.yaml; \ - nfpm package \ - --config $(DIST)/nfpm-$$arch.yaml \ + ARCH=$$arch VERSION=$(VERSION) nfpm package \ --packager deb \ --target $(DIST)/favoritter_$(VERSION)_$${arch}.deb; \ - rm -f $(DIST)/nfpm-$$arch.yaml; \ done -## Build .rpm packages (requires nfpm and envsubst) +## Build .rpm packages (requires nfpm) rpm: build-all @for platform in $(PLATFORMS); do \ arch=$${platform##*/}; \ echo "Packaging rpm for $$arch..."; \ - ARCH=$$arch VERSION=$(VERSION) envsubst < nfpm.yaml > $(DIST)/nfpm-$$arch.yaml; \ - nfpm package \ - --config $(DIST)/nfpm-$$arch.yaml \ + ARCH=$$arch VERSION=$(VERSION) nfpm package \ --packager rpm \ --target $(DIST)/favoritter_$(VERSION)_$${arch}.rpm; \ - rm -f $(DIST)/nfpm-$$arch.yaml; \ done ## Build container image diff --git a/PLANS-v1.1.md b/PLANS-v1.1.md deleted file mode 100644 index 369bd37..0000000 --- a/PLANS-v1.1.md +++ /dev/null @@ -1,316 +0,0 @@ -# Implementeringsplan v1.1 — Favoritter - -Fire funksjoner som utvider Favoritter med notater og OG-metadata, PWA-installering med Android-delingsstøtte, bedre redigeringsflyt, og utvidede admin-verktøy. - -## Oversikt - -| # | Funksjon | Omfang | Avhengigheter | -|---|----------|--------|---------------| -| 1 | OG-støtte + fritekstfelt (notes) | Migrasjon, modell, store, alle handlere, maler, eksport/import, feed | Ingen | -| 2 | PWA + Android share intent | Service worker, manifest, nye handlere, base-mal, JS | Avhenger av 1 (deling pre-fyller notes) | -| 3 | Redigeringsforbedringer (UX) | Maler, CSS, ny toggle-rute, ny partial | Avhenger av 1 (notes vises i kort) | -| 4 | Admin-forbedringer | Store, handlere, maler, ruter | Ingen | - ---- - -## Funksjon 1: OG-støtte + fritekstfelt (notes) - -### Beskrivelse - -Legger til et valgfritt langtekstfelt «notes» på hver favoritt. Forbedrer Open Graph-metatagger på detaljsiden slik at `og:description` bruker notes når det er tilgjengelig. - -### Berørte filer - -| Fil | Endring | -|-----|---------| -| `internal/database/migrations/002_add_fave_notes.sql` | Ny fil: ALTER TABLE faves ADD COLUMN notes | -| `internal/model/fave.go` | Legg til `Notes string` i Fave-struct | -| `internal/store/fave.go` | Oppdater alle SQL-spørringer, Create/Update-signaturer, scanFaves | -| `internal/handler/fave.go` | Les notes fra skjema i create/edit/update | -| `internal/handler/api/api.go` | Legg til notes i request/response-structs og faveJSON | -| `internal/handler/import_export.go` | Legg til notes i ExportFave, CSV-kolonner, import | -| `internal/handler/feed.go` | Bruk notes som feed item description | -| `web/templates/pages/fave_form.html` | Legg til textarea for notes mellom URL og bilde | -| `web/templates/pages/fave_detail.html` | Vis notes, forbedre og:description | - -### Migrasjons-SQL - -```sql --- 002_add_fave_notes.sql --- Legger til et valgfritt notatfelt på favoritter. -ALTER TABLE faves ADD COLUMN notes TEXT NOT NULL DEFAULT ''; -``` - -### Implementeringssteg - -1. **Migrasjon**: Opprett `internal/database/migrations/002_add_fave_notes.sql` -2. **Modell**: Legg til `Notes string` etter `ImagePath` i `model.Fave` -3. **Store — Create**: Endre signatur til `Create(userID, description, url, imagePath, notes, privacy)`, oppdater INSERT -4. **Store — GetByID**: Legg til `f.notes` i SELECT og `&f.Notes` i Scan -5. **Store — Update**: Endre signatur, legg til `notes = ?` i UPDATE -6. **Store — Alle list-metoder**: Legg til `f.notes` i SELECT for `ListByUser`, `ListPublicByUser`, `ListPublic`, `ListByTag` -7. **Store — scanFaves**: Legg til `&f.Notes` i Scan -8. **Handler — fave.go**: Les `notes` fra form i `handleFaveCreate` og `handleFaveUpdate`. Pass `fave.Notes` i `handleFaveEdit` template data -9. **Handler — api/api.go**: Legg til `Notes` i request/response-structs, `faveJSON`, import -10. **Handler — import_export.go**: Legg til notes i `ExportFave`, CSV header/rader, import-parsing -11. **Handler — feed.go**: Sett `item.Description = f.Notes` når det ikke er tomt i `favesToFeedItems` -12. **Template — fave_form.html**: Legg til ` - - diff --git a/web/templates/pages/fave_list.html b/web/templates/pages/fave_list.html index 814bd17..155445f 100644 --- a/web/templates/pages/fave_list.html +++ b/web/templates/pages/fave_list.html @@ -21,6 +21,9 @@ {{.Description}} + {{if eq .Privacy "private"}} + Privat + {{end}} {{if .Tags}}
@@ -29,28 +32,6 @@ {{end}}
{{end}} -
- - - - Rediger - -
{{end}} diff --git a/web/templates/pages/home.html b/web/templates/pages/home.html index 1515c85..9d28f9e 100644 --- a/web/templates/pages/home.html +++ b/web/templates/pages/home.html @@ -1,13 +1,3 @@ -{{define "head"}} - - - -{{if .ExternalURL}} - -{{end}} - -{{end}} - {{define "content"}} {{if .User}}
diff --git a/web/templates/pages/profile.html b/web/templates/pages/profile.html index ca25524..0cf2f4b 100644 --- a/web/templates/pages/profile.html +++ b/web/templates/pages/profile.html @@ -2,11 +2,6 @@ {{with .Data}}{{$d := .}}{{with .ProfileUser}} {{if eq .ProfileVisibility "public"}} - {{if .Bio}} - - {{else}} - - {{end}} {{if $.ExternalURL}} @@ -80,20 +75,6 @@ {{end}} {{end}} - {{if $d.IsOwner}} - - {{end}} {{end}} diff --git a/web/templates/pages/tag_browse.html b/web/templates/pages/tag_browse.html index 84cfaa9..2502082 100644 --- a/web/templates/pages/tag_browse.html +++ b/web/templates/pages/tag_browse.html @@ -1,15 +1,3 @@ -{{define "head"}} -{{with .Data}} - - - -{{if $.ExternalURL}} - -{{end}} - -{{end}} -{{end}} - {{define "content"}} {{with .Data}}
diff --git a/web/templates/partials/privacy_toggle.html b/web/templates/partials/privacy_toggle.html deleted file mode 100644 index 90a0b9f..0000000 --- a/web/templates/partials/privacy_toggle.html +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/web/templates/partials/tag_suggestions.html b/web/templates/partials/tag_suggestions.html index 1b833b6..22911e1 100644 --- a/web/templates/partials/tag_suggestions.html +++ b/web/templates/partials/tag_suggestions.html @@ -2,6 +2,6 @@ id="tag-option-{{$i}}" class="tag-suggestion" aria-selected="false" - data-tag-name="{{$tag.Name}}" + onclick="addTag(this, '{{$tag.Name}}')" tabindex="-1">{{$tag.Name}} {{end}}