From d074e6d2e512a293599dbe5144032f2a4d6b31c4 Mon Sep 17 00:00:00 2001 From: Wuesteon Date: Mon, 8 Dec 2025 23:11:01 +0100 Subject: [PATCH] docs: comprehensive staging deployment troubleshooting guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add extensive documentation for staging deployment issues: - Lazy client initialization pattern for runtime URLs - PostgreSQL ALTER TABLE USING clause requirement - Debugging checklist for common issues (API, CORS, 500, 401) - Summary table of common mistakes and prevention 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/STAGING_DEPLOYMENT_ISSUES.md | 135 +++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 3 deletions(-) diff --git a/docs/STAGING_DEPLOYMENT_ISSUES.md b/docs/STAGING_DEPLOYMENT_ISSUES.md index 1dd0e283d..4df93e2dd 100644 --- a/docs/STAGING_DEPLOYMENT_ISSUES.md +++ b/docs/STAGING_DEPLOYMENT_ISSUES.md @@ -9,6 +9,8 @@ This document captures common issues encountered during staging deployments and 3. [CD Workflow Version Tags](#3-cd-workflow-version-tags) 4. [Database Setup](#4-database-setup) 5. [User ID Format (Better Auth)](#5-user-id-format-better-auth) +6. [Debugging Checklist](#6-debugging-checklist) +7. [Summary: Common Mistakes to Avoid](#summary-common-mistakes-to-avoid) --- @@ -62,6 +64,37 @@ function getApiUrl(): string { } ``` +### Lazy Client Initialization Pattern + +**Important**: API clients must be lazily initialized to read the URL at request time, not at module load time: + +```typescript +// CORRECT - Lazy initialization +let _client: ReturnType | null = null; + +function getClient() { + if (!_client) { + _client = createApiClient(getApiUrl()); // URL evaluated when called + } + return _client; +} + +export async function getTasks() { + return getClient().get('/tasks'); // Client created on first use +} +``` + +```typescript +// WRONG - Module-level initialization +const client = createApiClient(getApiUrl()); // URL evaluated at import time! + +export async function getTasks() { + return client.get('/tasks'); // Will use stale URL +} +``` + +**Why this matters**: When the module is imported, the `window` object may not have the injected environment variables yet. The lazy pattern ensures the URL is read only when the client is actually needed. + ### Docker Compose Pattern Use two environment variables: @@ -210,12 +243,22 @@ Backend database schemas use `uuid` type for `user_id`, but Better Auth generate Change `user_id` columns from `uuid` to `text`: ```sql --- For each table with user_id -ALTER TABLE tasks ALTER COLUMN user_id TYPE text; -ALTER TABLE projects ALTER COLUMN user_id TYPE text; +-- For each table with user_id (use USING clause for explicit conversion) +ALTER TABLE tasks ALTER COLUMN user_id TYPE text USING user_id::text; +ALTER TABLE projects ALTER COLUMN user_id TYPE text USING user_id::text; -- etc. ``` +**Important**: Always use the `USING` clause when converting column types. Without it, PostgreSQL may silently fail or produce unexpected results: + +```sql +-- CORRECT - Explicit conversion +ALTER TABLE events ALTER COLUMN user_id TYPE text USING user_id::text; + +-- RISKY - May fail silently on some data types +ALTER TABLE events ALTER COLUMN user_id TYPE text; +``` + ### Prevention When creating new backend schemas, **always use `text` type for user_id**: @@ -277,3 +320,89 @@ cd ~/manacore-staging && docker compose up -d --no-deps --force-recreate /api/v1/endpoint \ + -H "Origin: http://46.224.108.214:5173" + ``` + Should return `Access-Control-Allow-Origin` header. + +3. **Check container logs** + ```bash + ssh deploy@46.224.108.214 "docker logs --tail 100" + ``` + +### 500 Internal Server Error + +1. **Check database exists** + ```bash + docker exec manacore-postgres-staging psql -U postgres -c '\l' + ``` + +2. **Check tables exist** + ```bash + docker exec manacore-postgres-staging psql -U postgres -d -c '\dt' + ``` + +3. **Check for type mismatches** (especially user_id uuid vs text) + +### 401 Unauthorized + +1. **Check token is being sent** + ```bash + # In browser Network tab, check Authorization header + ``` + +2. **Check JWKS endpoint** + ```bash + curl http://46.224.108.214:3001/api/v1/auth/jwks + ``` + +3. **Check issuer/audience match** - Token must have `iss: manacore` and `aud: manacore` + +### Container Not Updated + +1. **Check image version** + ```bash + docker ps --format '{{.Names}}: {{.Image}}' + ``` + +2. **Check .env file** + ```bash + cat ~/manacore-staging/.env | grep VERSION + ``` + +3. **Force recreate** + ```bash + docker compose up -d --no-deps --force-recreate + ``` + +--- + +## Summary: Common Mistakes to Avoid + +| Mistake | Consequence | Prevention | +|---------|-------------|------------| +| Using `import.meta.env` for Docker runtime | URLs baked at build time | Use `window.__PUBLIC_*__` with runtime injection | +| Initializing API clients at module level | Client uses stale URLs | Use lazy initialization pattern | +| Using `uuid` type for user_id | Better Auth IDs fail validation | Always use `text` type for user_id | +| Missing CORS origin for manacore-web | Dashboard can't call backends | Add port 5173 to all backend CORS configs | +| Wrong tag format for mana-core-auth | Deployment fails, can't find Dockerfile | Use `mana-core-auth-staging-v*` not `auth-staging-v*` | +| Forgetting to create database | Backend crashes on startup | Create database before first deployment | +| ALTER TABLE without USING clause | Silent failures on type conversion | Always use `USING column::new_type` |