diff --git a/.env.example b/.env.example index f33d0c2..15c8495 100644 --- a/.env.example +++ b/.env.example @@ -87,9 +87,9 @@ JWT_SECRET=your_jwt_secret_here # The frontend URL that is allowed to make requests to the backend # IMPORTANT: Must match your frontend URL exactly! # Development: http://localhost:5173 (Vite dev server) -# Production: https://dashboard.yourdomain.com +# Production: https://start.feuerwehr-rems.at # Multiple origins: Use comma-separated values (if supported by your setup) -CORS_ORIGIN=http://localhost:80 +CORS_ORIGIN=https://start.feuerwehr-rems.at # ============================================================================ # FRONTEND CONFIGURATION @@ -103,16 +103,16 @@ FRONTEND_PORT=80 # API URL for frontend # The URL where the frontend will send API requests # Development: http://localhost:3000 -# Production: https://api.yourdomain.com +# Production: https://start.feuerwehr-rems.at (proxied via nginx /api/) # IMPORTANT: Must be accessible from the user's browser! -VITE_API_URL=http://localhost:3000 +VITE_API_URL=https://start.feuerwehr-rems.at # Authentik URL for frontend # The base URL of your Authentik instance (without application path) # Development: http://localhost:9000 -# Production: https://auth.yourdomain.com +# Production: https://auth.firesuite.feuerwehr-rems.at # IMPORTANT: Used for OAuth redirect URL construction -VITE_AUTHENTIK_URL=https://auth.yourdomain.com +VITE_AUTHENTIK_URL=https://auth.firesuite.feuerwehr-rems.at # ============================================================================ # AUTHENTIK OAUTH CONFIGURATION @@ -133,18 +133,18 @@ AUTHENTIK_CLIENT_SECRET=your_client_secret_here # OAuth Issuer URL # From Authentik: Applications → Providers → Your Provider → OpenID Configuration -# Format: https://auth.yourdomain.com/application/o/your-app-slug/ +# Format: https://auth.firesuite.feuerwehr-rems.at/application/o/your-app-slug/ # IMPORTANT: Must end with a trailing slash (/) # Development: http://localhost:9000/application/o/feuerwehr-dashboard/ -# Production: https://auth.yourdomain.com/application/o/feuerwehr-dashboard/ -AUTHENTIK_ISSUER=https://auth.yourdomain.com/application/o/feuerwehr-dashboard/ +# Production: https://auth.firesuite.feuerwehr-rems.at/application/o/feuerwehr-dashboard/ +AUTHENTIK_ISSUER=https://auth.firesuite.feuerwehr-rems.at/application/o/feuerwehr-dashboard/ # OAuth Redirect URI # The URL where Authentik will redirect after successful authentication # Must match EXACTLY what you configured in Authentik # Development: http://localhost:5173/auth/callback -# Production: https://dashboard.yourdomain.com/auth/callback -AUTHENTIK_REDIRECT_URI=https://dashboard.yourdomain.com/auth/callback +# Production: https://start.feuerwehr-rems.at/auth/callback +AUTHENTIK_REDIRECT_URI=https://start.feuerwehr-rems.at/auth/callback # OAuth Scopes (optional, has defaults) # Default: openid profile email @@ -227,13 +227,13 @@ AUTHENTIK_REDIRECT_URI=https://dashboard.yourdomain.com/auth/callback # BACKEND_PORT=3000 # NODE_ENV=production # JWT_SECRET= -# CORS_ORIGIN=https://dashboard.yourdomain.com +# CORS_ORIGIN=https://start.feuerwehr-rems.at # FRONTEND_PORT=80 -# VITE_API_URL=https://api.yourdomain.com +# VITE_API_URL=https://start.feuerwehr-rems.at # AUTHENTIK_CLIENT_ID= # AUTHENTIK_CLIENT_SECRET= -# AUTHENTIK_ISSUER=https://auth.yourdomain.com/application/o/feuerwehr-dashboard/ -# AUTHENTIK_REDIRECT_URI=https://dashboard.yourdomain.com/auth/callback +# AUTHENTIK_ISSUER=https://auth.firesuite.feuerwehr-rems.at/application/o/feuerwehr-dashboard/ +# AUTHENTIK_REDIRECT_URI=https://start.feuerwehr-rems.at/auth/callback # LOG_LEVEL=info # # ============================================================================ diff --git a/API_DOCUMENTATION.md b/API_DOCUMENTATION.md index 0c30e1d..aa00a35 100644 --- a/API_DOCUMENTATION.md +++ b/API_DOCUMENTATION.md @@ -23,7 +23,7 @@ http://localhost:3000 ### Production ``` -https://api.yourdomain.com +https://start.feuerwehr-rems.at ``` ## Authentication @@ -155,7 +155,7 @@ Check if the API is running and healthy. **Request**: ```http GET /health HTTP/1.1 -Host: api.yourdomain.com +Host: start.feuerwehr-rems.at ``` **Response**: @@ -197,7 +197,7 @@ Handle OAuth callback and exchange authorization code for tokens. **Request Example**: ```http POST /api/auth/callback HTTP/1.1 -Host: api.yourdomain.com +Host: start.feuerwehr-rems.at Content-Type: application/json { @@ -295,7 +295,7 @@ Refresh an expired access token using a refresh token. **Request Example**: ```http POST /api/auth/refresh HTTP/1.1 -Host: api.yourdomain.com +Host: start.feuerwehr-rems.at Content-Type: application/json { @@ -370,7 +370,7 @@ Authorization: Bearer **Request Example**: ```http POST /api/auth/logout HTTP/1.1 -Host: api.yourdomain.com +Host: start.feuerwehr-rems.at Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` @@ -407,7 +407,7 @@ Authorization: Bearer **Request Example**: ```http GET /api/user/me HTTP/1.1 -Host: api.yourdomain.com +Host: start.feuerwehr-rems.at Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` @@ -479,10 +479,10 @@ HTTP/1.1 404 Not Found Frontend redirects to Authentik: ```javascript -const authentikAuthUrl = `https://auth.yourdomain.com/application/o/authorize/`; +const authentikAuthUrl = `https://auth.firesuite.feuerwehr-rems.at/application/o/authorize/`; const params = new URLSearchParams({ client_id: 'your_client_id', - redirect_uri: 'https://dashboard.yourdomain.com/auth/callback', + redirect_uri: 'https://start.feuerwehr-rems.at/auth/callback', response_type: 'code', scope: 'openid profile email' }); @@ -494,13 +494,13 @@ window.location.href = `${authentikAuthUrl}?${params}`; After authentication, Authentik redirects to: ``` -https://dashboard.yourdomain.com/auth/callback?code=abc123def456 +https://start.feuerwehr-rems.at/auth/callback?code=abc123def456 ``` #### Step 3: Exchange Code for Tokens ```bash -curl -X POST https://api.yourdomain.com/api/auth/callback \ +curl -X POST https://start.feuerwehr-rems.at/api/auth/callback \ -H "Content-Type: application/json" \ -d '{ "code": "abc123def456" @@ -532,14 +532,14 @@ Response: #### Step 4: Access Protected Resources ```bash -curl -X GET https://api.yourdomain.com/api/user/me \ +curl -X GET https://start.feuerwehr-rems.at/api/user/me \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` #### Step 5: Refresh Token When Expired ```bash -curl -X POST https://api.yourdomain.com/api/auth/refresh \ +curl -X POST https://start.feuerwehr-rems.at/api/auth/refresh \ -H "Content-Type: application/json" \ -d '{ "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." @@ -553,7 +553,7 @@ curl -X POST https://api.yourdomain.com/api/auth/refresh \ ```typescript import axios from 'axios'; -const API_URL = 'https://api.yourdomain.com'; +const API_URL = 'https://start.feuerwehr-rems.at'; // Create axios instance with auth const api = axios.create({ @@ -612,32 +612,32 @@ export const logout = async () => { #### Health Check ```bash -curl -X GET https://api.yourdomain.com/health +curl -X GET https://start.feuerwehr-rems.at/health ``` #### Login Callback ```bash -curl -X POST https://api.yourdomain.com/api/auth/callback \ +curl -X POST https://start.feuerwehr-rems.at/api/auth/callback \ -H "Content-Type: application/json" \ -d '{"code":"your_auth_code"}' ``` #### Get Current User ```bash -curl -X GET https://api.yourdomain.com/api/user/me \ +curl -X GET https://start.feuerwehr-rems.at/api/user/me \ -H "Authorization: Bearer your_access_token" ``` #### Refresh Token ```bash -curl -X POST https://api.yourdomain.com/api/auth/refresh \ +curl -X POST https://start.feuerwehr-rems.at/api/auth/refresh \ -H "Content-Type: application/json" \ -d '{"refreshToken":"your_refresh_token"}' ``` #### Logout ```bash -curl -X POST https://api.yourdomain.com/api/auth/logout \ +curl -X POST https://start.feuerwehr-rems.at/api/auth/logout \ -H "Authorization: Bearer your_access_token" ``` @@ -660,7 +660,7 @@ The API is configured to only accept requests from allowed origins: ```javascript // Backend CORS configuration cors({ - origin: process.env.CORS_ORIGIN, // e.g., https://dashboard.yourdomain.com + origin: process.env.CORS_ORIGIN, // e.g., https://start.feuerwehr-rems.at credentials: true }) ``` diff --git a/AUTHENTIK_SETUP.md b/AUTHENTIK_SETUP.md index 67f054e..48c033a 100644 --- a/AUTHENTIK_SETUP.md +++ b/AUTHENTIK_SETUP.md @@ -21,8 +21,8 @@ Before you begin, you need: - An Authentik instance (self-hosted or cloud) - Admin access to Authentik -- Your Feuerwehr Dashboard URL (e.g., `https://dashboard.yourdomain.com`) -- Your backend API URL (e.g., `https://api.yourdomain.com`) +- Your Feuerwehr Dashboard URL (e.g., `https://start.feuerwehr-rems.at`) +- Your backend API URL (e.g., `https://start.feuerwehr-rems.at`) ## Authentik Installation @@ -146,7 +146,7 @@ Protocol Settings: ``` http://localhost:5173/auth/callback http://localhost/auth/callback -https://dashboard.yourdomain.com/auth/callback +https://start.feuerwehr-rems.at/auth/callback ``` Add one URI per line. Include all environments (development, staging, production). @@ -173,7 +173,7 @@ Configure the application: Name: Feuerwehr Dashboard Slug: feuerwehr-dashboard Provider: Feuerwehr Dashboard Provider (select from dropdown) -Launch URL: https://dashboard.yourdomain.com +Launch URL: https://start.feuerwehr-rems.at ``` **UI Settings** (optional): @@ -256,10 +256,10 @@ This is the Vite dev server URL. ### Production Environment ``` -https://dashboard.yourdomain.com/auth/callback +https://start.feuerwehr-rems.at/auth/callback ``` -Replace `yourdomain.com` with your actual domain. +Replace `feuerwehr-rems.at` with your actual domain. ### Docker Local Testing @@ -317,11 +317,11 @@ const scopes = 'openid profile email'; 1. In the provider details, find **OpenID Configuration URL**: ``` - https://auth.yourdomain.com/application/o/feuerwehr-dashboard/.well-known/openid-configuration + https://auth.firesuite.feuerwehr-rems.at/application/o/feuerwehr-dashboard/.well-known/openid-configuration ``` 2. Important URLs from this configuration: - - **Issuer**: `https://auth.yourdomain.com/application/o/feuerwehr-dashboard/` + - **Issuer**: `https://auth.firesuite.feuerwehr-rems.at/application/o/feuerwehr-dashboard/` - **Authorization Endpoint**: Auto-discovered - **Token Endpoint**: Auto-discovered - **Userinfo Endpoint**: Auto-discovered @@ -334,8 +334,8 @@ Update your Feuerwehr Dashboard `.env` file: # Authentik OAuth Configuration AUTHENTIK_CLIENT_ID= AUTHENTIK_CLIENT_SECRET= -AUTHENTIK_ISSUER=https://auth.yourdomain.com/application/o/feuerwehr-dashboard/ -AUTHENTIK_REDIRECT_URI=https://dashboard.yourdomain.com/auth/callback +AUTHENTIK_ISSUER=https://auth.firesuite.feuerwehr-rems.at/application/o/feuerwehr-dashboard/ +AUTHENTIK_REDIRECT_URI=https://start.feuerwehr-rems.at/auth/callback # For development, use: # AUTHENTIK_ISSUER=http://localhost:9000/application/o/feuerwehr-dashboard/ @@ -361,7 +361,7 @@ AUTHENTIK_REDIRECT_URI=https://dashboard.yourdomain.com/auth/callback 2. **Open the dashboard** in your browser: ``` Development: http://localhost:5173 - Production: https://dashboard.yourdomain.com + Production: https://start.feuerwehr-rems.at ``` 3. **Click "Login" button** @@ -441,7 +441,7 @@ In the dashboard: **Solution**: 1. Ensure `CORS_ORIGIN` in backend `.env` matches frontend URL 2. For development: `CORS_ORIGIN=http://localhost:5173` -3. For production: `CORS_ORIGIN=https://dashboard.yourdomain.com` +3. For production: `CORS_ORIGIN=https://start.feuerwehr-rems.at` 4. Restart backend after changing CORS settings ### Issue 4: Token Validation Failed @@ -561,7 +561,7 @@ After configuration, verify: Client Type: Confidential Client ID: Client Secret: -Redirect URIs: https://dashboard.yourdomain.com/auth/callback +Redirect URIs: https://start.feuerwehr-rems.at/auth/callback Scopes: openid, profile, email Access Token Validity: 3600 Refresh Token Validity: 86400 @@ -571,8 +571,8 @@ Refresh Token Validity: 86400 ```bash AUTHENTIK_CLIENT_ID= AUTHENTIK_CLIENT_SECRET= -AUTHENTIK_ISSUER=https://auth.yourdomain.com/application/o/feuerwehr-dashboard/ -AUTHENTIK_REDIRECT_URI=https://dashboard.yourdomain.com/auth/callback +AUTHENTIK_ISSUER=https://auth.firesuite.feuerwehr-rems.at/application/o/feuerwehr-dashboard/ +AUTHENTIK_REDIRECT_URI=https://start.feuerwehr-rems.at/auth/callback ``` ## Security Best Practices diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index b4f00c8..e7f8591 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -154,19 +154,19 @@ NODE_ENV=production JWT_SECRET= # CORS - Set to your domain! -CORS_ORIGIN=https://dashboard.yourdomain.com +CORS_ORIGIN=https://start.feuerwehr-rems.at # Frontend FRONTEND_PORT=80 # API URL - Set to your backend URL -VITE_API_URL=https://api.yourdomain.com +VITE_API_URL=https://start.feuerwehr-rems.at # Authentik OAuth (from Authentik setup) AUTHENTIK_CLIENT_ID= AUTHENTIK_CLIENT_SECRET= -AUTHENTIK_ISSUER=https://auth.yourdomain.com/application/o/feuerwehr/ -AUTHENTIK_REDIRECT_URI=https://dashboard.yourdomain.com/auth/callback +AUTHENTIK_ISSUER=https://auth.firesuite.feuerwehr-rems.at/application/o/feuerwehr/ +AUTHENTIK_REDIRECT_URI=https://start.feuerwehr-rems.at/auth/callback ``` Secure the .env file: @@ -253,7 +253,7 @@ Key points for production: Create `Caddyfile`: ```caddy -dashboard.yourdomain.com { +start.feuerwehr-rems.at { reverse_proxy localhost:80 encode gzip @@ -265,7 +265,7 @@ dashboard.yourdomain.com { } } -api.yourdomain.com { +start.feuerwehr-rems.at { reverse_proxy localhost:3000 encode gzip } @@ -299,7 +299,7 @@ Create Nginx configuration (`/etc/nginx/sites-available/feuerwehr`): ```nginx server { listen 80; - server_name dashboard.yourdomain.com; + server_name start.feuerwehr-rems.at; location / { proxy_pass http://localhost:80; @@ -312,7 +312,7 @@ server { server { listen 80; - server_name api.yourdomain.com; + server_name start.feuerwehr-rems.at; location / { proxy_pass http://localhost:3000; @@ -330,7 +330,7 @@ Enable and obtain SSL: sudo ln -s /etc/nginx/sites-available/feuerwehr /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx -sudo certbot --nginx -d dashboard.yourdomain.com -d api.yourdomain.com +sudo certbot --nginx -d start.feuerwehr-rems.at -d start.feuerwehr-rems.at ``` ### Option 3: Using Docker with Traefik diff --git a/DOCKER_QUICK_REF.md b/DOCKER_QUICK_REF.md index d0f3838..45c1fff 100644 --- a/DOCKER_QUICK_REF.md +++ b/DOCKER_QUICK_REF.md @@ -217,8 +217,8 @@ docker run -p 80:80 feuerwehr-frontend:latest # 1. Set production environment variables export POSTGRES_PASSWORD="secure_production_password" export JWT_SECRET="secure_jwt_secret_min_32_chars" -export CORS_ORIGIN="https://yourdomain.com" -export VITE_API_URL="https://api.yourdomain.com" +export CORS_ORIGIN="https://feuerwehr-rems.at" +export VITE_API_URL="https://start.feuerwehr-rems.at" # 2. Build and start docker-compose up -d diff --git a/backend/src/config/environment.ts b/backend/src/config/environment.ts index 1102a27..ac618c2 100644 --- a/backend/src/config/environment.ts +++ b/backend/src/config/environment.ts @@ -59,7 +59,7 @@ const environment: EnvironmentConfig = { max: parseInt(process.env.RATE_LIMIT_MAX || '100', 10), }, authentik: { - issuer: process.env.AUTHENTIK_ISSUER || 'https://authentik.yourdomain.com/application/o/your-app/', + issuer: process.env.AUTHENTIK_ISSUER || 'https://auth.firesuite.feuerwehr-rems.at/application/o/feuerwehr-dashboard/', clientId: process.env.AUTHENTIK_CLIENT_ID || 'your_client_id_here', clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || 'your_client_secret_here', redirectUri: process.env.AUTHENTIK_REDIRECT_URI || 'http://localhost:5173/auth/callback', diff --git a/docker-compose.yml b/docker-compose.yml index 18e4ac0..32ff5d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,11 +36,11 @@ services: DB_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required} JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-24h} - CORS_ORIGIN: ${CORS_ORIGIN:-http://localhost:80} + CORS_ORIGIN: ${CORS_ORIGIN:-https://start.feuerwehr-rems.at} AUTHENTIK_ISSUER: ${AUTHENTIK_ISSUER:?AUTHENTIK_ISSUER is required} AUTHENTIK_CLIENT_ID: ${AUTHENTIK_CLIENT_ID:?AUTHENTIK_CLIENT_ID is required} AUTHENTIK_CLIENT_SECRET: ${AUTHENTIK_CLIENT_SECRET:?AUTHENTIK_CLIENT_SECRET is required} - AUTHENTIK_REDIRECT_URI: ${AUTHENTIK_REDIRECT_URI:-http://localhost/auth/callback} + AUTHENTIK_REDIRECT_URI: ${AUTHENTIK_REDIRECT_URI:-https://start.feuerwehr-rems.at/auth/callback} ports: - "${BACKEND_PORT:-3000}:3000" depends_on: @@ -61,7 +61,7 @@ services: context: ./frontend dockerfile: Dockerfile args: - VITE_API_URL: ${VITE_API_URL:-http://localhost:3000} + VITE_API_URL: ${VITE_API_URL:-https://start.feuerwehr-rems.at} VITE_AUTHENTIK_URL: ${VITE_AUTHENTIK_URL:?VITE_AUTHENTIK_URL is required} VITE_CLIENT_ID: ${AUTHENTIK_CLIENT_ID:?AUTHENTIK_CLIENT_ID is required} container_name: feuerwehr_frontend_prod diff --git a/frontend/.env.development b/frontend/.env.development index ca095a9..167d9f7 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1,3 +1,3 @@ VITE_API_URL=http://localhost:3000 -VITE_AUTHENTIK_URL=https://authentik.yourdomain.com +VITE_AUTHENTIK_URL=https://auth.firesuite.feuerwehr-rems.at VITE_CLIENT_ID=your_client_id_here diff --git a/frontend/nginx.conf b/frontend/nginx.conf index bec42d6..6fc3704 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -65,6 +65,16 @@ http { add_header Content-Type text/plain; } + # Proxy API requests to backend + location /api/ { + proxy_pass http://backend:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; diff --git a/frontend/src/utils/config.ts b/frontend/src/utils/config.ts index 23947e6..4dc8039 100644 --- a/frontend/src/utils/config.ts +++ b/frontend/src/utils/config.ts @@ -1,6 +1,6 @@ export const config = { apiUrl: import.meta.env.VITE_API_URL || 'http://localhost:3000', - authentikUrl: import.meta.env.VITE_AUTHENTIK_URL || 'https://authentik.yourdomain.com', + authentikUrl: import.meta.env.VITE_AUTHENTIK_URL || 'https://auth.firesuite.feuerwehr-rems.at', clientId: import.meta.env.VITE_CLIENT_ID || 'your_client_id_here', };