Commit Graph

102 Commits

Author SHA1 Message Date
Falkan
5480bb246c refactor: move sessionSecret+passwordHash to env vars, seed data/ from defaults/
- SESSION_SECRET and PASSWORD_HASH moved out of config.json into env vars
- data.ts: AppConfig no longer holds secrets; loadConfig/loadData seed from
  defaults/ on first run if data/ files are missing
- auth.ts: requireAuth/authRequest read SESSION_SECRET from process.env directly
- login/+server.ts: reads PASSWORD_HASH and SESSION_SECRET from process.env
- defaults/config.json: ships with image (no secrets)
- defaults/units.json: ships with image as initial unit data
- package.json: add dotenv dep; start/serve load .env via -r dotenv/config
- Dockerfile: copy defaults/ into image; data/ is PVC-only
- .env.example: documents required env vars for local dev
- Remove k8s/ — managed externally
2026-03-19 23:54:37 -04:00
Falkan
5b89585505 feat: first-run config.json auto-generation on blank PVC
- loadConfig(): if config.json missing, create data/ dir, write default config
  with random sessionSecret and empty passwordHash, log instructions
- loadData(): throw clear error if units.json missing instead of crashing opaquely
- deployment.yaml: remove envFrom secretRef (SESSION_SECRET/PASSWORD_HASH not
  read from env — config.json on PVC is the source of truth)
2026-03-19 23:40:20 -04:00
Falkan
2c5d267d84 chore: clean up build scripts — simple docker build+push to registry.nerdrage.cloud
- Remove build-local.sh and old build-push.sh (crictl/scp/buildah workflow)
- New build-push.sh: docker build + push to registry.nerdrage.cloud/dickloads
- deployment.yaml: update image to registry.nerdrage.cloud/dickloads:latest, imagePullPolicy Always
2026-03-19 23:00:00 -04:00
Falkan
337b722cf2 fix: multi-domain CSRF allowlist, trustProxy for nginx-ingress TLS termination
- hooks.server.ts: replace empty stub with multi-origin CSRF guard
  - Always allows humorunits.com and dickloads.com (+ www variants)
  - ALLOWED_ORIGINS env var for additional origins (staging, preview)
  - Dev: localhost:3000/4173/5173 auto-allowed when NODE_ENV != production
    or ALLOWED_ORIGINS is unset — no config needed for local dev
- svelte.config.js: disable built-in single-origin CSRF check (we own it now)
  trustProxy: true so X-Forwarded-Proto/IP are correct behind nginx-ingress
- login route: derive secure-cookie flag from X-Forwarded-Proto header so
  session cookies are marked Secure even when Node sees plain HTTP from ingress
- deployment.yaml: add NODE_ENV=production and explicit ALLOWED_ORIGINS
2026-03-19 13:18:21 -04:00
Falkan
8d2b838d84 chore: add build-local.sh for manual node import via crictl 2026-03-19 02:09:13 -04:00
Falkan
1e7331d4fc fix: use crictl image import instead of ctr for proper CRI registration 2026-03-19 02:02:43 -04:00
Falkan
e70949009f fix: imagePullPolicy IfNotPresent instead of Never 2026-03-19 01:58:48 -04:00
Falkan
c588949411 fix: tag image as localhost/humor-units to avoid docker.io expansion 2026-03-19 01:55:39 -04:00
Falkan
112336397b fix: qualify image as localhost/humor-units:latest for kubelet 2026-03-19 01:54:47 -04:00
Falkan
cffaaea26d fix: use OCI archive workflow instead of registry — scp + ctr import on each node 2026-03-19 01:33:47 -04:00
Falkan
8e739f4077 fix: use NodePort for registry — resolvable from node host network 2026-03-19 01:27:16 -04:00
Falkan
5901ba92e8 fix: --ignore-scripts in production stage avoids svelte-kit not found 2026-03-19 01:17:07 -04:00
Falkan
e5ff5a90c5 fix: build-push.sh — push via port-forward, tag with in-cluster registry address 2026-03-19 01:08:02 -04:00
Falkan
177b15920f chore: in-cluster registry manifest, build-push.sh script 2026-03-19 01:00:54 -04:00
Falkan
e6a023e14a chore: add Dockerfile and k8s deployment manifests 2026-03-19 00:42:31 -04:00
Falkan
cd0482bade feat: tooltip viewport clamping — shifts minimally to keep bounding box in view 2026-03-19 00:27:01 -04:00
Falkan
d1500c6ac3 fix: tooltip tracks current mouse position, opens at cursor 2026-03-19 00:24:20 -04:00
Falkan
7dec5272b1 revert: tooltip positioning back to 52fc0b8 (pre-viewport-clamping) 2026-03-19 00:20:37 -04:00
Falkan
15b5871eb9 fix: tooltip positioning without rAF — measure synchronously via ready class 2026-03-19 00:16:49 -04:00
Falkan
65f760a254 fix: tooltip positioning — measure after rAF, shift up minimally to fit 2026-03-19 00:05:09 -04:00
Falkan
c13db8b826 fix: tooltip flips above cursor when it would clip viewport bottom 2026-03-18 23:58:40 -04:00
Falkan
52fc0b826f fix: tooltip respects newlines via white-space: pre-wrap 2026-03-18 23:49:24 -04:00
Falkan
248df8d552 chore: add auto-generated ID backlog item 2026-03-18 23:36:54 -04:00
Falkan
8bdde05ca7 chore: add BACKLOG.md 2026-03-18 23:07:05 -04:00
Falkan
1dd2f0c5d3 feat: optgroup dividers in unit dropdown, matching group order and visibility 2026-03-18 23:00:07 -04:00
Falkan
a44551d4bc fix: settings button size in admin header 2026-03-18 22:44:45 -04:00
Falkan
cd34fd27a1 fix: uniform sizing for admin header buttons 2026-03-18 22:40:48 -04:00
Falkan
61d1d49f4b feat: sort-alpha button per group, settings in header, sortPosition pin
- ⇅ button on each group header: sorts units alpha (labelPlural + symbol tiebreak) via order API
- ⚙ Settings moved to admin-header alongside Log out
- Unit type gets sortPosition?: number (1-based, optional)
- Converter sort: partitions pinned/floating, inserts pins at requested slots
  Pins > length append at end; gaps fill naturally with floating items
