mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
d5850041a75da8cb9448ebcd2d49319f44fb6717
626 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d5850041a7 | fix(costs): rework the cost panel UX wise and apply prettier on the shared package | ||
|
|
ad6e1ddcc8 | fix(airtrail): add back missing tests | ||
|
|
66f661e2a1 | fix(airtrail): gate airtrail update behind a user setting, on airtrail update: rebuild payload from fresh data to prevent any data loss | ||
|
|
7aefeb4c53 |
fix(atlas): give every sub-national region a distinct code (#1217)
geoBoundaries fills shapeISO with the bare country code for some countries (every Spanish region got "ESP", every Chinese "CHN", also Chile/Oman), so marking one region lit up the whole country. build-atlas-geo.mjs now keeps shapeISO only when it is a real "XX-..." subdivision code and otherwise synthesizes a unique per-country id from the region name. Regenerated admin1.geojson.gz: Spain/China/ Chile/Oman now carry distinct region codes (countries with real codes, e.g. Germany, are unchanged). |
||
|
|
63fb5a9c89 |
feat(admin): let admins set a default currency for new users
Adds a currency picker to Admin > User Defaults. Stored as the default_currency user-default, so users who have not picked their own currency inherit it in Costs. |
||
|
|
17245c5a8c |
fix(atlas): keep the continent breakdown in sync on mark/unmark (#1225)
The optimistic mark/unmark updates bumped the country total but never the per-continent counts, so the continent column froze until a full reload. Move the country to continent map into @trek/shared (single source for server and client) and adjust the matching continent count at every optimistic site: the country confirm flow plus the choose / region mark and region unmark handlers. |
||
|
|
6ab4989c38 |
fix(planner): let a booking's day follow its date when edited (#1237)
Preserving the old day_id on edit left a re-dated booking on its previous start day while end_day_id followed the new date, so it spanned both. Stop sending day_id from the edit modal entirely - the server derives both ends from the booking's date (and keeps the current day when there is no date), so a re-dated booking moves cleanly to the matching day. |
||
|
|
ea7f7fd9f3 |
fix(planner): derive a booking day from its date when none is set (#1237)
The client always sends day_id on a reservation update, so the server only derived it from reservation_time when the field was absent. A non-transport booking saved without a selected day (Book tab) therefore got day_id null and vanished from the Plan, even though its date matched a day. Derive the day from reservation_time whenever day_id is null, mirroring create. |
||
|
|
438f71bbc6 |
test(reservations): align syncBudgetOnUpdate unit tests with no-wipe + type-sync
The service now leaves a linked expense alone when no budget entry is on the payload (only an explicit total_price 0 deletes it) and syncs the category on a booking type change. Update the unit tests accordingly - the old "price cleared" case passed entry: undefined, which is now a no-op and left a mocked return queued that leaked into the next test. |
||
|
|
c15c89ca61 |
feat(costs): create an expense from a booking, fix editing total-only items
Replace the inline price + budget-category fields in the Transport and Reservation booking modals with a "Create expense" flow: the modal saves the booking, then opens the full Costs editor prefilled (name + category mapped from the booking type) and linked to the reservation. A booking with a linked expense shows it inline with edit / remove. Also fix the Costs editor so an expense with a recorded total but no payers (transport-derived or pre-rework items) opens with its amount, lets you set the currency, and saves - it previously showed 0 everywhere and could not be saved. Legacy / localized categories now map to the fixed keys, and changing a booking's type keeps its linked expense category in sync (unless it was manually set). - shared: reservation_id on budget create, typeToCostCategory helper, i18n keys - server: createBudgetItem stores reservation_id; keep total_price for payerless items; a booking update no longer wipes its linked expense and syncs the category on type change - client: shared BookingCostsSection, exported ExpenseModal with prefill and an editable total, page-level save-then-open wiring |
||
|
|
f98058a3af |
feat(backup): make the upload size limit configurable
The restore upload was capped at a hard-coded 500 MB, so instances whose backup archive (uploads/ included) grew past that got a 413 "File too large" with no way to raise it. Add a BACKUP_UPLOAD_LIMIT_MB env var (default 500, invalid values warn and fall back), documented in .env.example. |
||
|
|
39a3ee7ce7 |
fix(collab): show poll option labels in the UI
The poll API formatted each option as { label, voters }, but the React poll
component renders opt.text - so every option button came out blank. Emit text
alongside label (kept for any other consumer) so options render again.
|
||
|
|
e09849d5b4 |
fix(oidc): keep dots in generated usernames
The OIDC username sanitizer stripped dots because they were missing from the allowed character class, so a name claim like "first.last" became "firstlast". Dots are valid usernames (the profile validator already allows ^[a-zA-Z0-9_.-]+$), so add the dot to the sanitizer. |
||
|
|
d40c5ce7a6 |
fix(demo): skip first-run admin seed in demo mode
When DEMO_MODE is on, the demo seeder creates its own admin (admin@trek.app, username "admin") right after the generic seeds run. The first-run admin bootstrap was grabbing username "admin" first, so the demo seeder hit the UNIQUE(username) constraint and aborted before the demo user was ever created - which surfaced as a 500 "Demo user not found" on demo-login. Skip the generic admin bootstrap when demo mode owns the admin account. |
||
|
|
f6af1d67a2 | chore: bump version to 3.1.0 [skip ci] | ||
|
|
ad893eb1cc |
Release 3.1.0 (#1185)
* Phase 0 — NestJS + Zod foundation harness (F1–F8) (#1050) Co-hosted NestJS app behind the existing Express server via a strangler-fig dispatcher, sharing the same better-sqlite3 connection and JWT httpOnly cookie. Additive and dormant: default routing stays on Express, Nest only serves its own /api/_nest diagnostics until a module opts in. F1 @trek/shared Zod contract package; F2 Nest bootstrap co-hosted (fall-through, single Dockerfile/port); F3 shared better-sqlite3 provider; F4 JWT cookie auth guard (+ @CurrentUser, admin guard); F5 Zod validation pipe + error-envelope parity; F6 Nest test + coverage gates; F7 per-prefix strangler toggle (env, default Express); F8 CI build/typecheck/test/coverage. Remaining F4/F6/F8 checklist items (trip-access + permission levels + MFA policy, e2e harness/seed + 80% gate, Nest↔Express parity test, Playwright PR-comment workflow) are tracked on the first consuming module cards (L1/A1/C1). * feat(weather): migrate /api/weather to the NestJS pilot module (L1) (#1053) First strangler migration (L1): /api/weather is served by a NestJS module. - @trek/shared/weather Zod contract; Nest controller byte-identical to the legacy Express route (paths, query params, status codes, { error } bodies, lang default, ApiError/500 passthrough). Service reuses getWeather/getDetailedWeather (+ shared cache; MCP tools unchanged). - Strangler routes /api/weather to Nest by default; the legacy Express route + its migration-time parity test were decommissioned in this PR. - Frontend (FE2): weatherApi typed against the @trek/shared WeatherResult contract. - Harness: reusable Nest-vs-Express parity harness, e2e harness (temp SQLite + seed/cookie helpers, real JwtAuthGuard), src/nest coverage gate raised to >=80%, src/nest test guide. - Verified end-to-end on a prod mirror (dev1): 401/400/200 via Nest with real Open-Meteo data, Express route gone. * fix(packing): multiply item weight by quantity in bag/total weight calcs (#898) Quantity now counts toward bag and total weights. Generalised to an itemWeight() helper used by every weight sum (bag totals + max, unassigned, grand total; sidebar + bag modal) with unit tests. * feat(i18n): add Korean (ko) translation (#977) Korean translation by @ppuassi, topped up to full en.ts key parity. Language registration follows separately. * feat(i18n): add Japanese (ja) translation (#829) Japanese translation by @soma3978, at full en.ts key parity, registered in supportedLanguages + TranslationContext. * Add Turkish (tr) translation + language registry (#1029) Turkish translation by @SkyLostTR, at full en.ts key parity, registered in supportedLanguages + TranslationContext. * i18n: register Korean + add Ukrainian translation (#1055) Korean translation by @ppuassi (#977) — now registered. Ukrainian by @JeffyOLOLO (#902) — lifted onto a clean branch. Both at full en.ts key parity (2258 keys). * chore: fix monorepo build pipeline and migrate shared to built package (#1056) * chore: fix monorepo build pipeline and migrate shared to built package - Root package.json: add workspace scripts (dev, build, test, test:cov, test:e2e) that delegate to actual scripts in shared/server/client workspaces - shared: add tsup build step (CJS + ESM dual output, .d.ts); consumers now import from the built dist instead of raw TS source via path aliases - server: replace tsc-alias with tsconfig-paths (tsc-alias mangled node_modules paths); fix MCP SDK path aliases to point to root node_modules (../node_modules) - server/scripts/dev.mjs: delay node --watch until tsc -w signals first-pass done, eliminating the spurious restart on every dev startup - client/vite.config.js + vitest.config.ts: remove @trek/shared path alias (no longer needed now that shared is a proper package) - Consolidate package-lock.json at the workspace root; drop per-workspace lock files * chore: fix test script to reflect root package.json * chore: add missing lint and prettier script in root package.json * fix(ci): build shared before tests; fix vitest MCP SDK alias paths vitest.config.ts aliases pointed at ./node_modules/ (server-local) but packages are hoisted to the root node_modules/ in the npm workspace — changed to ../node_modules/. CI jobs now install and build shared before running server/client tests so that @trek/shared's dist/ exists when vitest resolves the package. * fix(docker): update Dockerfile and CI for monorepo workspace structure Dockerfile: - Add shared-builder stage that produces @trek/shared dist before client and server stages need it - Each build stage carries root package.json + package-lock.json so npm can resolve @trek/shared as a workspace dependency - Production stage installs via workspace context (npm ci --workspace=server --omit=dev) so node_modules/@trek/shared symlinks to shared/dist correctly - Copy server/tsconfig.json into the image so tsconfig-paths/register can find the MCP SDK path aliases at runtime - CMD cds into /app/server before starting node so tsconfig-paths baseUrl resolves and ../node_modules points to /app/node_modules - Remove mkdir for /app/server (now a real dir); keep symlinks for uploads/data docker.yml version-bump: - Replace manual per-workspace cd+npm-version calls with single: npm version --workspaces --include-workspace-root --no-git-tag-version (mirrors the version:* scripts in root package.json) - git add now references root package-lock.json; adds shared/package.json .dockerignore: add shared/dist package.json: fix version:prerelease preid (alpha → pre) * fix(tests): use in-memory SQLite per worker in test mode vitest pool:forks spawns parallel worker processes that all called initDb() on the same data/travel.db, causing SQLite "database is locked" and "duplicate column name" races. When NODE_ENV=test each fork now gets an isolated :memory: DB so migrations run independently with no file contention. * chore(ci): add ACT guards to skip DockerHub steps in local act runs act sets ACT=true automatically. Guards added: - docker login: if: ${{ !env.ACT }} - build outputs: type=docker (local load) when ACT, push-by-digest when CI - digest export/upload: if: ${{ !env.ACT }} - merge job: if: ${{ !env.ACT }} - release-helm job (docker.yml): if: ${{ !env.ACT }} - version-bump git push (docker.yml): wrapped in [ -z "$ACT" ] shell guard Run locally with: ./bin/act -j build -W .github/workflows/docker.yml \ -P ubuntu-latest=catthehacker/ubuntu:act-latest * fix(ci): move ACT guards to step level; add guards to security.yml env context is invalid in job-level if conditions — moved all ACT guards down to individual steps. Also guards docker login + scout in security.yml so act can run the build-only part of that workflow. * fix(ci): skip git fetch and tag logic in act (no remote access in local containers) * Revert "fix(ci): skip git fetch and tag logic in act (no remote access in local containers)" This reverts commit |
||
|
|
e27be5c965 | chore: bump version to 3.0.22 [skip ci] | ||
|
|
86ee8044da |
v3.0.22 Bug Fixes & Improvements (#1041)
Bundles the v3.0.22 bug fixes and improvements. See the release notes for the full list. |
||
|
|
bfe6664ac4 | chore: bump version to 3.0.21 [skip ci] | ||
|
|
117942f45e |
v3.0.21 Bug Fixes (#998)
* fix(journey): remove photo upload count limit and surface upload errors (#997) Removes the arbitrary 10-file cap on journey entry photo uploads and 20-file cap on gallery uploads. MulterErrors now return proper 4xx responses instead of 500, and the client surfaces the server error message via toast rather than silently trapping the user in the post editor overlay. * fix(planner): remove correct assignment when place assigned to same day multiple times When a place was assigned to the same day more than once, the "Remove from day" button in PlaceInspector always deleted the first assignment (Array.find on place.id) instead of the currently selected one. Now prefers selectedAssignmentId when available. Fixes #1005 * fix(map): enable 3D terrain for Mapbox outdoors style in trip planner wantsTerrain() only matched satellite styles, so the outdoors-v12 style was flat in the planner despite showing correct 3D terrain in the settings preview. Added outdoors-v12 to the allowlist; marker drift is already handled by syncMarkerAltitudes(). Fixes #1002 * fix(maps): send Referer header on Google API calls when APP_URL is set Supports HTTP referrer restrictions on GCP API keys. Documents the restriction types and photo troubleshooting steps in the wiki. |
||
|
|
7e49f3467c | chore: bump version to 3.0.20 [skip ci] | ||
|
|
93b51a0bf5 | fix(csp): allow unsafe-eval for HEIC image conversion | ||
|
|
5b710a429a | chore: bump version to 3.0.19 [skip ci] | ||
|
|
da3cba2de3 |
v3.0.19 Bug Fixes (#992)
* fix(mcp): replace relative oauth constent redirect by absolute redirect derived from APP_URL (#987) * feat(journey): convert HEIC/HEIF uploads to JPEG for cross-platform compatibility HEIC is an Apple-only format not recognised as an image by many browsers and platforms. heic-to (lazy-loaded) now converts HEIC/HEIF files to JPEG before upload in both the gallery and entry editor photo pickers. Embedded metadata (EXIF, GPS) may be lost during conversion — documented in the Journey Journal wiki page. * fix(journey): skip heic-to import for non-HEIC files to avoid test env failures * fix(notifications): prevent double-escaping HTML in password reset emails buildPasswordResetHtml passed a pre-built HTML block to buildEmailHtml, which then escaped it again — rendering raw tags as plain text in the email. |
||
|
|
7f87dc1ce1 | chore: bump version to 3.0.18 [skip ci] | ||
|
|
e7b419d397 |
security: login timing enumeration fix + dep CVE patches (v3.0.18) (#984)
* fix(security): equalise login response timing to prevent user enumeration (CWE-208)
Always run bcrypt.compareSync regardless of whether the email exists, using a
module-scope DUMMY_PASSWORD_HASH for unknown/OIDC-only accounts. Also wraps the
login handler in a 350ms minimum-latency pad (matching /forgot-password) as
defence-in-depth against CPU jitter and future code-path drift.
Fixes: CWE-203, CWE-208 — Observable Timing Discrepancy (CVSS 5.3 Medium)
* chore(deps): patch hono/picomatch/ip-address/brace-expansion CVEs, bump to node:24-alpine
Extends server/package.json overrides to pin hono >=4.12.16, picomatch >=4.0.4,
brace-expansion >=2.0.3, ip-address >=10.1.1. Adds matching overrides to client/.
Lockfiles regenerated to resolve: hono 4.12.18, ip-address 10.2.0, picomatch 4.0.4.
Also bumps base image node:22-alpine -> node:24-alpine (reduces base image CVEs)
and adds .github/workflows/security.yml to gate PRs on critical/high CVEs via
Docker Scout.
Addresses: CVE-2026-44456, CVE-2026-44455 (hono), CVE-2026-42338 (ip-address),
CVE-2026-33671, CVE-2026-33672 (picomatch), CVE-2026-33750 (brace-expansion)
* chore: update emails in security.md
* ci(security): use docker/login-action for Scout auth instead of env vars
* chore: regenerate lock files
* chore: correct secret names
* chore: pr perms write
* fix(docker): remove package-lock.json from production image after npm ci
Docker Scout reads package-lock.json as an SBOM source and reports all
lockfile entries including devDependencies (e.g. picomatch via vitest/vite)
even when they are not physically installed. The lockfile has no runtime
purpose after npm ci completes, so delete it to ensure Scout only reports
packages actually present in node_modules.
* fix(docker): remove npm CLI from production image to eliminate bundled CVEs
picomatch@4.0.3, brace-expansion@5.0.4, and ip-address@10.1.0 were all
coming from /usr/local/lib/node_modules/npm — npm's own bundled packages
shipped with node:24-alpine. The production container only needs the node
binary to run the server; npm is unused at runtime.
Removing npm + npx after npm ci drops the package count from 500 to 365
and eliminates all npm-ecosystem CVEs (0H 0M remaining from npm packages).
Only busybox CVE-2025-60876 remains, which has no fix in Alpine 3.23.
* fix(deps): remove client overrides and brace-expansion server override; audit fix
brace-expansion ^2.0.3 in the client forced all installations to v2, breaking
minimatch in CI (test:coverage path via @vitest/coverage-v8 -> test-exclude)
which expects the named-export API of brace-expansion v5. The CVE it targeted
(>=4.0.0,<5.0.5) was only in npm's own bundled packages, already eliminated
by removing npm from the Docker image.
Also removes picomatch and ip-address client overrides for the same reason:
all three CVEs sourced from /usr/local/lib/node_modules/npm/, not app deps.
Drops brace-expansion from server overrides (server uses v2.1.0, outside the
affected range >=4.0.0).
* fix(#981): align public share itinerary order with daily planner (#985)
The public share page rendered daily items in a different order than the
authenticated planner because it used a simplified, divergent merge
algorithm. Five specific bugs:
1. shareService never loaded reservation_day_positions, so per-day
transport positions were lost on the share page (fell back to
day_plan_position ?? 999, pushing transports to the bottom).
2. Multi-day transports (overnight trains/flights) only appeared on their
start day due to date-string filtering instead of day_id span logic.
3. Assignment-linked transports appeared twice (once as place, once as
transport card) because the assignment_id exclusion was missing.
4. Time-based transport insertion was absent; missing positions used 999
instead of a computed fractional position from the place timeline.
5. created_at tiebreaker was missing for assignments and notes with equal
order_index/sort_order, making order non-deterministic on the share page.
Fix: extract the authoritative merge logic (parseTimeToMinutes,
getSpanPhase, getDisplayTimeForDay, getTransportForDay, getMergedItems)
from DayPlanSidebar into client/src/utils/dayMerge.ts and use it in both
the planner and SharedTripPage. Enrich the shareService payload with
day_positions from reservation_day_positions and add created_at tiebreakers
to the assignment and day_notes ORDER BY clauses.
* fix(#983): shift owner vacay entries when update_trip moves trip window
updateTrip() now calls shiftOwnerEntriesForTripWindow() which looks up
the owner's own vacay plan (not the active plan) and shifts all entries
in the old date window by the same offset as the trip start date.
|
||
|
|
de3152ee57 | chore: bump version to 3.0.17 [skip ci] | ||
|
|
de6c0fb781 |
fix: prevent Invalid URL crash when APP_URL lacks a protocol (#972)
* fix: prevent Invalid URL crash when APP_URL lacks a protocol (issue #970) - Add getMcpSafeUrl() to notifications.ts: wraps getAppUrl() and guarantees a result that satisfies the MCP SDK's checkIssuerUrl requirement (https:// or http://localhost). Non-HTTPS, non-localhost URLs fall back to http://localhost:{PORT} instead of propagating an "Issuer URL must be HTTPS" error. - Switch app.ts, mcp/index.ts, mcp/oauthProvider.ts, and oauthService.ts to import getMcpSafeUrl instead of getAppUrl for all MCP resource URL construction, so a misconfigured APP_URL never crashes the metadata router initialisation. - Restrict the SDK metadata router middleware to /.well-known/* paths only. Previously it was invoked on every request; in production the lazy getMetaRouter() init ran on GET / and threw "Invalid URL" when APP_URL had no scheme, returning 500 for every page load. - Log a startup warning when APP_URL is set but not usable, and include the resolved App URL in the startup banner so operators can confirm the correct value at a glance. - Update oauth.test.ts mock to target notifications.getMcpSafeUrl. * fix: show getAppUrl in banner and add two separate APP_URL startup checks - Banner now displays getAppUrl() (the resolved app URL) rather than getMcpSafeUrl() so operators see the actual configured value - Two independent startup warnings after the banner when APP_URL is set: 1. whether APP_URL is a valid URL (parseable by new URL()) 2. whether APP_URL is MCP-safe (https:// or http://localhost) - Fix getMcpSafeUrl() fallback port to use Number(PORT) || 3001, consistent with how index.ts parses PORT * fix: update oidc.ts to import getAppUrl from notifications |
||
|
|
9f1d05e886 | chore: bump version to 3.0.16 [skip ci] | ||
|
|
25f326a659 |
v3.0.16 — bug fixes (#964)
* fix(mcp): MCP RFC compliant for more strict clients * fix(mcp): serve flat /.well-known/oauth-protected-resource for ChatGPT reconnect Clients such as ChatGPT probe the flat well-known URL on every fresh discovery cycle (i.e. after a full disconnect/reconnect where cached OAuth state is cleared). The SDK's mcpAuthMetadataRouter only serves the path-based form /.well-known/oauth-protected-resource/mcp, so the flat probe returned 404. Without the resource metadata, ChatGPT fell back to the issuer URL as the resource parameter (https://…/ instead of https://…/mcp). The authorize handler then rejected it with invalid_target and redirected back to ChatGPT's callback with an error — showing the user the TREK home page instead of the consent form. Add an explicit GET handler for the flat URL that returns the same protected resource metadata, so the resource URI is discovered correctly on the first probe. * fix(mcp): fix OAuth popup blank page — SW denylist and COOP header Service worker was intercepting /oauth/authorize navigate requests (not in denylist), serving index.html, and React Router's catch-all redirected to / instead of the SDK authorize handler. Helmet's default COOP: same-origin isolated the /oauth/consent popup from its cross-origin opener, making window.opener null and breaking the popup-based OAuth completion signal for ChatGPT and similar clients. * fix(ntfy): encode non-Latin-1 header values with RFC 2047 to prevent ByteString crash Todo/trip names containing chars like → or € (and non-Latin-1 locale templates for Czech, Chinese, Russian, etc.) caused the Fetch API to throw when setting the ntfy Title header. Apply RFC 2047 base64 encoded-word encoding for any header value containing chars above U+00FF; ntfy decodes this automatically. * docs(mcp): document Cloudflare bot detection blocking ChatGPT MCP requests Add Cloudflare WAF note to MCP-Setup and a full troubleshooting entry covering root cause (IP reputation + UA heuristics), free-plan limitation (disable Bot Fight Mode entirely, with explicit warning), and paid-plan WAF skip rule with the full expression syntax and path table for all MCP/OAuth/.well-known routes. * fix(pwa): detect upstream proxy auth challenges and recover gracefully Behind Cloudflare Zero Trust or Pangolin, cross-origin auth redirects on /api/* calls surface as CORS errors (error.response === undefined) that the existing 401 interceptor never catches, leaving the PWA stuck with network-error toasts instead of re-authenticating. New connectivity module probes /api/health every 30s using fetch with cache:no-store and inspects Content-Type to reliably detect whether the server is reachable vs intercepted by an upstream proxy. axios interceptor changes: - On !error.response + navigator.onLine: run probeNow(); if the health probe also fails (proxy is intercepting all requests), trigger a guarded window.location.reload() so the edge proxy can intercept the top-level navigation and run its auth flow (covers CF Access and Pangolin 302 mode) - On error.response status 401 with text/html body: same reload path, covering Pangolin header-auth extended compatibility mode which returns 401+HTML instead of a 302 redirect. TREK own 401s are always JSON so there is no collision with the existing AUTH_REQUIRED branch. - sessionStorage flag prevents reload loops; cleared on any successful response so the guard resets after re-auth. /api/health excluded from SW NetworkFirst cache (vite.config.js regex) and Cache-Control: no-store added server-side so probes always hit the network and cannot be served stale from the 24h api-data cache. LoginPage caches last-known appConfig in localStorage so the SSO button renders in OIDC+UN/PW dual mode even when the config fetch is intercepted by the proxy. Auto-redirect to IdP skipped when config comes from cache to avoid redirect loops while the proxy is challenging. Fixes discussion #836. * fix(files): add bottom-nav padding to files tab wrapper on mobile * fix(budget): expose toolbar on mobile so users can add budget categories * fix(pwa): unregister SW before proxy-reauth reload so Pangolin can challenge WorkBox's NavigationRoute served the cached SPA shell on window.location.reload(), meaning Pangolin/CF Access never saw the navigation and the app was left stuck showing stale offline data. Unregistering the SW first lets the navigation reach the network so the upstream proxy can run its auth flow. Also rebuilds server/public with corrected sw.js (health excluded from NetworkFirst, /oauth/ and /.well-known/ added to NavigationRoute denylist). * chore: remove committed build artifacts from server/public Dockerfile and Proxmox community script both rebuild client/dist and copy it into server/public at build time — committed artifacts were never used. Replace with .gitkeep and add server/public/* to .gitignore. * chore: add build-from-sources script |
||
|
|
640e5616e9 | chore: bump version to 3.0.15 [skip ci] | ||
|
|
22f3bf4bfc |
fix: add APP_VERSION fallback and HOST bind address env var (#952 #953) (#955)
* fix: add APP_VERSION fallback and HOST bind env var (#952 #953) - Read package.json version when APP_VERSION env var is absent so the startup banner shows the correct version for source/Proxmox installs - Add HOST env var to control the HTTP bind address; only applied when set so Docker deployments are unaffected (bind-all-interfaces default) - Parse PORT as Number() so malformed values like '10.0.0.72:3001' fall back to 3001 instead of silently misbehaving - Document HOST in .env.example, Environment-Variables wiki, and Install-Proxmox wiki with explicit warnings against using it in Docker * fix: correct package.json path in APP_VERSION fallback index.ts sits at server/src/ — one level up reaches server/package.json, not two (../../ overshot to the repo root where no package.json exists). |
||
|
|
51b5bd6966 | chore: bump version to 3.0.14 [skip ci] | ||
|
|
6072b969d6 |
Bug fixes - May 2nd 2026 (#941)
* fix: collab chat input hidden by mobile bottom nav bar Closes #939 * chore: prepare database for nest + typeorm * fix(ssrf): relax internal network resolution (#947) * docs(ssrf): update Internal-Network-Access wiki to reflect relaxed guard Loopback, link-local, and .local/.internal hostnames are now all overridable with ALLOW_INTERNAL_NETWORK=true (commit |
||
|
|
4ae4e0c676 | chore: bump version to 3.0.13 [skip ci] | ||
|
|
51ab30f436 |
Bug fixes - April 30th 2026 (#936)
* fix: hotel day-range clamping in ReservationModal + stale assignment_id on accommodation clear (issues #929, #934)
* ReservationModal hotel start/end pickers now use findIndex-based
positional clamping instead of raw ID arithmetic, matching the fix
applied to DayDetailPanel in
|
||
|
|
8b53948231 | chore: bump version to 3.0.12 [skip ci] | ||
|
|
7f1fb508db | chore: bump version to 3.0.11 [skip ci] | ||
|
|
1f5deeba6c |
Bug fixes - April 27th 2026 (#907)
* fix: clean up dangling FK references before deleting a user Resolves FOREIGN KEY constraint failed (500) on DELETE /api/admin/users/:id and DELETE /api/auth/me when the target user had rows in trip_members.invited_by, share_tokens.created_by, budget_items.paid_by_user_id, journeys.user_id, journey_entries.author_id, journey_contributors.user_id, or journey_share_tokens.created_by — none of which had ON DELETE clauses. Introduces deleteUserCompletely() in userCleanupService.ts which wraps all cleanup and the final DELETE FROM users in a single transaction. Both adminService.deleteUser and authService.deleteAccount now call it instead of the bare DELETE. Tests ADMIN-005b and AUTH-040 cover all reference types including notification sender/recipient and notice dismissals. * test: extend FK deletion tests to cover journeys, files, and photos ADMIN-005b and AUTH-040 now also seed and assert: - owned journey with entries (cascade-deleted via journeys.user_id cleanup) - trip_files.uploaded_by (SET NULL — file survives, attribution cleared) - trek_photos.owner_id (SET NULL — photo record survives, owner cleared) - trip_photos.user_id (CASCADE — photo association removed) * test: extend user deletion tests to cover all FK relationships ADMIN-005b and AUTH-040 now seed and assert every user FK relationship: CASCADE (row deleted): trips, trip_members, tags, mcp_tokens, oauth_tokens, oauth_consents, vacay_plans, vacay_plan_members, bucket_list, visited_countries, visited_regions, packing_templates, invite_tokens, collab_notes, settings, password_reset_tokens, notification_channel_preferences SET NULL (row survives, column nulled): categories, todo_items.assigned_user_id, packing_bags, audit_log Caught and fixed: notification_preferences was dropped in migration 72; correct table is notification_channel_preferences. * fix: preserve URL hash and OIDC redirect target through login flow - Include location.hash in redirect param at all three producer sites (ProtectedRoute, axios 401 interceptor, OAuthAuthorizePage) so hash fragments survive the login bounce - Stash redirectTarget in sessionStorage before any OIDC provider redirect and restore it after the code exchange, since the IdP strips the original ?redirect= param during the roundtrip - Clear sessionStorage on OIDC error to avoid stale state - Add tests covering sessionStorage stash on mount, navigate to saved redirect after OIDC exchange, fallback to /dashboard, and cleanup on error * fix: use day position instead of ID for accommodation date range clamping Math.min/Math.max over raw day IDs breaks the start/end picker when a trip's day IDs are non-monotonic relative to day_number (normal after repeated generateDays extend/shrink cycles). Replaced with findIndex lookups so clamping is always based on positional order. Closes #889 * fix: normalize env var comparisons to be case-insensitive All NODE_ENV, DEMO_MODE, OIDC_ONLY, FORCE_HTTPS, COOKIE_SECURE, and ALLOW_INTERNAL_NETWORK checks now use .toLowerCase() so values like 'Production' or 'True' behave identically to their lowercase forms. Also adds APP_VERSION to the startup banner. * fix: delete surplus days when shortening a trip When shrinking a trip's date range, surplus days are now deleted along with their assignments, notes, and accommodations (cascade). Places remain in the trip pool; reservations keep their day reference nulled by the existing ON DELETE SET NULL constraint (issue #909). Updates TRIP-SVC-011 to reflect the new behaviour; adds TRIP-SVC-016 as a regression test for the empty-day case. * fix: auto-backup retention deletes itself and manual backups on Docker Two bugs in cleanupOldBackups: 1. Filter was .endsWith('.zip') — swept manual backup-*.zip files too. Now restricted to auto-backup-* prefix. 2. Age was derived from stat.birthtimeMs, which is 0 on overlayfs (Docker default), making every backup appear epoch-old and get deleted immediately. Age is now parsed from the filename timestamp and falls back to mtimeMs (reliable on overlayfs). Also converts inline require('./services/auditLog') calls to a static import throughout scheduler.ts, and adds 8 unit tests covering the fixed retention logic including the overlayfs regression case. * test: update TRIP-024 to match delete behavior on trip shrink * feat: add bypass-branch-check label to skip branch enforcement |
||
|
|
2770a189df | chore: bump version to 3.0.10 [skip ci] | ||
|
|
2b162a8cc7 | chore: reset to 3.0.9 | ||
|
|
009d89fecf | chore: bump version to 3.0.10 [skip ci] | ||
|
|
303e7de433 | chore: bump version to 3.0.9 [skip ci] | ||
|
|
bc1fb71391 |
Fix exit code 132 on old CPUs by replacing sharp with jimp (issue #888) (#895)
sharp's prebuilt Linux x64 binary requires SSE4.2 (x86-64-v2), causing a SIGILL crash on older hardware (e.g. AMD A6-3420M). Replace with jimp, a pure-JS image library with no native binaries. Also skip thumbnail generation entirely when the Journey addon is disabled (the default), preventing the issue for most installs regardless of the image library used. |
||
|
|
cb425fb397 |
Fix 500 on reservation edit after DB reinit (issue #883)
saveEndpoints was bound at module load via db.transaction(...). When the demo-mode hourly reset (or a self-hoster's backup restore) closes the DB connection and reinitialises it, the bound transaction still references the now-closed connection — every subsequent reservation save with an endpoints field throws "The database connection is not open", which the client surfaces as "Internal server error". Bind the transaction lazily on each call so it always runs against the current connection. |
||
|
|
8342cf3010 | chore: bump version to 3.0.8 [skip ci] | ||
|
|
2a37eeccb3 |
fix: hot fixes 23-04-2026 (#856)
* fix(packing): resolve avatar URL path in bag and category assignees (#854) packingService was returning raw avatar filenames from the DB instead of the full /uploads/avatars/<filename> path, causing broken profile images for users with uploaded avatars. * fix(budget): use Map.get() to fix category rename no-op (#855) * fix(security): relax Referrer-Policy and document HSTS_INCLUDE_SUBDOMAINS (#862) (#863) - Change Helmet default from no-referrer to strict-origin-when-cross-origin so browsers send the origin on cross-origin requests, allowing Google Maps API key restrictions by HTTP referrer to work correctly - Document HSTS_INCLUDE_SUBDOMAINS in all deployment artifacts: .env.example, docker-compose.yml, README.md, unraid-template.xml, charts/values.yaml, charts/configmap.yaml, wiki/Environment-Variables.md * fix(planner): prefetch budget items on trip page mount (#864) Loads budgetItems alongside reservations when TripPlannerPage mounts so the Budget category dropdown in ReservationModal and TransportModal shows pre-existing categories on first open, regardless of whether the Budget tab has been visited. Closes #861 * fix(reservations): prevent Invalid Date when end time is set without end date (#866) When reservation_end_time held a bare time string ("HH:MM"), fmtDate() produced Invalid Date on the reservation card. - Modal: when end date is blank but end time is filled, construct a same-day ISO datetime using the start date (prevents time-only strings from ever being persisted) - Panel: derive endDatePart via regex so date-only end values ("YYYY-MM-DD") still show the multi-day range, while bare time strings are skipped and handled correctly by the existing time column logic Closes #860 * fix(planner): format reservation end time instead of rendering raw ISO string (#867) Closes #859 * fix(planner): wire Route toggle into mobile day sidebar (#850) (#868) The per-booking Route icon was missing on mobile because the mobile DayPlanSidebar invocation in TripPlannerPage didn't pass visibleConnectionIds or onToggleConnection. Mobile PWA users couldn't activate reservation map overlays without forcing desktop mode. Also corrects the Map-Features wiki: fixes the setting name ("Booking route labels" not "Show connection labels"), documents the route_calculation requirement for travel-time pills, and explains that overlays are off by default and must be toggled per reservation. |
||
|
|
ae0e59d9f1 | chore: bump version to 3.0.7 [skip ci] | ||
|
|
50bb7573fd |
[Snyk] Security upgrade uuid from 9.0.1 to 14.0.0 (#849)
* fix: server/package.json & server/package-lock.json to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-UUID-16133035 * fix: bump fast-xml-parser version --------- Co-authored-by: snyk-bot <snyk-bot@snyk.io> Co-authored-by: jubnl <jgunther021@gmail.com> |
||
|
|
b852317c84 | chore: bump version to 3.0.6 [skip ci] |