Commit Graph

142 Commits

Author SHA1 Message Date
mauriceboe 16cadeb09e feat(atlas): sub-national region view when zooming in
- Zoom >= 5 shows visited regions (states/provinces/departments) colored on the map
- Server resolves places to regions via Nominatim reverse geocoding (zoom=8)
- Supports all ISO levels: lvl4 (states), lvl5 (provinces), lvl6 (departments)
- Handles city-states (Berlin, Vienna, Hamburg) via city/county fallback
- Fuzzy name matching between Nominatim and GeoJSON for cross-format compatibility
- 10m admin_1 GeoJSON loaded server-side (cached), filtered per country
- Region colors match their parent country color
- Custom DOM tooltip (ref-based, no re-renders on hover)
- Country layer dims to 35% opacity when regions visible
- place_regions DB table caches resolved regions permanently
- Rate-limited Nominatim calls (1 req/sec) with progressive resolution
2026-04-05 01:31:19 +02:00
mauriceboe b8058a2755 fix(reservations): budget category dropdown, localized auto-category, price input cleanup
- Budget category uses dropdown with existing categories instead of freetext
- Auto category uses translated booking type names (e.g. "Volo" in Italian)
- Remove number input spinner arrows, use decimal inputMode
- Add budget entry creation to PUT handler (update), not just POST (create)
- Error logging for failed budget entry creation
- i18n keys for all 13 languages
2026-04-05 00:13:07 +02:00
mauriceboe aa244dd548 feat(reservations): add price field with automatic budget entry creation
- Optional price and budget category fields on the reservation form
- When a price is set, a budget entry is automatically created on save
- Price and category stored in reservation metadata for reference
- Hint text shown when price is entered
- i18n keys for EN and DE
2026-04-04 23:59:30 +02:00
mauriceboe 0b36427c09 feat(todo): add To-Do list feature with 3-column layout
- New todo_items DB table with priority, due date, description, user assignment
- Full CRUD API with WebSocket real-time sync
- 3-column UI: sidebar filters (All, My Tasks, Overdue, Done, by Priority),
  task list with inline badges, and detail/create pane
- Apple-inspired design with custom dropdowns, date picker, priority system (P1-P3)
- Mobile responsive: icon-only sidebar, bottom-sheet modals for detail/create
- Lists tab with sub-tabs (Packing List + To-Do), persisted selection
- Addon renamed from "Packing List" to "Lists"
- i18n keys for all 13 languages
- UI polish: notification colors use system theme, mobile navbar cleanup,
  settings page responsive buttons
2026-04-04 16:58:24 +02:00
mauriceboe 50d2a211e5 fix(oidc): revert default scope to 'openid email profile'
Removes 'groups' from the default OIDC_SCOPE fallback, which caused
invalid_scope errors with providers that don't support it (e.g. Google).

Fixes #391
2026-04-04 13:33:54 +02:00
jubnl 93c0d6fe78 fix(trips): default to 7-day window when dates are omitted on creation
- No dates → tomorrow to tomorrow+7d
- Start only → end = start+7d
- End only → start = end-7d
- Both provided → unchanged

fix(ci): include client/package-lock.json in version bump commit
2026-04-03 23:58:39 +02:00
jubnl d765a80ea3 fix(immich): proxy shared photos using owner's Immich credentials
Trip members viewing another member's shared photo were getting a 404
because the proxy endpoints always used the requesting user's Immich
credentials instead of the photo owner's. The ?userId= query param the
client already sent was silently ignored.

- Add canAccessUserPhoto() to verify the asset is shared and the
  requesting user is a trip member before allowing cross-user proxying
- Pass optional ownerUserId through proxyThumbnail, proxyOriginal, and
  getAssetInfo so credentials are fetched for the correct user
