Commit Graph

48 Commits

Author SHA1 Message Date
vikingowl 2a1fb1eece feat(web): admin discovery review queue with accept/reject 2026-04-18 07:54:18 +02:00
vikingowl 79001011df fix(search): support PLZ filter on home page
The home page dropped `plz` server-side, so /markets was called with
radius but no center (unfiltered) and the PLZ input rendered empty
after reload. +page.server.ts now reads plz, geocodes via /geocode,
and echoes plz back in searchParams for form rehydration.

Relaxes /geocode DTO + guard to accept PLZ without city — Nominatim
already supports postal-only lookups. URL lat/lon (GPS flow) take
priority over plz on tie-break; geocode failures fall through to no
geo-filter so the page always renders.
2026-04-18 05:32:28 +02:00
vikingowl df5b0563c9 refactor(web): bundle server with esbuild, slim runtime to alpine+node
Post-process adapter-node output into a single self-contained
build/bundle.mjs (614 KB) via esbuild, then ship it on a fresh
alpine:3.21 base with just the node binary copied in. Drops the
node_modules + package manager baggage that comes with node:25-alpine.

- Add esbuild devDep + `bundle` script (scripts/bundle.mjs)
- Dockerfile: drop `deps` stage; final stage is alpine + node binary +
  bundle + static client assets
- Uncompressed image: 177 MB -> 149 MB (-16%)
- Verified: /, /healthz, static assets all respond identically;
  outbound TLS to api.marktvogt.de works via node's built-in CA bundle
2026-04-18 05:05:07 +02:00
vikingowl f9b77f362f chore(helm): right-size resource requests/limits per cluster telemetry
Drop requests to match observed peak usage and widen CPU limits for
burst headroom (Burstable QoS). Backend, web, Postgres, and Dragonfly
all had requests == limits pinned at defaults well above measured
7-day peaks.

- backend: req 100m/128Mi -> 50m/64Mi, lim 100m/128Mi -> 200m/128Mi
- web:     req 100m/128Mi -> 50m/96Mi, lim 100m/128Mi -> 200m/128Mi
- postgres (CNPG): req 50m/256Mi -> 15m/128Mi, lim 200m/512Mi -> 100m/256Mi
- dragonfly: req 100m/128Mi -> 100m/72Mi, lim 100m/128Mi -> 150m/128Mi

