Files
omo-bot/DEPLOYMENT.md
zwitschi 4d6b80a3b9
CI-CD / Bot Lint Test Build (push) Successful in 17s
CI-CD / Dashboard Lint Build (push) Successful in 13s
CI-CD / Deploy to Coolify (push) Failing after 3s
feat: Add detailed token requirements and troubleshooting steps for Coolify API access in deployment guide
2026-05-17 19:43:44 +02:00

263 lines
8.7 KiB
Markdown

# Deployment Guide
This document covers production deployment for the `omo-bot` Coolify project with **two separate resources**:
- `omo-bot-backend` (Node.js bot + Admin API)
- `omo-bot-dashboard` (React/Vite static dashboard from `admin-dashboard/`)
## Deployment Topology
Recommended domains:
- `api.openmicodyssey.com` -> `omo-bot-backend`
- `admin.openmicodyssey.com` -> `omo-bot-dashboard`
Recommended edge topology:
- Internet -> Nginx reverse proxy -> Coolify resources
- Nginx routes by hostname to backend/dashboard resource domains or internal ports
Optional same-origin pattern:
- `admin.openmicodyssey.com` serves dashboard and reverse-proxies `/admin/*` + `/health` to backend.
## 1. Create Coolify Project and Resources
1. In Coolify, create (or open) project `omo-bot`.
2. Add resource `omo-bot-backend` from this repository:
- Build context: repository root
- Start command: `npm start`
- Internal port: `8787` (Admin API)
3. Add resource `omo-bot-dashboard` from this repository:
- Base directory: `admin-dashboard`
- Build command: `npm run build`
- Publish directory: `/`
- Internal port: `80` (typical static web port behind Coolify)
4. Configure domains for each resource and enable TLS certificates.
## 1.1 Nginx Reverse Proxy in Front of Coolify
Use this pattern when you want one public edge server in front of Coolify-managed services.
Recommended flow:
1. Public DNS for `api.openmicodyssey.com` and `admin.openmicodyssey.com` points to Nginx host.
2. Nginx terminates TLS and forwards traffic to Coolify resource domains or private IP:port targets.
3. Coolify resources stay private or restricted to internal network where possible.
Example host-based routing:
```nginx
server {
listen 80;
server_name api.openmicodyssey.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name api.openmicodyssey.com;
ssl_certificate /etc/letsencrypt/live/api.openmicodyssey.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.openmicodyssey.com/privkey.pem;
location / {
proxy_pass http://coolify-backend.internal:8787;
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 https;
}
}
server {
listen 80;
server_name admin.openmicodyssey.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name admin.openmicodyssey.com;
ssl_certificate /etc/letsencrypt/live/admin.openmicodyssey.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/admin.openmicodyssey.com/privkey.pem;
location / {
proxy_pass http://coolify-dashboard.internal:80;
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 https;
}
}
```
If you prefer same-origin routing for dashboard + API through one public domain, proxy `location /admin/` and `location /health` to backend while keeping `/` on dashboard.
Recommended OAuth callback route:
- `https://admin.openmicodyssey.com/oauth/discord/callback`
If you use this callback path, make sure dashboard hosting serves the SPA entrypoint for that route instead of returning `404`.
## 2. Configure Runtime Variables
Backend (`omo-bot-backend`) minimum:
- `DATABASE_URL`
- `CONFIG_DB_ENABLED=true`
- Any temporary legacy vars needed for first-time `db:seed`
Dashboard (`omo-bot-dashboard`) minimum:
- `VITE_ADMIN_API_BASE_URL` (for split-domain deployment, set to `https://api.openmicodyssey.com`)
- `VITE_DISCORD_CLIENT_ID`
- `VITE_DISCORD_REDIRECT_URI` (recommended: `https://admin.openmicodyssey.com/oauth/discord/callback`)
OAuth alignment:
- Discord Developer Portal redirect URI must exactly match the dashboard callback URL.
- Config DB key `OAUTH_BRIDGE_REDIRECT_URI` must match the same callback URL.
- Dashboard env `VITE_DISCORD_REDIRECT_URI` must match the same callback URL.
Discord OAuth authorize URL shape:
```text
https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=https%3A%2F%2Fadmin.openmicodyssey.com%2Foauth%2Fdiscord%2Fcallback&scope=identify%20guilds&prompt=consent
```
## 3. Get Coolify API Token and Resource UUIDs
1. In Coolify, open `Keys & Tokens` -> `API Tokens` and create a token.
2. Copy your Coolify base URL (example: `https://coolify.yourdomain.com`).
3. Find resource UUID for:
- `omo-bot-backend`
- `omo-bot-dashboard`
Deploy API endpoint used by CI:
- `GET /api/v1/deploy?uuid=<resource_uuid>`
- Authorization: `Bearer <COOLIFY_API_TOKEN>`
### 3.1 Token Requirements (Important)
If CI fails with `Coolify API token rejected during preflight (HTTP 403)`, token exists but lacks access to API target.
Use this checklist:
1. Create token in same Coolify instance that hosts `omo-bot-backend` and `omo-bot-dashboard` resources.
2. Ensure token owner has access to project/team containing those resources.
3. Ensure token is API token (not UI session token/cookie).
4. Set `COOLIFY_BASE_URL` to Coolify root URL only (example: `https://coolify.yourdomain.com`), no extra path like `/project/...`.
5. Rotate token if unsure, then update Gitea secret `COOLIFY_API_TOKEN`.
Required permissions to grant (minimum):
- Project/resource read access for both deploy targets (`omo-bot-backend`, `omo-bot-dashboard`)
- Deploy/trigger permission on both resources
- Team/project membership that includes those resources (not observer/read-only)
If your Coolify token form shows these permissions:
- root
- write
- deploy
- read
- read:sensitive
Use this selection for CI deploy token:
- Required: read + deploy
- Optional: write (only if your instance still blocks deploy with read+deploy)
- Avoid for CI: root
- Optional and sensitive: read:sensitive (only needed when debugging APIs that must return secrets/log internals)
Tip: Start with read + deploy. If preflight still returns 403, temporarily add write to verify scope mismatch, then reduce to least-privilege that still works.
Preflight check from local shell (should NOT return `401` or `403`):
```bash
curl -i \
-H "Authorization: Bearer $COOLIFY_API_TOKEN" \
"$COOLIFY_BASE_URL/api/v1/deploy"
```
Resource trigger checks (match CI fallback logic):
```bash
# Variant 1: GET + Bearer
curl -i -G \
-H "Authorization: Bearer $COOLIFY_API_TOKEN" \
--data-urlencode "uuid=$COOLIFY_RESOURCE_UUID" \
"$COOLIFY_BASE_URL/api/v1/deploy"
# Variant 2: POST JSON + Bearer
curl -i -X POST \
-H "Authorization: Bearer $COOLIFY_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"uuid\":\"$COOLIFY_RESOURCE_UUID\"}" \
"$COOLIFY_BASE_URL/api/v1/deploy"
# Variant 3: GET + raw Authorization token
curl -i -G \
-H "Authorization: $COOLIFY_API_TOKEN" \
--data-urlencode "uuid=$COOLIFY_RESOURCE_UUID" \
"$COOLIFY_BASE_URL/api/v1/deploy"
```
If all variants return `403`, issue is permissions/scope in Coolify, not Gitea secret injection.
## 4. Configure Gitea Action Secrets/Variables
In Gitea repository settings, add Actions secrets:
- `COOLIFY_BASE_URL`
- `COOLIFY_API_TOKEN`
- `COOLIFY_RESOURCE_UUID_BOT`
- `COOLIFY_RESOURCE_UUID_DASHBOARD`
Current workflow in `.gitea/workflows/ci-cd.yml` triggers backend and dashboard deploys separately via the Coolify Deploy API.
## 5. Domain and DNS Checklist
1. Create DNS records to Nginx edge host (or directly to Coolify if no edge proxy):
- `A` record for root/subdomain targets
- `CNAME` where appropriate
2. Verify certificates issued on Nginx or Coolify, depending on TLS termination point.
3. Verify Nginx upstreams match deployed ports:
- backend -> `8787`
- dashboard -> `80`
4. Verify OAuth callback route resolves through dashboard hosting:
- `https://admin.openmicodyssey.com/oauth/discord/callback`
5. Verify dashboard can call API at configured `VITE_ADMIN_API_BASE_URL`.
6. Verify API health endpoint over TLS:
- `GET https://api.openmicodyssey.com/health`
## 6. Post-Deploy Verification
1. `npm run register:commands` (if bot app/guild IDs changed).
2. Confirm bot online in Discord and slash commands visible.
3. Open dashboard URL and run OAuth login flow.
4. Confirm Discord redirects back to `https://admin.openmicodyssey.com/oauth/discord/callback` without `404`.
5. Validate Admin API auth behavior with and without bearer token.
## 7. Coolify 403 Troubleshooting
When CI fails in `Validate Coolify API access` with HTTP `403`:
1. Confirm token belongs to correct Coolify instance and team/project.
2. Confirm resource UUIDs are from same instance and still valid.
3. Re-run local preflight curl from section 3.1.
4. Recreate token and update Gitea secret.
5. Re-run pipeline.
Current CI workflow already prints sanitized response snippets for deploy failures and tries auth/method fallback variants in `.gitea/workflows/ci-cd.yml`.