- Admin unit form: Force position number input
2026-03-18 22:16:44 -04:00
Falkan
8ec935d238 fix: alpha sort by labelPlural primary, symbol secondary
Handles ties like 'Barrel (US fluid)' vs 'Barrel (US dry)'
2026-03-18 21:13:17 -04:00
Falkan
1c6dc7984a chore: add build:watch and serve scripts, install nodemon 2026-03-18 20:52:21 -04:00
Falkan
1308b1f96f feat: editable unit/group IDs with full cascade
- Unit ID rename: updates group.baseUnitId refs + {old-id:*} in all descriptions
- Group ID rename: updates unit.group refs + {old-id:*} in all descriptions
- Admin form ID fields unlocked; hint text updated
- renameIdInDescription() shared helper in both PUT handlers
2026-03-18 20:46:36 -04:00
Falkan
7d79284fd7 feat: start.sh for production server, hooks.server.ts stub, svelte.config cleanup
- start.sh: builds and launches Node production server on 192.168.0.94:4078
- Multi-domain CSRF: not needed — SvelteKit natively passes when Origin matches Host
- hooks.server.ts: minimal stub with explanation
- svelte.config.js: reverted csrf experiment (not supported in this version)
2026-03-18 20:30:34 -04:00
Falkan
8dbc625c68 fix: in-flight guards on loadData/loadConfig, remove duplicate loadConfig
Concurrent calls from rapid reloads or rapid saves could pile up,
each writing state on completion and potentially re-triggering more loads.
Guards ensure only one load is in flight at a time; extra calls are dropped.
2026-03-18 20:21:15 -04:00
Falkan
4c2fe9b3ad fix: replace $effect with onMount for initial data load
$effect re-runs on any reactive state change — loadData/loadConfig both
write state, creating an infinite loop that hung the page under load.
2026-03-18 20:18:30 -04:00
Falkan
b020b74c31 feat: yoloVisibility config, alwaysShowLabel on groups, site settings panel
- AppConfig gains yoloVisibility: 'auto' | 'never' (default: auto)
- Group gains alwaysShowLabel?: boolean
- YOLO toggle only renders when showYoloToggle is true (auto + >1 group)
- Group divider shows when >1 group OR alwaysShowLabel is set
- New /admin/api/config GET+PUT endpoint for yolo settings
- Admin: alwaysShowLabel checkbox in group form
- Admin: ⚙ Settings panel with YOLO label, description, and visibility controls
2026-03-18 20:15:45 -04:00
Falkan
2c27edc487 fix: cycle references resolve to Label (symbol) instead of empty
When a {id:description} reference forms a cycle, the back-reference
now expands to 'Label (symbol)' rather than being stripped.
So pound ↔ dickload renders as 'See dickload (dl)' and 'See pound (lb)'.
2026-03-18 19:59:33 -04:00
Falkan
d332cc135e fix: description placeholder sentinel leaking to output
Two bugs:
1. Sentinel used double-underscore delimiters (__DESC:id__) — fragile regex
   [^_]+ would break on IDs containing underscores, and Pass-1 output
   containing sentinels would leak through Pass 2 unstripped.