RAM limits unchanged where reasonable to preserve OOM protection;
Dragonfly CPU request kept at 100m (peak 74m) but limit raised to
avoid throttling under brief bursts.
2026-04-18 04:36:12 +02:00
vikingowl 808f4ddda6 chore(deps): bump Kit 2.57.1, Vite 7.3.2, quic-go 0.57.0; override cookie 0.7.2
Resolves 11 Semgrep Supply Chain findings (4 reachable HIGH, 3 unreachable HIGH,
4 moderate/low). Build verified on web (pnpm build) and backend (go build ./...).
2026-04-18 02:53:15 +02:00
vikingowl 7d07d47ec5 chore: convert to GitLab monorepo
- Merge backend, web, app, planning histories (118 commits preserved)
- Replace Woodpecker CI with .gitlab-ci.yml (path-based triggering)
- Switch mistral-go-sdk from somegit.dev to github.com/VikingOwl91/mistral-go-sdk v1.3.0
- Consolidate .pre-commit-config.yaml and .gitignore at repo root
- Remove per-service .woodpecker.yml files
2026-04-07 02:53:03 +02:00
vikingowl 01881e56bc fix(helm): update imagePullSecret to itsh-registry 2026-04-06 20:01:32 +02:00
vikingowl cb996f46bb fix(ci): switch container registry to registry.itsh.dev 2026-04-06 19:48:56 +02:00
vikingowl 2455eda23b fix(ci): install pnpm directly, corepack removed in Node 25 2026-04-01 23:55:56 +02:00
vikingowl ed98563739 refactor: switch package manager from bun to pnpm
- Replace bun with pnpm (v10.33.0) via corepack
- Upgrade all Docker images from node:22/bun to node:25-alpine
- Update CI pipeline, pre-commit hooks, and prettierignore
- Add packageManager field to package.json for version pinning
2026-04-01 23:52:30 +02:00
vikingowl 553ceb5d85 fix(helm): Helm 4 CI, startup probe, guaranteed QoS, config checksum
- Upgrade CI deploy to Helm 4.1 with --rollback-on-failure --wait=watcher
- Replace initialDelaySeconds with startup probe (15x2s=30s window)
- Set resources req=limit (100m/128Mi) for Guaranteed QoS class
- Add ConfigMap checksum annotation to trigger rollouts on config changes
2026-04-01 23:47:20 +02:00
vikingowl 99781b4cf3 fix(ci): update deploy namespace to tenant-2 2026-03-09 16:19:28 +01:00
vikingowl 8f306059d8 fix(helm): lower resource limits to fit within tenant-quota (1 CPU)
Set web limits to 200m/256Mi to stay within the tenant-1
ResourceQuota of 1 CPU total.
2026-03-08 19:29:51 +01:00
vikingowl 62a9f682a0 feat(helm): add HTTPRoute sectionName and HTTP→HTTPS redirect, update resources
Add sectionName to HTTPRoute for HTTPS listener pinning and a separate
HTTP→HTTPS 301 redirect route. Update resources from req=limit to
request/limit separation for pay-as-you-go billing.
2026-03-08 19:04:30 +01:00
vikingowl 612231d977 chore: normalize resources to 100m/100Mi and enable zero-downtime deploys
Set CPU and memory requests equal to limits (100m/100Mi). Switch rolling
update strategy to maxSurge=1, maxUnavailable=0 so new pods start
before old ones terminate.
2026-03-05 18:25:47 +01:00
vikingowl 5ea6afca3e feat: group market editions by series in search and admin list
Public search now deduplicates to one card per series with an edition
count badge. Admin list uses a grouped endpoint with expandable rows.
Market detail page shows an edition year switcher when multiple editions
exist. Admin detail page includes series edition management.
2026-03-05 18:14:47 +01:00
vikingowl 2d32a098cc feat: add search, sorting, and filtering to admin market list
- Search box for name/city filtering
- Clickable column headers for sorting (name, city, status, date, created)
- Sort direction toggle (asc/desc) with arrow indicators
- All filters preserved through pagination and sort changes
2026-03-05 15:22:13 +01:00
vikingowl 6f8df87f80 feat: add AI research, geocoding, enriched market forms and public submit upgrade
- AI research panel with structured display (opening hours, admission)
- Shared MarketForm component with admin/public mode
- Geocode button to derive coordinates from address
- Opening hours, admission info fields in all forms
- Currency-aware pricing, full European country list
- Public submit now includes all market fields
- Weekday normalization from date for AI research results
- Duplicate detection warning on admin detail view
2026-03-05 15:17:59 +01:00
vikingowl 0a59225e81 fix: match 2FA error code and escape TOTP pattern in login form
Backend returns code '2fa_required' but frontend checked for
'totp_required', so the TOTP input field never appeared. Also fix
the same Svelte pattern brace escaping issue as in TOTPSetup.
2026-02-27 14:55:50 +01:00
vikingowl 25c0a265a4 chore: remove OAuth buttons from login page 2026-02-27 14:54:58 +01:00
vikingowl 18ac35c477 fix: escape regex braces in TOTP input pattern attribute
Svelte was interpreting {6} in pattern="[0-9]{6}" as an expression,
rendering the pattern as "[0-9]6" instead of the intended 6-digit regex.
2026-02-27 14:46:50 +01:00
vikingowl dd4e6184ac feat: add user dropdown menu, password management, fix Turnstile keys
- Replace inline nav items with UserMenu dropdown (display name trigger,
  Profil/Sicherheit/Admin/Abmelden, click-outside/Escape to close)
- Add password set/change form to profile security section
- Fix Turnstile site key (extra A, swapped l/1)
2026-02-27 14:39:01 +01:00
vikingowl bb6912d94d fix: add Turnstile site key to runtime ConfigMap
The page uses $env/dynamic/public which reads env vars at runtime,
not build time. The Docker build ARG/ENV only exists in the builder
stage and doesn't propagate to the Node.js runtime container.

