Update to production Let's Encrypt certificates
This commit is contained in:
@@ -25,7 +25,7 @@ RUN npm run build
|
||||
FROM node:20-alpine AS production
|
||||
|
||||
# Install runtime dependencies only
|
||||
RUN apk add --no-cache dumb-init
|
||||
RUN apk add --no-cache dumb-init curl
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
@@ -29,10 +29,13 @@ certificatesResolvers:
|
||||
acme:
|
||||
email: admin@motovaultpro.com
|
||||
storage: /data/acme.json
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
# Use staging for development
|
||||
caServer: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
dnsChallenge:
|
||||
provider: cloudflare
|
||||
delayBeforeCheck: 10
|
||||
resolvers:
|
||||
- "1.1.1.1:53"
|
||||
- "8.8.8.8:53"
|
||||
# Production Let's Encrypt (no caServer = production by default)
|
||||
|
||||
# TLS configuration for local development
|
||||
tls:
|
||||
|
||||
@@ -6,6 +6,8 @@ services:
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --configFile=/etc/traefik/traefik.yml
|
||||
environment:
|
||||
CF_DNS_API_TOKEN_FILE: /run/secrets/cloudflare-dns-token
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
@@ -16,6 +18,7 @@ services:
|
||||
- ./config/traefik/middleware.yml:/etc/traefik/middleware.yml:ro
|
||||
- ./certs:/certs:ro
|
||||
- traefik_data:/data
|
||||
- ./secrets/app/cloudflare-dns-token.txt:/run/secrets/cloudflare-dns-token:ro
|
||||
networks:
|
||||
frontend:
|
||||
ipv4_address: 10.96.1.50
|
||||
@@ -73,6 +76,7 @@ services:
|
||||
- "traefik.http.routers.mvp-frontend.rule=(Host(`motovaultpro.com`) || Host(`www.motovaultpro.com`)) && !PathPrefix(`/api`)"
|
||||
- "traefik.http.routers.mvp-frontend.entrypoints=websecure"
|
||||
- "traefik.http.routers.mvp-frontend.tls=true"
|
||||
- "traefik.http.routers.mvp-frontend.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.mvp-frontend.priority=10"
|
||||
- "traefik.http.services.mvp-frontend.loadbalancer.server.port=3000"
|
||||
- "traefik.http.services.mvp-frontend.loadbalancer.healthcheck.path=/"
|
||||
@@ -128,11 +132,13 @@ services:
|
||||
- "traefik.http.routers.mvp-backend.rule=(Host(`motovaultpro.com`) || Host(`www.motovaultpro.com`)) && PathPrefix(`/api`)"
|
||||
- "traefik.http.routers.mvp-backend.entrypoints=websecure"
|
||||
- "traefik.http.routers.mvp-backend.tls=true"
|
||||
- "traefik.http.routers.mvp-backend.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.mvp-backend.priority=20"
|
||||
# Health check router (bypass auth)
|
||||
- "traefik.http.routers.mvp-backend-health.rule=(Host(`motovaultpro.com`) || Host(`www.motovaultpro.com`)) && Path(`/api/health`)"
|
||||
- "traefik.http.routers.mvp-backend-health.entrypoints=websecure"
|
||||
- "traefik.http.routers.mvp-backend-health.tls=true"
|
||||
- "traefik.http.routers.mvp-backend-health.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.mvp-backend-health.priority=30"
|
||||
# Service configuration
|
||||
- "traefik.http.services.mvp-backend.loadbalancer.server.port=3001"
|
||||
|
||||
@@ -86,6 +86,7 @@ These variables use GitLab's **File** type, which writes the value to a temporar
|
||||
| `AUTH0_CLIENT_SECRET` | File | Yes | Yes | Auth0 client secret for backend |
|
||||
| `GOOGLE_MAPS_API_KEY` | File | Yes | Yes | Google Maps API key |
|
||||
| `GOOGLE_MAPS_MAP_ID` | File | Yes | No | Google Maps Map ID |
|
||||
| `CF_DNS_API_TOKEN` | File | Yes | Yes | Cloudflare API token for Let's Encrypt DNS challenge |
|
||||
|
||||
### Configuration Variables
|
||||
|
||||
@@ -97,6 +98,20 @@ These variables use GitLab's **File** type, which writes the value to a temporar
|
||||
|
||||
Note: `DEPLOY_PATH` is automatically set in `.gitlab-ci.yml` using `GIT_CLONE_PATH` for a stable path.
|
||||
|
||||
### Creating Cloudflare API Token
|
||||
|
||||
The `CF_DNS_API_TOKEN` is required for automatic SSL certificate generation via Let's Encrypt DNS-01 challenge.
|
||||
|
||||
1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com/profile/api-tokens)
|
||||
2. Click **Create Token**
|
||||
3. Use template: **Edit zone DNS**
|
||||
4. Configure permissions:
|
||||
- **Permissions**: Zone > DNS > Edit
|
||||
- **Zone Resources**: Include > Specific zone > `motovaultpro.com`
|
||||
5. Click **Continue to summary** then **Create Token**
|
||||
6. Copy the token value immediately (it won't be shown again)
|
||||
7. Add as `CF_DNS_API_TOKEN` File variable in GitLab
|
||||
|
||||
### Setting Up a File Type Variable
|
||||
|
||||
1. Go to **Settings > CI/CD > Variables**
|
||||
@@ -126,10 +141,11 @@ MotoVaultPro uses a Kubernetes-style secrets pattern where secrets are mounted a
|
||||
|
||||
```
|
||||
secrets/app/
|
||||
postgres-password.txt -> /run/secrets/postgres-password
|
||||
auth0-client-secret.txt -> /run/secrets/auth0-client-secret
|
||||
google-maps-api-key.txt -> /run/secrets/google-maps-api-key
|
||||
google-maps-map-id.txt -> /run/secrets/google-maps-map-id
|
||||
postgres-password.txt -> /run/secrets/postgres-password
|
||||
auth0-client-secret.txt -> /run/secrets/auth0-client-secret
|
||||
google-maps-api-key.txt -> /run/secrets/google-maps-api-key
|
||||
google-maps-map-id.txt -> /run/secrets/google-maps-map-id
|
||||
cloudflare-dns-token.txt -> /run/secrets/cloudflare-dns-token
|
||||
```
|
||||
|
||||
### Security Benefits
|
||||
|
||||
136
docs/PROMPTS.md
136
docs/PROMPTS.md
@@ -23,8 +23,8 @@ Your task is to create a plan that can be dispatched to a seprate set of AI agen
|
||||
You are a senior DevOps SRE in charge of MotoVaultPro. A automotive fleet management application.
|
||||
|
||||
*** ACTION ***
|
||||
- There are errors in deployment then also console errors
|
||||
- It appears when trying to run the npm migrations there is an error.
|
||||
- The production deployment from GitLab CI is not installing the Let's Encrypt certificates
|
||||
- You should start looking at if the cloudflare API key is being passed into the pipeline.
|
||||
- Read README.md CLAUDE.md and AI-INDEX.md to understand this code repository in the context of this change.
|
||||
|
||||
*** CONTEXT ***
|
||||
@@ -32,135 +32,3 @@ You are a senior DevOps SRE in charge of MotoVaultPro. A automotive fleet manage
|
||||
|
||||
*** CHANGES TO IMPLEMENT ***
|
||||
- Plan and recommend the best solution for this change
|
||||
|
||||
Here are the relevant logs.
|
||||
egullickson@mvp-runner-1:~/motovaultpro$ docker compose run --rm mvp-backend npm run migrate
|
||||
[+] 3/3t 3/31
|
||||
✔ Network motovaultpro_backend Created 0.1s
|
||||
✔ Container mvp-postgres Running 0.0s
|
||||
✔ Container mvp-redis Running 0.0s
|
||||
Image motovaultpro-mvp-backend Building
|
||||
[+] Building 196.2s (26/26) FINISHED
|
||||
=> [internal] load local bake definitions 0.0s
|
||||
=> => reading from stdin 622B
|
||||
=> => unpacking to docker.io/library/motovaultpro-mvp-backend:latest 9.5s
|
||||
=> resolving provenance for metadata file 0.0s
|
||||
Image motovaultpro-mvp-backend Built
|
||||
Container motovaultpro-mvp-backend-run-9a43629523b7 Creating
|
||||
Container motovaultpro-mvp-backend-run-9a43629523b7 Created
|
||||
npm error Missing script: "migrate"
|
||||
npm error
|
||||
npm error To see a list of scripts, run:
|
||||
npm error npm run
|
||||
npm error A complete log of this run can be found in: /home/nodejs/.npm/_logs/2025-12-20T16_15_34_540Z-debug-0.log
|
||||
index-BZX6X6vG.js:21 [Auth Gate] Module loaded, authInitialized: false
|
||||
index-BZX6X6vG.js:21 [Navigation] State rehydrated successfully
|
||||
index-BZX6X6vG.js:21 [Auth0Provider] Component loaded Object
|
||||
index-BZX6X6vG.js:21 [TokenInjector] Component loaded
|
||||
index-BZX6X6vG.js:27 [DEBUG App] Render check - isLoading: true isAuthenticated: false isAuthGateReady: false
|
||||
index-BZX6X6vG.js:27 MotoVaultPro status: Object
|
||||
index-BZX6X6vG.js:21 [useIsAuthInitialized] Starting poll for auth init
|
||||
index-BZX6X6vG.js:27 DataSync: Skipping prefetch - user not authenticated
|
||||
index-BZX6X6vG.js:27 Window width: 1728 User agent mobile: false Mobile mode: false
|
||||
index-BZX6X6vG.js:21 [Auth Debug] Mobile: false, Loading: true, Authenticated: false, User: null
|
||||
index-BZX6X6vG.js:21 [Auth Debug] State check: Object
|
||||
index-BZX6X6vG.js:21 [DEBUG] setAuthInitialized called with: false (was: false )
|
||||
index-BZX6X6vG.js:21 [IndexedDB] Loaded 0 items into cache
|
||||
index-BZX6X6vG.js:21 [IndexedDB] Storage initialized successfully
|
||||
index-BZX6X6vG.js:21 [useIsAuthInitialized] Poll #1: initialized=false
|
||||
index-BZX6X6vG.js:21 [useIsAuthInitialized] Poll #2: initialized=false
|
||||
index-BZX6X6vG.js:21 [useIsAuthInitialized] Poll #3: initialized=false
|
||||
index-BZX6X6vG.js:21 [Auth0Provider] Redirect callback triggered Object
|
||||
index-BZX6X6vG.js:21 [Auth0Provider] Component loaded Object
|
||||
index-BZX6X6vG.js:21 [TokenInjector] Component loaded
|
||||
index-BZX6X6vG.js:27 [DEBUG App] Render check - isLoading: false isAuthenticated: true isAuthGateReady: false
|
||||
index-BZX6X6vG.js:27 MotoVaultPro status: Object
|
||||
index-BZX6X6vG.js:27 [DEBUG App] Auth gate not ready yet, showing loading state
|
||||
index-BZX6X6vG.js:21 [Auth Debug] Mobile: false, Loading: false, Authenticated: true, User: present
|
||||
index-BZX6X6vG.js:21 [Auth Debug] State check: Object
|
||||
index-BZX6X6vG.js:27 [DEBUG App] Render check - isLoading: false isAuthenticated: true isAuthGateReady: false
|
||||
index-BZX6X6vG.js:27 MotoVaultPro status: Object
|
||||
index-BZX6X6vG.js:27 [DEBUG App] Auth gate not ready yet, showing loading state
|
||||
index-BZX6X6vG.js:21 [Auth] IndexedDB storage is ready
|
||||
index-BZX6X6vG.js:21 [Mobile Auth] Initializing token cache (mobile: false, delay: 100ms)
|
||||
index-BZX6X6vG.js:21 [useIsAuthInitialized] Poll #4: initialized=false
|
||||
index-BZX6X6vG.js:21 [Mobile Auth] Token acquired successfully on attempt 1 Object
|
||||
index-BZX6X6vG.js:21 [Mobile Auth] Token pre-warming successful
|
||||
index-BZX6X6vG.js:21 [DEBUG] setAuthInitialized called with: true (was: false )
|
||||
index-BZX6X6vG.js:21 [DEBUG Auth Gate] Authentication fully initialized
|
||||
index-BZX6X6vG.js:21 [useIsAuthInitialized] Poll #5: initialized=true
|
||||
index-BZX6X6vG.js:21 [useIsAuthInitialized] Auth initialized via poll!
|
||||
index-BZX6X6vG.js:27 [DEBUG App] Render check - isLoading: false isAuthenticated: true isAuthGateReady: true
|
||||
index-BZX6X6vG.js:27 MotoVaultPro status: Object
|
||||
index-BZX6X6vG.js:21 [Auth0Provider] Component loaded Object
|
||||
index-BZX6X6vG.js:21 [TokenInjector] Component loaded
|
||||
index-BZX6X6vG.js:27 [DEBUG App] Render check - isLoading: false isAuthenticated: true isAuthGateReady: true
|
||||
index-BZX6X6vG.js:27 MotoVaultPro status: Object
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Query function called - fetching vehicles
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:21 [Mobile Auth] Token acquired successfully on attempt 1 Object
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:27 [useVehicles] Hook called - isAuthenticated: true isLoading: false
|
||||
index-BZX6X6vG.js:21 Uncaught Error: Minified React error #185; visit https://react.dev/errors/185 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
|
||||
at yi (index-BZX6X6vG.js:21:32113)
|
||||
at hi (index-BZX6X6vG.js:21:31638)
|
||||
at Yc (index-BZX6X6vG.js:21:64112)
|
||||
at Hc (index-BZX6X6vG.js:21:63724)
|
||||
at index-BZX6X6vG.js:27:314956
|
||||
at Zu (index-BZX6X6vG.js:21:97033)
|
||||
at Ad (index-BZX6X6vG.js:21:113396)
|
||||
at Fd (index-BZX6X6vG.js:21:113280)
|
||||
at Ad (index-BZX6X6vG.js:21:113441)
|
||||
at Fd (index-BZX6X6vG.js:21:113280)
|
||||
|
||||
@@ -31,6 +31,9 @@ RUN npm run build
|
||||
# Stage 4: Production stage with nginx
|
||||
FROM nginx:alpine AS production
|
||||
|
||||
# Add curl for healthchecks
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
# Create non-root user compatible with nginx
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nodejs -u 1001 -G nginx
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
# - AUTH0_CLIENT_SECRET
|
||||
# - GOOGLE_MAPS_API_KEY
|
||||
# - GOOGLE_MAPS_MAP_ID
|
||||
# - CF_DNS_API_TOKEN (Cloudflare DNS API token for Let's Encrypt certificates)
|
||||
#
|
||||
# Required GitLab CI/CD Variables (Variable type):
|
||||
# - DEPLOY_PATH
|
||||
@@ -70,6 +71,7 @@ inject_secret "POSTGRES_PASSWORD" "postgres-password.txt" || FAILED=1
|
||||
inject_secret "AUTH0_CLIENT_SECRET" "auth0-client-secret.txt" || FAILED=1
|
||||
inject_secret "GOOGLE_MAPS_API_KEY" "google-maps-api-key.txt" || FAILED=1
|
||||
inject_secret "GOOGLE_MAPS_MAP_ID" "google-maps-map-id.txt" || FAILED=1
|
||||
inject_secret "CF_DNS_API_TOKEN" "cloudflare-dns-token.txt" || FAILED=1
|
||||
|
||||
if [ $FAILED -eq 1 ]; then
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user