Commit graph

5 commits

Author SHA1 Message Date
b186fb4bc5 feat: add edit/delete buttons to list views and inline privacy toggle
Fave cards in the list and profile views now show edit, delete, and
privacy toggle buttons directly — no need to open the detail page first.

- New POST /faves/{id}/privacy route with HTMX privacy toggle partial
- New UpdatePrivacy store method for single-column update
- fave_list.html: edit link, HTMX delete, privacy toggle on every card
- profile.html: edit/delete for owner's own cards
- privacy_toggle.html: new HTMX partial that swaps inline on toggle
- CSS: compact .fave-card-actions styles

The existing handleFaveDelete already returns empty 200 for HTMX
requests, so hx-target="closest article" hx-swap="outerHTML" removes
the card from DOM seamlessly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:17:46 +02:00
1260cfd18f feat: add PWA support with Android share intent
Make Favoritter installable as a Progressive Web App with offline
static asset caching and Web Share Target API for Android.

New files:
- internal/handler/pwa.go: handlers for manifest, service worker,
  and share target
- web/static/sw.js: service worker (cache-first static, network-first
  HTML) with {{BASE_PATH}} placeholder for subpath deployments
- web/static/icons/: placeholder PWA icons (192, 512, 512-maskable)

Key design decisions:
- Share target uses GET (not POST) to avoid CSRF token issues — Android
  apps cannot provide CSRF tokens
- Manifest is generated dynamically to inject BasePath into start_url,
  scope, icon paths, and share_target action
- Service worker served at /sw.js with Cache-Control: no-cache and
  BasePath injected via string replacement
- handleShare extracts URLs from Android's "text" field as fallback
  (many apps put the URL there instead of "url")
- handleFaveNew replaced with handleFaveNewPreFill that reads url,
  description, notes from query params (enables share + bookmarklets)
- SW registration in app.js reads base-path from <meta> tag (CSP-safe)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:06:18 +02:00
485d01ce45 feat: add notes field to favorites and enhance OG meta tags
Add an optional long-form "notes" text field to each favorite for
reviews, thoughts, or extended descriptions. The field is stored in
SQLite via a new migration (002_add_fave_notes.sql) and propagated
through the entire stack:

- Model: Notes field on Fave struct
- Store: All SQL queries (Create, GetByID, Update, list methods,
  scanFaves) updated with notes column
- Web handlers: Read/write notes in create, edit, update forms
- API handlers: Notes in create, update, get, import request/response
- Export: Notes included in both JSON and CSV exports
- Import: Notes parsed from both JSON and CSV imports
- Feed: Notes used as Atom feed item summary when present
- Form template: New textarea between URL and image fields
- Detail template: Display notes, enhanced og:description with
  cascade: notes (truncated) → URL → generic fallback text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:40:08 +02:00
aa5ab6b415 fix: address code review findings for Phase 7-8
Bugs fixed:
- Renderer.Error set WriteHeader before Content-Type, causing
  the header to be silently dropped. Now sets Content-Type first.
- truncate template function operated on bytes, not runes — could
  split multi-byte UTF-8 characters (Norwegian æøå). Now uses
  []rune for correct Unicode handling.

Performance:
- Skip session DB lookup (2 queries) on /static/ and /uploads/
  requests — these never use user context.

UX consistency:
- Replace all http.NotFound and http.Error("Forbidden") in
  handler layer with styled error pages via Renderer.Error.
- Add notFound/forbidden helper methods on Handler.

Deployment fixes:
- Remove false libc6/glibc deps from nfpm.yaml (binary is
  statically linked with CGO_ENABLED=0).
- Add CGO_ENABLED=0 to Makefile build target for consistency.
- Add .dockerignore to exclude .git, dist/, data/ from build
  context.
- Remove phantom 'lint' from Makefile .PHONY.
- Document ProtectSystem=strict constraint in systemd service.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 16:39:10 +02:00
fc1f7259c5 feat: implement Phase 1 (auth) and Phase 2 (faves CRUD) foundation
Go backend with server-rendered HTML/HTMX frontend, SQLite database,
and filesystem image storage. Self-hostable single-binary architecture.

Phase 1 — Authentication & project foundation:
- Argon2id password hashing with timing-attack prevention
- Session management with cookie-based auth and periodic cleanup
- Login, signup (open/requests/closed modes), logout, forced password reset
- CSRF double-submit cookie pattern with HTMX auto-inclusion
- Proxy-aware real IP extraction (WireGuard/Tailscale support)
- Configurable base path for subdomain and subpath deployment
- Rate limiting on auth endpoints with background cleanup
- Security headers (CSP, X-Frame-Options, Referrer-Policy)
- Structured logging with slog, graceful shutdown
- Pico CSS + HTMX vendored and embedded via go:embed

Phase 2 — Faves CRUD with tags and images:
- Full CRUD for favorites with ownership checks
- Image upload with EXIF stripping, resize to 1920px, UUID filenames
- Tag system with HTMX autocomplete (prefix search, popularity-sorted)
- Privacy controls (public/private per fave, user-configurable default)
- Tag browsing, pagination, batch tag loading (avoids N+1)
- OpenGraph meta tags on public fave detail pages

Includes code quality pass: extracted shared helpers, fixed signup
request persistence bug, plugged rate limiter memory leak, removed
dead code, and logged previously-swallowed errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 15:55:22 +02:00