Add PUBLIC_TURNSTILE_SITE_KEY to the Helm ConfigMap so it's
available as a process.env var when the SSR server runs.
2026-02-27 14:21:02 +01:00
vikingowl 83264b4b41 fix: enable auth nav, turnstile deployment, country dropdown, profile routes
- Add PUBLIC_TURNSTILE_SITE_KEY as Docker build arg and Woodpecker CI arg
- Uncomment auth nav in Header and MobileNav (login/logout/profile links)
- Move ThemeToggle from header to footer
- Expand country dropdown from DACH-only to all European countries
- Replace profile route redirect with requireAuth guard
- Set cookie secure flag based on environment (secure in prod)
- Add error handling to admin markets page (403 instead of 500)
2026-02-27 14:12:23 +01:00
vikingowl e4e5fbf9f5 fix: correct magic link API endpoint path
Frontend was calling /auth/magic-link/request but the backend
route is POST /auth/magic-link, causing a 404.
2026-02-27 13:40:38 +01:00
vikingowl 325a1121a0 fix: replace ASCII-encoded umlauts with proper German characters in UI text
User-facing strings used ae/oe/ue/ss instead of ä/ö/ü/ß, carried over
from ASCII-only planning docs convention.
2026-02-27 11:12:21 +01:00
vikingowl 2e6acceb33 feat: add admin panel, market submission form, and legal updates
- Admin layout with auth guard at /admin
- Admin market list with status filter tabs (pending/approved/rejected)
- Admin market detail/review with approve/reject actions
- Admin market create and edit forms with shared MarketForm component
- Anonymous market submission form at /markt/einreichen with Turnstile
- Optional latitude/longitude fields on submission form
- Admin and submission types added to API types
- requireAdmin guard for role-based frontend access
- Header/mobile nav updated with admin and submission links
- Auth layout redirect removed to re-enable login flow
- Login form action renamed to fix named actions conflict
- Impressum updated with user-submitted content section
- Datenschutz updated with submission form and Turnstile sections
2026-02-27 11:04:31 +01:00
vikingowl 3447402059 fix: add missing recommended fields to Event structured data
Resolve Google Search Console warnings for Event structured data by
adding image fallback, organizer url, offers url, and offers for free
markets.
2026-02-24 12:56:54 +01:00
vikingowl c582c7c4d6 feat: parse admission notes into structured price table
Parse semi-structured notes text (e.g. "Samstag: Erw. 15 €, Kinder 8 €")
into grouped table rows instead of showing raw text below the table.

- Split notes on sentence boundaries (". " + uppercase), handling
  abbreviations like "Erw." correctly
- Extract "Label: Category Price €" patterns into table groups
- Expand common abbreviations (Erw. → Erwachsene, Erm. → Ermäßigt)
- Guard against "Cat: Price, Cat: Price" format (colons in pricesStr)
- Guard against bare prices after colon (letter-start requirement)
- Graceful fallback to flat table + raw notes when parsing fails
2026-02-23 06:12:39 +01:00
vikingowl 6b23cc330e feat: add Prettier, ESLint, and pre-commit hooks
- Add Prettier with svelte and tailwindcss plugins
- Add ESLint with typescript-eslint and eslint-plugin-svelte
- Add pre-commit hooks (trailing-whitespace, end-of-file-fixer,
  check-yaml, check-json, no-commit-to-branch, prettier, eslint,
  svelte-check)
- Add format, format:check, lint, lint:fix scripts to package.json
- Rename CI typecheck step to lint, add format:check and lint commands
- Fix ESLint issues: unused imports, Leaflet type import, extract
  JSON-LD from {@html} template literals to script-block variables
2026-02-23 05:33:44 +01:00
vikingowl b0d7e6c4aa fix: add lightweight /healthz endpoint, skip SSR for k8s probes
Add /healthz handler in hooks.server.ts that returns early without auth
or SSR processing. Update Helm probes from / to /healthz to avoid
unnecessary log noise and wasted SSR renders.
2026-02-22 20:27:00 +01:00
vikingowl 7282f25986 feat: add SEO landing pages for states and cities
Add /maerkte/ browse-by-state overview, /maerkte/[state]/ state detail
pages, and /maerkte/[state]/[city]/ city detail pages. Each page fetches
all markets and filters client-side (SSR). Includes slug utilities with
umlaut handling, JSON-LD structured data, breadcrumb navigation on all
pages including market detail, and sitemap entries for all new routes.
2026-02-22 20:21:48 +01:00
vikingowl 072c9dc934 feat: add version polling and auto-reload on deploy
- Enable kit.version.pollInterval (60s) so SvelteKit detects new
  deploys via version.json
- Add beforeNavigate guard that forces a full page reload when a
  new version is detected, preventing stale client-side state