- Enforce shared=1 check so unshared photos remain inaccessible
2026-04-03 22:32:41 +02:00
Maurice 5dd80d5cb8 feat: Discord links, translation sync, iOS login fix, trip copy fix
- Add Discord button to admin GitHub panel and user menu
- Sync all 13 translation files to 1434 keys with native translations
- Fix duplicate keys in Polish translation (pl.ts)
- Fix iOS login race condition: sameSite strict→lax, loadUser sequence counter
- Fix trip copy route: add missing db, Trip, TRIP_SELECT imports
2026-04-03 14:39:44 +02:00
Julien G. 905c7d460b Add comprehensive backend test suite (#339)
* add test suite, mostly covers integration testing, tests are only backend side

* workflow runs the correct script

* workflow runs the correct script

* workflow runs the correct script

* unit tests incoming

* Fix multer silent rejections and error handler info leak

- Revert cb(null, false) to cb(new Error(...)) in auth.ts, collab.ts,
  and files.ts so invalid uploads return an error instead of silently
  dropping the file
- Error handler in app.ts now always returns 500 / "Internal server
  error" instead of forwarding err.message to the client

* Use statusCode consistently for multer errors and error handler

- Error handler in app.ts reads err.statusCode to forward the correct
  HTTP status while keeping the response body generic
2026-04-03 13:17:53 +02:00
Gérnyi Márk d48714d17a feat: add copy/duplicate trip from dashboard (#270)
New POST /api/trips/:id/copy endpoint that deep copies all trip
planning data (days, places, assignments, reservations, budget,
packing, accommodations, day notes) with proper FK remapping
inside a transaction. Skips files, collab data, and members.

Copy button on all dashboard card types (spotlight, grid, list,
archived) gated by trip_create permission. Translations for all
12 languages.

Also adds reminder_days to Trip interface (removes as-any casts).
2026-04-03 12:38:45 +02:00
jubnl c0e9a771d6 feat: add in-app notification system with real-time delivery
Introduces a full in-app notification system with three types (simple,
boolean with server-side callbacks, navigate), three scopes (user, trip,
admin), fan-out persistence per recipient, and real-time push via
WebSocket. Includes a notification bell in the navbar, dropdown, dedicated
/notifications page, and a dev-only admin tab for testing all notification
variants.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 18:57:52 +02:00
Maurice 979322025d refactor: extract business logic from routes into reusable service modules 2026-04-02 17:14:53 +02:00
jubnl 32b63adc68 fix: add OIDC_SCOPE env var and document it across all config files
Fixes #306 — OIDC scopes were hardcoded to 'openid email profile',
causing OIDC_ADMIN_CLAIM-based role mapping to fail when the required
scope (e.g. 'groups') wasn't requested. The new OIDC_SCOPE variable
defaults to 'openid email profile groups' so group-based admin mapping
works out of the box. Variable is now documented in README, docker-compose,
.env.example, and the Helm chart values.
2026-04-02 07:46:58 +02:00
jubnl 1a4c04e239 fix: resolve Immich 401 passthrough causing spurious login redirects
- Auth middleware now tags its 401s with code: AUTH_REQUIRED so the
  client interceptor only redirects to /login on genuine session failures,
  not on upstream API errors
- Fix /albums and album sync routes using raw encrypted API key instead
  of getImmichCredentials() (which decrypts it), causing Immich to reject
  requests with 401
- Add toast error notifications for all Immich operations in MemoriesPanel
  that previously swallowed errors silently

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 21:19:53 +02:00
jubnl 0ebcff9504 Conflict resolution 2026-04-01 17:40:45 +02:00
Julien G. edafe01387 Merge branch 'dev' into dev 2026-04-01 17:30:31 +02:00
Maurice 16277a3811 security: fix missing trip access checks on Immich routes (GHSA-pcr3-6647-jh72)
security: require auth for uploaded photos (GHSA-wxx3-84fc-mrx2)

GHSA-pcr3-6647-jh72 (HIGH):
- Add canAccessTrip check to all /trips/:tripId/photos and
  /trips/:tripId/album-links endpoints
- Prevents authenticated users from accessing other trips' photos

GHSA-wxx3-84fc-mrx2 (LOW):
- /uploads/photos now requires JWT auth token or valid share token
- Covers and avatars remain public (needed for login/share pages)
- Files were already blocked behind auth
2026-04-01 15:46:08 +02:00
Maurice ef5b381f8e feat: collapse days hides map markers, Immich test-before-save (#216)
Map markers:
- Collapsing a day in the sidebar hides its places from the map
- Places assigned to multiple days only hide when all days collapsed
- Unplanned places always stay visible

Immich settings:
- New POST /integrations/immich/test endpoint validates credentials
  without saving them
- Save button disabled until test connection passes
- Changing URL or API key resets test status
- i18n: testFirst key for all 12 languages
2026-04-01 15:30:59 +02:00
Maurice ef9880a2a5 feat: Immich album linking with auto-sync (#206)
- Link Immich albums to trips — photos sync automatically
- Album picker shows all user's Immich albums
- Linked albums displayed as chips with sync/unlink buttons
- Auto-sync on link: fetches all album photos and adds to trip
- Manual re-sync button for each linked album
- DB migration: trip_album_links table

fix: shared Immich photos visible to other trip members

- Thumbnail/original proxy now uses photo owner's Immich credentials
  when userId query param is provided, fixing 404 for shared photos
- i18n: album keys for all 12 languages
2026-04-01 15:21:20 +02:00
Maurice 95cb81b0e5 perf: major trip planner performance overhaul (#218)
Store & re-render optimization:
- TripPlannerPage uses selective Zustand selectors instead of full store
- placesSlice only updates affected days on place update/delete
- Route calculation only reacts to selected day's assignments
- DayPlanSidebar uses stable action refs instead of full store

Map marker performance:
- Shared photoService for PlaceAvatar and MapView (single cache, no duplicate requests)
- Client-side base64 thumbnail generation via canvas (CORS-safe for Wikimedia)
- Map markers use base64 data URL <img> tags for smooth zoom (no external image decode)
- Sidebar uses same base64 thumbnails with IntersectionObserver for visible-first loading
- Icon cache prevents duplicate L.divIcon creation
- MarkerClusterGroup with animate:false and optimized chunk settings
- Photo fetch deduplication and batched state updates

Server optimizations:
- Wikimedia image size reduced to 400px (from 600px)
- Photo cache: 5min TTL for errors (was 12h), prevents stale 404 caching
- Removed unused image-proxy endpoint

UX improvements:
- Splash screen with plane animation during initial photo preload
- Markdown rendering in DayPlanSidebar place descriptions
- Missing i18n keys added, all 12 languages synced to 1376 keys
2026-04-01 14:56:01 +02:00
Maurice 4ebf9c5f11 feat: add expense date and CSV export to budget
- New expense_date column on budget items (DB migration #42)
- Date column in budget table with custom date picker
- CSV export button with BOM, semicolon separator, localized dates,
  currency in header, per-person/day calculations
- CustomDatePicker compact/borderless modes for inline table use
- i18n keys for all 12 languages
2026-04-01 12:16:11 +02:00
jubnl add0b17e04 feat(auth): migrate JWT storage from localStorage to httpOnly cookies
Eliminates XSS token theft risk by storing session JWTs in an httpOnly
cookie (trek_session) instead of localStorage, making them inaccessible
to JavaScript entirely.

- Add cookie-parser middleware and setAuthCookie/clearAuthCookie helpers
- Set trek_session cookie on login, register, demo-login, MFA verify, OIDC exchange
- Auth middleware reads cookie first, falls back to Authorization: Bearer (MCP unchanged)
- Add POST /api/auth/logout to clear the cookie server-side
- Remove all localStorage auth_token reads/writes from client
- Axios uses withCredentials; raw fetch calls use credentials: include
- WebSocket ws-token exchange uses credentials: include (no JWT param)
- authStore initialises isLoading: true so ProtectedRoute waits for /api/auth/me

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 11:02:45 +02:00
Julien G. 9292acb979 Merge branch 'dev' into dev 2026-04-01 10:27:51 +02:00
Maurice 040840917c feat: add Google Maps list import
Import places from shared Google Maps lists via URL.
Button in places sidebar next to GPX import opens a modal
where users can paste a shared list link. Server fetches
list data from Google Maps and creates places with name,
coordinates and notes. i18n keys added for all 12 languages.

Closes #205
2026-04-01 10:13:35 +02:00
Maurice 8c85ea3644 fix: restrict trip listing and access to own/shared trips only (#250)
Remove admin override that returned all trips regardless of ownership.
Admins now see only their own trips and trips where they are a member.
2026-04-01 09:29:28 +02:00
jubnl 7a314a92b1 fix: add SSRF protection for link preview and Immich URL
- Create server/src/utils/ssrfGuard.ts with checkSsrf() and createPinnedAgent()
  - Resolves DNS before allowing outbound requests to catch hostnames that
    map to private IPs (closes the TOCTOU gap in the old inline checks)
  - Always blocks loopback (127.x, ::1) and link-local/metadata (169.254.x)
  - RFC-1918, CGNAT (100.64/10), and IPv6 ULA ranges blocked by default;
    opt-in via ALLOW_INTERNAL_NETWORK=true for self-hosters running Immich
    on a local network
  - createPinnedAgent() pins node-fetch to the validated IP, preventing
    DNS rebinding between the check and the actual connection

- Replace isValidImmichUrl() (hostname-string check, no DNS resolution)
  with checkSsrf(); make PUT /integrations/immich/settings async
  - Audit log entry (immich.private_ip_configured) written when a user
    saves an Immich URL that resolves to a private IP
  - Response includes a warning field surfaced as a toast in the UI

- Replace ~20 lines of duplicated inline SSRF logic in the link-preview
  handler with a single checkSsrf() call + pinned agent

- Document ALLOW_INTERNAL_NETWORK in README, docker-compose.yml,
  server/.env.example, chart/values.yaml, chart/templates/configmap.yaml,
  and chart/README.md
2026-04-01 07:59:03 +02:00
jubnl e03505dca2 fix: enforce consistent password policy across all auth flows
Replace duplicated inline validation with a shared validatePassword()
utility that checks minimum length (8), rejects repetitive and common
passwords, and requires uppercase, lowercase, a digit, and a special
character.

- Add server/src/services/passwordPolicy.ts as single source of truth
- Apply to registration, password change, and admin create/edit user
  (admin routes previously had zero validation)
- Fix client min-length mismatch (6 vs 8) in RegisterPage and LoginPage
- Add client-side password length guard to AdminPage forms
- Update register.passwordTooShort and settings.passwordWeak i18n keys
  in all 12 locales to reflect the corrected requirements
2026-04-01 07:58:46 +02:00
jubnl ce8d498f2d fix: add independent rate limiter for MFA verification endpoints
TOTP brute-force is a realistic attack once a password is compromised:
with no independent throttle, an attacker shared the login budget (10
attempts) across /login, /register, and /mfa/verify-login, and
/mfa/enable had no rate limiting at all.

- Add a dedicated `mfaAttempts` store so MFA limits are tracked
  separately from login attempts
- Introduce `mfaLimiter` (5 attempts / 15 min) applied to both
  /mfa/verify-login and /mfa/enable
- Refactor `rateLimiter()` to accept an optional store parameter,
  keeping all existing call-sites unchanged
- Include mfaAttempts in the periodic cleanup interval
2026-04-01 07:58:29 +02:00
jubnl b109c1340a fix: prevent ICS header injection in calendar export
Three vulnerabilities patched in the /export.ics route:

- esc() now handles bare \r and CRLF sequences — the previous regex only
  matched \n, leaving \r intact and allowing CRLF injection via \r\n
- reservation DESCRIPTION field was built from unescaped user data
  (type, confirmation_number, notes, airline, flight/train numbers,
  airports) and written raw into ICS output; now passed through esc()
- Content-Disposition filename used ICS escaping instead of HTTP header
  sanitization; replaced with a character allowlist to prevent " and
  \r\n injection into the response header
2026-04-01 07:58:18 +02:00
jubnl 6f5550dc50 fix: decouple at-rest encryption from JWT_SECRET, add JWT rotation
Introduces a dedicated ENCRYPTION_KEY for encrypting stored secrets
(API keys, MFA TOTP, SMTP password, OIDC client secret) so that
rotating the JWT signing secret no longer invalidates encrypted data,
and a compromised JWT_SECRET no longer exposes stored credentials.

- server/src/config.ts: add ENCRYPTION_KEY (auto-generated to
  data/.encryption_key if not set, same pattern as JWT_SECRET);
  switch JWT_SECRET to `export let` so updateJwtSecret() keeps the
  CJS module binding live for all importers without restart
- apiKeyCrypto.ts, mfaCrypto.ts: derive encryption keys from
  ENCRYPTION_KEY instead of JWT_SECRET
- admin POST /rotate-jwt-secret: generates a new 32-byte hex secret,
  persists it to data/.jwt_secret, updates the live in-process binding
  via updateJwtSecret(), and writes an audit log entry
- Admin panel (Settings → Danger Zone): "Rotate JWT Secret" button
  with a confirmation modal warning that all sessions will be
  invalidated; on success the acting admin is logged out immediately
- docker-compose.yml, .env.example, README, Helm chart (values.yaml,
  secret.yaml, deployment.yaml, NOTES.txt, README): document
  ENCRYPTION_KEY and its upgrade migration path
2026-04-01 07:57:55 +02:00
jubnl dfdd473eca fix: validate uploaded backup DB before restore
Before swapping in a restored database, run PRAGMA integrity_check and
verify the five core TREK tables (users, trips, trip_members, places,
days) are present. This blocks restoring corrupt, empty, or unrelated
SQLite files that would otherwise crash the app immediately after swap,
and prevents a malicious admin from hot-swapping a crafted database with
forged users or permissions.
2026-04-01 07:57:42 +02:00
jubnl b515880adb fix: encrypt Immich API key at rest using AES-256-GCM
Per-user Immich API keys were stored as plaintext in the users table,
giving any attacker with DB read access full control over each user's
Immich photo server. Keys are now encrypted on write with
maybe_encrypt_api_key() and decrypted at the point of use via a shared
getImmichCredentials() helper. A new migration (index 66) back-fills
encryption for any existing plaintext values on startup.
2026-04-01 07:57:29 +02:00
jubnl 78695b4e03 fix: replace JWT tokens in URL query params with short-lived ephemeral tokens
Addresses CWE-598: long-lived JWTs were exposed in WebSocket URLs, file
download links, and Immich asset proxy URLs, leaking into server logs,
browser history, and Referer headers.

- Add ephemeralTokens service: in-memory single-use tokens with per-purpose
  TTLs (ws=30s, download/immich=60s), max 10k entries, periodic cleanup
- Add POST /api/auth/ws-token and POST /api/auth/resource-token endpoints
- WebSocket auth now consumes an ephemeral token instead of verifying the JWT
  directly from the URL; client fetches a fresh token before each connect
- File download ?token= query param now accepts ephemeral tokens; Bearer
  header path continues to accept JWTs for programmatic access
- Immich asset proxy replaces authFromQuery JWT injection with ephemeral token
  consumption
- Client: new getAuthUrl() utility, AuthedImg/ImmichImg components, and async
  onClick handlers replace the synchronous authUrl() pattern throughout
  FileManager, PlaceInspector, and MemoriesPanel
- Add OIDC_DISCOVERY_URL env var and oidc_discovery_url DB setting to allow
  overriding the auto-constructed discovery endpoint (required for Authentik
  and similar providers); exposed in the admin UI and .env.example
2026-04-01 07:57:14 +02:00
jubnl 0ee53e7b38 fix: prevent OIDC redirect URI construction from untrusted X-Forwarded-Host
The OIDC login route silently fell back to building the redirect URI from
X-Forwarded-Host/X-Forwarded-Proto when APP_URL was not configured. An
attacker could set X-Forwarded-Host: attacker.example.com to redirect the
authorization code to their own server after the user authenticates.

Remove the header-derived fallback entirely. If APP_URL is not set (via env
or the app_url DB setting), the OIDC login endpoint now returns a 500 error
rather than trusting attacker-controlled request headers. Document APP_URL
in .env.example as required for OIDC use.
2026-04-01 07:56:55 +02:00
jubnl 1b28bd96d4 fix: encrypt SMTP password at rest using AES-256-GCM
The smtp_pass setting was stored as plaintext in app_settings, exposing
SMTP credentials to anyone with database read access. Apply the same
encrypt_api_key/decrypt_api_key pattern already used for OIDC client
secrets and API keys. A new migration transparently re-encrypts any
existing plaintext value on startup; decrypt_api_key handles legacy
plaintext gracefully so in-flight reads remain safe during upgrade.
2026-04-01 07:56:43 +02:00
jubnl bba50f038b fix: encrypt OIDC client secret at rest using AES-256-GCM
The oidc_client_secret was written to app_settings as plaintext,
unlike Maps and OpenWeather API keys which are protected with
apiKeyCrypto. An attacker with read access to the SQLite file
(e.g. via a backup download) could obtain the secret and
impersonate the application with the identity provider.

- Encrypt on write in PUT /api/admin/oidc via maybe_encrypt_api_key
- Decrypt on read in GET /api/admin/oidc and in getOidcConfig()
  (oidc.ts) before passing the secret to the OIDC client library
- Add a startup migration that encrypts any existing plaintext value
  already present in the database
2026-04-01 07:56:29 +02:00
jubnl c9341eda3f fix: remove RCE vector from admin update endpoint.
The POST /api/admin/update endpoint ran git pull, npm install, and npm run build via execSync, potentially giving any compromised admin account full code execution on the host in case repository is compromised. TREK ships as a Docker image so runtime self-updating is unnecessary.
- Remove the /update route and child_process import from admin.ts
- Remove the installUpdate API client method
- Replace the live-update modal with an info-only panel showing docker pull instructions and a link to the GitHub release
- Drop the updating/updateResult state and handleInstallUpdate handler
2026-04-01 07:55:34 +02:00
Gérnyi Márk 1fbc19ad4f fix: add missing permission checks to file routes and map context menu
- Add checkPermission to 6 unprotected file endpoints (star, restore,
  permanent delete, empty trash, link, unlink)
- Gate map right-click place creation with place_edit permission
- Use file_upload permission for collab note file uploads
2026-03-31 23:45:11 +02:00
Gérnyi Márk 23edfe3dfc fix: harden permissions system after code review
- Gate permissions in /app-config behind optionalAuth so unauthenticated
  requests don't receive admin configuration
- Fix trip_delete isMember parameter (was hardcoded false)
- Return skipped keys from savePermissions for admin visibility
- Add disabled prop to CustomSelect, use in BudgetPanel currency picker
- Fix CollabChat reaction handler returning false instead of void
- Pass canUploadFiles as prop to NoteFormModal instead of internal store read
- Make edit-only NoteFormModal props optional (onDeleteFile, note, tripId)
- Add missing trailing newlines to .gitignore and it.ts
2026-03-31 23:36:17 +02:00
Gérnyi Márk c1bce755ca refactor: dedupe database requests 2026-03-31 23:36:15 +02:00
Gérnyi Márk 7d3b37a2a3 feat: add configurable permissions system with admin panel
Adds a full permissions management feature allowing admins to control
who can perform actions across the app (trip CRUD, files, places,
budget, packing, reservations, collab, members, share links).

- New server/src/services/permissions.ts: 16 configurable actions,
  in-memory cache, checkPermission() helper, backwards-compatible
  defaults matching upstream behaviour
- GET/PUT /admin/permissions endpoints; permissions loaded into
  app-config response so clients have them on startup
- checkPermission() applied to all mutating route handlers across
  10 server route files; getTripOwnerId() helper eliminates repeated
  inline DB queries; trips.ts and files.ts now reuse canAccessTrip()
  result to avoid redundant DB round-trips
- New client/src/store/permissionsStore.ts: Zustand store +
  useCanDo() hook; TripOwnerContext type accepts both Trip and
  DashboardTrip shapes without casting at call sites
- New client/src/components/Admin/PermissionsPanel.tsx: categorised
  UI with per-action dropdowns, customised badge, save/reset
- AdminPage, DashboardPage, FileManager, PlacesSidebar,
  TripMembersModal gated via useCanDo(); no prop drilling
- 46 perm.* translation keys added to all 12 language files
2026-03-31 23:36:15 +02:00
Maurice 3444e3f446 Merge branch 'perf-test' of https://github.com/jubnl/TREK into dev
# Conflicts:
#	client/src/components/Map/MapView.tsx
2026-03-31 23:10:02 +02:00
Maurice 9e3ac1e490 fix: increase max trip duration from 90 to 365 days 2026-03-31 22:58:27 +02:00
Maurice 36cd2feca5 fix: use Nominatim reverse geocoding for accurate country detection in atlas
Bounding boxes overlap for neighboring countries (e.g. Munich matched
Austria instead of Germany). Now uses Nominatim reverse geocoding with
in-memory cache as primary fallback, bounding boxes only as last resort.
2026-03-31 21:58:20 +02:00
Maurice fbe3b5b17e Merge pull request #225 from andreibrebene/improvements/various-improvements
Improvements/various improvements
2026-03-31 21:40:26 +02:00
Maurice 10107ecf31 fix: require auth for file downloads, localize atlas search, use flag images
- Block direct access to /uploads/files (401), serve via authenticated
  /api/trips/:tripId/files/:id/download with JWT verification
- Client passes auth token as query parameter for direct links
- Atlas country search now uses Intl.DisplayNames (user language) instead
  of English GeoJSON names
- Atlas search results use flagcdn.com flag images instead of emoji
2026-03-31 21:38:16 +02:00
Andrei Brebene 7522f396e7 feat: configurable trip reminders, admin full access, and enhanced audit logging
- Add configurable trip reminder days (1, 3, 9 or custom up to 30) settable by trip owner
- Grant administrators full access to edit, archive, delete, view and list all trips
- Show trip owner email in audit logs and docker logs when admin edits/deletes another user's trip
- Show target user email in audit logs when admin edits or deletes a user account
- Use email instead of username in all notifications (Discord/Slack/email) to avoid ambiguity
- Grey out notification event toggles when no SMTP/webhook is configured
- Grey out trip reminder selector when notifications are disabled
- Skip local admin account creation when OIDC_ONLY=true with OIDC configured
- Conditional scheduler logging: show disabled reason or active reminder count
- Log per-owner reminder creation/update in docker logs
- Demote 401/403 HTTP errors to DEBUG log level to reduce noise
- Hide edit/archive/delete buttons for non-owner invited users on trip cards
- Fix literal "0" rendering on trip cards from SQLite numeric is_owner field
- Add missing translation keys across all 14 language files

Made-with: Cursor
2026-03-31 22:23:38 +03:00
Andrei Brebene 9b2f083e4b feat: notifications, audit logging, and admin improvements
- Add centralized notification service with webhook (Discord/Slack) and
  email (SMTP) support, triggered for trip invites, booking changes,
  collab messages, and trip reminders
- Webhook sends one message per event (group channel); email sends
  individually per trip member, excluding the actor
- Discord invite notifications now include the invited user's name
- Add LOG_LEVEL env var (info/debug) controlling console and file output
- INFO logs show user email, action, and IP for audit events; errors
  for HTTP requests
- DEBUG logs show every request with full body/query (passwords redacted),
  audit details, notification params, and webhook payloads
- Add persistent trek.log file logging with 10MB rotation (5 files)
  in /app/data/logs/
- Color-coded log levels in Docker console output
- Timestamps without timezone name (user sets TZ via Docker)
- Add Test Webhook and Save buttons to admin notification settings
- Move notification event toggles to admin panel
- Add daily trip reminder scheduler (9 AM, timezone-aware)
- Wire up booking create/update/delete and collab message notifications
- Add i18n keys for notification UI across all 13 languages

Made-with: Cursor
2026-03-31 22:23:23 +03:00
jubnl 9a949d7391 Performance on trip planner (Maybe ?) 2026-03-31 21:13:29 +02:00
Maurice f7160e6dec Merge pull request #179 from shanelord01/audit/remediation-clean
Automated Security & Quality Audit via Claude Code
2026-03-31 20:53:48 +02:00