From 767ae238431b059316202eac1f914a9406268c1a Mon Sep 17 00:00:00 2001 From: Daniel Volz Date: Sun, 24 May 2026 13:36:01 +0200 Subject: [PATCH] docs: clarify dev hosts and deployment guidance --- .env.example | 5 +++++ .github/copilot-instructions.md | 25 ++++++------------------- .github/workflows/docker-build.yml | 2 ++ README.md | 17 +++++++++++++++-- docs/CONFIGURATION.md | 16 +++++++++++++--- docs/DEVELOPMENT.md | 13 +++++++++++-- frontend/vite.config.ts | 20 +++++++++++++++++++- 7 files changed, 71 insertions(+), 27 deletions(-) diff --git a/.env.example b/.env.example index 5edcc1c..7570db7 100644 --- a/.env.example +++ b/.env.example @@ -10,6 +10,8 @@ PUID=1000 PGID=1000 PORT=3000 +# Docker Compose quickstart serves the frontend on http://localhost:4174. +# Local Vite development usually uses http://localhost:5173 or http://localhost:4173 instead. CORS_ORIGINS=http://localhost:4174 # Server default timezone for scheduled reminders. @@ -18,8 +20,11 @@ TZ=Europe/Berlin # Public base URL used for notification action links. # Required for intake reminder action buttons. +# Use an externally reachable HTTPS URL for remote/self-hosted access. # PUBLIC_APP_URL=https://medassist.example.com # If this uses a non-local host, include that origin in CORS_ORIGINS. +# Local Vite development automatically allows this hostname; set +# VITE_ALLOWED_HOSTS only when you need additional development hostnames. # Log level: debug, info, warn, error, silent LOG_LEVEL=info diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index dfe7694..6bc23cc 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,26 +1,13 @@ # MedAssist-ng - Copilot Entry Point -## VERY IMPORTANT - Prioritized Constraints +This file is intentionally thin. `AGENTS.md` is the canonical governance file for this repository. -**First: Update Memory and Reports** -- Always keep agent work memory updated in `doku/memory_notes.md` so progress and decisions remain recoverable across context loss. - - If `doku/memory_notes.md` is missing, create it immediately. -- Always keep a user-facing work report updated in `doku/report.md` so completed work is easy to review. - - If `doku/report.md` is missing, create it immediately. -- This memory/report rule replaces the previous `doku/APP_BEHAVIOR.md` persistence requirement. - -**Second: Follow Governance Rules** -- Consult `AGENTS.md` for governance, workflow, and skill rules when that file exists in the workspace. - -When `AGENTS.md` exists in the workspace, use it as the single source of truth for governance, workflow, and skill rules. +If rules differ between files, follow `AGENTS.md`. ## Required Startup Steps 1. Read `AGENTS.md` first when it exists in the workspace. -2. If `AGENTS.md` exists, identify triggered skills from it and read each referenced `SKILL.md` before making changes. -3. Follow delegation boundaries exactly (`@testing-manager` for testing, `@release-manager` for release orchestration). -4. When work moves into a different thematic area, create or switch to a dedicated local branch or worktree before editing code, and reuse the same branch/worktree for follow-up work inside that same theme. - -## Scope - -This file intentionally stays minimal to prevent duplicated or conflicting instructions. +2. Ensure `doku/memory_notes.md` and `doku/report.md` exist and keep them updated during meaningful work. These files are local-only and must not be staged or committed unless explicitly requested. +3. Identify triggered skills from `AGENTS.md` and read only the matching `SKILL.md` files before making changes. +4. Follow delegation boundaries from `AGENTS.md`: `@testing-manager` for testing work and `@release-manager` for release orchestration, including the documented fallback protocol when a required specialist is unavailable. +5. Keep all non-canonical instruction files brief and aligned with `AGENTS.md`; do not duplicate full governance here. diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 2f741ab..cd0af90 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -24,6 +24,8 @@ on: concurrency: group: docker-build-${{ github.ref }} + # Cancel older runs on the same ref so the shared branch tag stays aligned + # with the newest commit instead of racing older builds against newer ones. cancel-in-progress: true # Default minimal permissions diff --git a/README.md b/README.md index ee40282..565c0bd 100644 --- a/README.md +++ b/README.md @@ -157,10 +157,13 @@ Share your medication schedule with others via a public link. ### Multi-Person Support - Manage medications for multiple people - Share schedules via link. Recipients can mark doses as taken, you see it live +- Optionally allow shared links to view and edit intake journal notes for their visible schedule window - Optionally embed the medication overview directly on shared links via a settings toggle ### Data Export & Import -- Export all your data (medications, dose history, settings) as JSON +- Export all your data (medications, dose history, intake journal notes, settings) as JSON +- Review validated import contents before replacing current data +- Optionally download a fresh backup before confirming import - Import previously exported data with automatic ID remapping - Choose whether to include sensitive data in exports @@ -188,6 +191,16 @@ docker compose -p medassist-ng up -d Open `http://localhost:4174` and start tracking your medications. +### Verify Deployment + +After the containers start, confirm the stack is actually healthy: + +1. Run `docker compose ps` and confirm the `backend` service is `healthy` and the `frontend` service is running. +2. Open `http://localhost:3000/health` and confirm the backend responds with JSON that includes `"status":"ok"`. +3. Open `http://localhost:4174` and confirm the app shell loads and can reach the API. + +If the frontend loads but API requests fail, check the backend health endpoint first and confirm `CORS_ORIGINS` includes the frontend origin you are using. If you plan to open reminder or share links from another device, set `PUBLIC_APP_URL` to the externally reachable app URL instead of relying on `localhost`. + # Configuration Configure the application with environment variables in `.env`. Keep the basic container settings in the README and use the dedicated docs for the full reference. @@ -206,7 +219,7 @@ Optional but commonly needed: | Variable | Default | Description | |----------|---------|-------------| -| `PUBLIC_APP_URL` | — | Public base URL for notification action links | +| `PUBLIC_APP_URL` | — | Public base URL for notification action and share links | Detailed configuration references: diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 997c527..d59ac65 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -9,9 +9,9 @@ Configure MedAssist with environment variables in `.env`. Start from `.env.examp | `PUID` | `1000` | User ID for container file permissions | | `PGID` | `1000` | Group ID for container file permissions | | `PORT` | `3000` | Backend API port | -| `CORS_ORIGINS` | `http://localhost:4174` | Allowed origins for CORS | +| `CORS_ORIGINS` | `http://localhost:4174` | Allowed origins for CORS in the Docker Compose quickstart; local Vite development commonly uses `http://localhost:5173` or `http://localhost:4173` | | `TZ` | `Europe/Berlin` | Server default timezone for scheduled reminders | -| `PUBLIC_APP_URL` | — | Public base URL for notification action links. Must be reachable by phones, browsers, and notification providers; do not point this to `localhost` or an internal Docker hostname. | +| `PUBLIC_APP_URL` | — | Public base URL for notification action and share links. Strongly recommended for any deployment used from another device; do not point this to `localhost` or an internal Docker hostname. Local Vite development also allows this hostname automatically. | | `LOG_LEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error`, or `silent` | | `RATE_LIMIT_MAX` | `100` | Maximum requests per minute per IP | | `OPENAPI_DOCS_ENABLED` | `auto` | Explicitly enable or disable `/docs` and `/docs/json` | @@ -22,6 +22,12 @@ API docs behavior: - `OPENAPI_DOCS_ENABLED=true` enables `/docs` and `/docs/json`. - `OPENAPI_DOCS_ENABLED=false` disables the docs only. +`CORS_ORIGINS` note: + +- The `.env.example` file is optimized for the Docker Compose quickstart, where the frontend runs on `http://localhost:4174`. +- Local frontend development uses the Vite dev server instead, so the backend schema defaults cover `http://localhost:5173` and `http://localhost:4173`. +- If you use a custom hostname or reverse proxy, include that origin in `CORS_ORIGINS`. + ## Authentication | Variable | Default | Description | @@ -102,13 +108,17 @@ API reference: Reminder timing uses IANA timezones. `TZ` is the server default. Users can override it in Settings. +These values are runtime defaults. User-specific settings can override reminder behavior after first save. + ## Push Notifications Push notification setup, provider support, and URL examples are documented in [PUSH_NOTIFICATIONS.md](PUSH_NOTIFICATIONS.md). Recommended provider: `ntfy`, especially for intake reminders with direct actions. -Notification action links use `PUBLIC_APP_URL` as their base URL. For self-hosted setups, this should normally be your externally reachable HTTPS address, for example `https://med.example.com`. +Notification action and share links should use `PUBLIC_APP_URL` as their reachable base URL. For self-hosted setups, this should normally be your externally reachable HTTPS address, for example `https://med.example.com`. + +If `PUBLIC_APP_URL` is missing in a remote deployment, reminder links can still be generated from local origins that are unreachable from phones or external browsers. ## Default User Settings diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index fb1a948..c9148eb 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -19,8 +19,17 @@ If the frontend dev server runs behind a reverse proxy or on a remote host, set These development overrides are documented here intentionally and are not part of the standard operator-focused `.env.example` surface. +## API Proxy Contract + +- Frontend browser code should call `/api/*`, not hardcoded backend hostnames. +- Vite rewrites `/api/*` to the backend target configured by `BACKEND_URL` or the built-in default for the current environment. +- Default backend target: + - local dev outside Docker: `http://localhost:3000` + - dev stack inside Docker: `http://backend-dev:3000` +- If your backend runs on a different host or service name, set `BACKEND_URL` explicitly before starting Vite. + - `BACKEND_URL`: backend target used by the Vite `/api` proxy; default `http://localhost:3000` outside Docker and `http://backend-dev:3000` in Docker -- `VITE_ALLOWED_HOSTS`: comma-separated hostnames allowed to connect to the dev server; default `localhost,127.0.0.1` +- `VITE_ALLOWED_HOSTS`: comma-separated hostnames allowed to connect to the dev server; default `localhost,127.0.0.1` plus the hostname from `PUBLIC_APP_URL` when configured - `VITE_HMR_HOST`: public hostname for HMR websocket connections - `VITE_HMR_PROTOCOL`: websocket protocol override (`ws` or `wss`) - `VITE_HMR_CLIENT_PORT`: public websocket port exposed to the browser @@ -43,4 +52,4 @@ npm run check npm run build ``` -Use the root-level commands for full-stack validation when a change spans backend and frontend. Keep using the package-local commands when you are validating only one slice. \ No newline at end of file +Use the root-level commands for full-stack validation when a change spans backend and frontend. Keep using the package-local commands when you are validating only one slice. diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 56edafa..bf0a6cd 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -20,6 +20,22 @@ function parseOptionalPort(value: string | undefined) { return Number.isFinite(parsed) ? parsed : undefined; } +function parseUrlHostname(value: string | undefined) { + if (!value) { + return undefined; + } + + try { + return new URL(value).hostname; + } catch { + return undefined; + } +} + +function uniqueHosts(hosts: Array) { + return [...new Set(hosts.filter((host): host is string => Boolean(host)))]; +} + // Read version from package.json at build time const packageJson = JSON.parse(readFileSync("./package.json", "utf-8")); @@ -27,7 +43,9 @@ const packageJson = JSON.parse(readFileSync("./package.json", "utf-8")); // In Docker, prefer backend-dev to avoid localhost proxy failures. const defaultBackendTarget = existsSync("/.dockerenv") ? "http://backend-dev:3000" : "http://localhost:3000"; const backendTarget = process.env.BACKEND_URL || defaultBackendTarget; -const allowedHosts = parseCsvEnv(process.env.VITE_ALLOWED_HOSTS, ["localhost", "127.0.0.1"]); +const configuredAllowedHosts = parseCsvEnv(process.env.VITE_ALLOWED_HOSTS, []); +const baseAllowedHosts = configuredAllowedHosts.length > 0 ? configuredAllowedHosts : ["localhost", "127.0.0.1"]; +const allowedHosts = uniqueHosts([...baseAllowedHosts, parseUrlHostname(process.env.PUBLIC_APP_URL)]); const hmrHost = process.env.VITE_HMR_HOST?.trim(); const hmrProtocol = process.env.VITE_HMR_PROTOCOL === "ws" ? "ws" : process.env.VITE_HMR_PROTOCOL === "wss" ? "wss" : undefined; const hmrClientPort = parseOptionalPort(process.env.VITE_HMR_CLIENT_PORT);