- Preload fonts, JS, and CSS for faster initial paint
2026-02-22 19:24:18 +01:00
vikingowl ed52963bf5 fix(ci): set PUBLIC_API_BASE_URL for typecheck step
svelte-kit sync needs the env var to generate correct types for
$env/static/public imports.
2026-02-22 18:38:51 +01:00
vikingowl 6f1709a258 fix: use PUBLIC_API_BASE_URL env var instead of hardcoded localhost
SSR was fetching from http://localhost:8080 in production, causing
the markets endpoint to fail silently and return 0 results.
2026-02-22 18:36:26 +01:00
vikingowl f83d73c06d fix(deploy): set maxSurge=0 to fit within ResourceQuota during rollout
With 900m/1000m CPU limits used, rolling updates fail because the
new pod cannot be created alongside the old one. Setting maxSurge=0
and maxUnavailable=1 kills the old pod first, avoiding quota exhaustion
at the cost of brief downtime during deploys.
2026-02-22 12:00:23 +01:00
vikingowl f83c712b2b fix: remove PageServerLoad annotations from disabled routes
The auth and profile layout redirects break SvelteKit's type
generation for child page loads, causing false-positive type
errors where PageData requires `user` from the root layout.
2026-02-22 11:50:14 +01:00
vikingowl df93f83fcd feat(seo): add sitemap, canonical URLs, structured data, and OG/Twitter tags
- Add dynamic sitemap.xml with static pages and market entries
- Add canonical URLs and base OG/Twitter meta tags in root layout
- Add JSON-LD WebSite schema with SearchAction on home page
- Add JSON-LD Event schema on market detail pages
- Add twitter:card summary_large_image for markets with images
- Add OG tags to impressum and datenschutz pages
- Add font preloading for critical rendering path fonts
- Add Sitemap directive to robots.txt
2026-02-22 11:45:27 +01:00
vikingowl 2c462144a7 chore: temporarily disable login/signup and profile routes
Comment out auth navigation in Header and MobileNav.
Add redirect-to-home layout guards on /auth/* and /profile/* routes.
2026-02-22 11:34:05 +01:00
vikingowl cd29b49b0e feat: add impressum and datenschutz pages
Add legal pages required by German law (TMG §5, DSGVO).
Impressum includes operator identity, contact, and liability notices.
Datenschutz covers all data processing: registration, OAuth, sessions,
cookies, geolocation, map tiles, and user rights.
2026-02-22 11:32:22 +01:00
vikingowl bf1ba11de2 fix(deploy): add seccompProfile RuntimeDefault to satisfy PodSecurity restricted policy 2026-02-22 10:03:33 +01:00
vikingowl e957419b77 fix(docker): use existing nobody user instead of creating UID 65534 2026-02-22 09:54:35 +01:00
vikingowl 2f9f4c4b49 fix(ci): correct registry to somegit.dev 2026-02-22 09:50:49 +01:00
vikingowl 2718321201 feat(deploy): add container build and Helm chart for k8s deployment
- Switch adapter-auto to adapter-node (SSR) for container deployment
- Add multi-stage Dockerfile: Bun build, Node.js 22 runtime, UID 65534
- Add Helm chart (deploy/helm/) with Deployment, Service, HTTPRoute, HPA, PDB, ServiceAccount
- Use HTTPRoute (Gateway API) targeting nginx-gateway, TLS via cert-manager
- Enforce readOnlyRootFilesystem with emptyDir for /tmp
- Set ORIGIN env var for adapter-node CSRF protection
- Add Woodpecker CI: typecheck, docker push (SHA tag), helm upgrade --atomic
2026-02-22 09:32:13 +01:00
vikingowl 76afe71854 feat: add signet ring logo and favicon with fleur-de-lis
Introduces brand identity with a medieval signet ring motif:
gold fleur-de-lis on dark green signet face. Adds SVG favicon,
raster fallbacks (ICO/PNG), apple-touch-icon, web manifest,
and inline signet ring logo in the header next to the wordmark.
2026-02-18 07:44:41 +01:00
vikingowl 2b02168747 feat: medieval aesthetic restyling with dark/light mode
- Add MedievalSharp + Crimson Pro fonts (woff2)
- Implement OKLCH color theme: forest green primary, gold accent,
  warm stone neutrals, parchment/vellum surfaces, brick red danger
- Add dark/light mode with system preference detection and user toggle
  (ThemeToggle component cycling system/light/dark)
- Prevent FOUC with inline script in app.html
- Replace native <select> with custom accessible dropdown (combobox/
  listbox pattern, keyboard nav, type-ahead, app-themed styling)
- Add IP geolocation fallback via geojs.io when native geolocation
  fails, with visual location badge showing resolved city name
- Shared form control styles via @layer base
- WCAG/a11y: skip-to-content link, aria-invalid, aria-describedby,
  aria-live, focus-visible, role="search", color-scheme:dark
- Update all 23 component/page files with new color system and
  dark: variants
2026-02-18 07:19:30 +01:00
vikingowl 1bcab1f1d4 feat: implement SvelteKit web frontend MVP
SvelteKit 2 (Svelte 5 runes) + Tailwind 4 + TypeScript frontend for
the Marktvogt medieval market platform.

- Market search with keyword FTS, location/radius, date range, sorting
- List/map toggle (Leaflet with OSM tiles, dynamic import)
- Market detail pages with SSR, SEO meta/OG tags, opening hours,
  admission prices, embedded map
- Auth: login (password + magic link + OAuth), register, logout
- Profile: view/edit display name + avatar, account deletion
- 2FA: TOTP setup, verify, disable
- httpOnly cookie auth with JWT access + session token refresh
- Responsive layout with mobile hamburger nav
- German UI text for DACH audience
2026-02-18 06:27:51 +01:00
vikingowl 1e2a3d9686 initial commit 2026-02-18 04:49:25 +01:00