2. After Pass 2 substitution, sentinels inside substituted text (from
   Pass-1 output of the referenced id) were never cleaned up.

Fix: switch to null-byte delimiters (\x00DESC:id\x00) which cannot appear
in user text, and add a second .replace(SENTINEL_RE, '') pass to strip any
sentinels that survive after substitution (self-refs, nested cycles).
2026-03-18 19:51:01 -04:00
Falkan
1204ede140 feat: Enter to save, Escape to discard, auto-save on unit/group selection
- selectUnit/selectGroup now async; call autoSaveCurrentForm before switching
- Enter in any form field (except textarea) triggers save
- Escape restores form to snapshot (discards unsaved changes)
- discardUnitForm/discardGroupForm helpers for snapshot restore
2026-03-18 19:42:56 -04:00
Falkan
554b5ba079 fix: desc-indicator contrast — primary color at 75% opacity, magenta in Dan Mode 2026-03-18 19:35:18 -04:00
Falkan
6a9ecf9d46 fix: custom tooltip (no wiggle required), add ⓘ indicator for described units
- Replace native title-based tooltip with custom positioned div
  Native title only renders on mousemove; custom div fires on mouseenter
  after 500ms delay as intended
- Tooltip styled dark with fade-in; Dan Mode gets yellow-on-black with magenta glow
- Units with descriptions show a small superscript ⓘ on the result label
2026-03-18 19:33:14 -04:00
Falkan
6e38522a89 feat: domain-based branding (humorunits.com / dickloads.com)
- src/lib/brand.ts: Brand config map, getBrand() reads hostname server-side
- src/routes/+layout.server.ts: new, passes brand to layout
- +layout.svelte: site name and footer driven by brand config
- humorunits.com footer: 'invented conversions for ridiculous people'
- dickloads.com footer: placeholder (pending Shannon's pick)
2026-03-18 19:27:49 -04:00
Falkan
b1a5586c25 fix: checkbox alignment, wording, and Dan Mode drag handle contrast 2026-03-18 19:13:48 -04:00
Falkan
d088e61006 feat: two-pass description placeholder resolver with cycle handling
Pass 1 resolves all field tokens ({id}, {id:label}, {id:plural}, {id:symbol})
and replaces {id:description} tokens with sentinels.
Pass 2 replaces sentinels with Pass 1 output for the referenced id — one level
deep, so mutual references expand once then stop cleanly.

ConverterCard now uses resolveAllDescriptions() for efficiency (one map
build per reactive update instead of per-unit).
2026-03-18 17:37:00 -04:00
Falkan
998cda0bb3 feat: hidden flag, description placeholders, drag handle contrast fix
- Unit and Group get hidden?: boolean — filtered server-side before render
- Hidden group also suppresses all its units from the converter
- New resolveDescription() utility in src/lib/description.ts
  supports {id}, {id:label}, {id:plural}, {id:symbol} placeholders
- ConverterCard pre-resolves descriptions and passes result to ConversionResult
- ConversionResult accepts description prop instead of reading unit.description
- Admin forms: Hidden checkbox added for both units and groups
- drag-handle color overridden to --pico-primary-inverse on selected/drag-over rows
  (white in light/dark themes, black on Dan Mode yellow)
2026-03-18 16:53:34 -04:00
Falkan
9d56d2ed46 Revert "fix: replace braille glyph drag handle with SVG grip icon"
This reverts commit 21f1f6baaa.
2026-03-18 16:34:48 -04:00
Falkan
21f1f6baaa fix: replace braille glyph drag handle with SVG grip icon 2026-03-18 16:26:39 -04:00
Falkan
5ed392a2d4 fix: template literal quotes in fetch calls; a11y roles on drag elements 2026-03-17 20:52:44 -04:00
Falkan
3598a2dd25 fix: all admin client-side fetches and redirects use configured adminPath 2026-03-17 20:49:38 -04:00
Falkan
e655204064 fix: block direct /admin access when custom adminPath is configured 2026-03-17 20:45:07 -04:00
Falkan
c044f98aa6 feat: adminPath from config.json rewrites route at startup via reroute hook 2026-03-17 20:40:38 -04:00