# Testing Guide ## Quick Verification ```bash # 1. Start server bun run dev # 2. Health check curl http://localhost:3000/health # 3. Open browser open http://localhost:3000 ``` ## API Endpoints | Endpoint | Method | Purpose | |----------|--------|---------| | `/health` | GET | Server status | | `/api/check/:name` | GET | Name availability | | `/api/deploy` | POST | Start deployment | | `/api/status/:id` | GET | SSE progress stream | | `/api/stack/:name` | DELETE | Delete stack and cleanup | ## Test Checklist ### Backend | Test | Command | Expected | |------|---------|----------| | Server starts | `bun run dev` | "starting on http://0.0.0.0:3000" | | Health endpoint | `curl localhost:3000/health` | `{"status":"healthy"...}` | | TypeScript | `bun run typecheck` | No errors | | Dokploy connection | Check `/api/check/test-name` | Returns availability | ### Frontend | Test | Action | Expected | |------|--------|----------| | Page loads | Open localhost:3000 | Dark theme, centered content | | Typewriter | Wait 2s | "Choose Your Stack Name" animates | | Language switch | Click 🇲🇦 | Arabic text, RTL layout | | Name validation | Type "ab" | Error: too short | | Reserved name | Type "admin" | Error: reserved | | Valid name | Type "my-stack" | "✓ Name is available!" | ### Deployment Flow | Step | Indicator | |------|-----------| | Submit form | Progress bar appears | | SSE updates | Log entries animate in | | Success | Typewriter: "Deployment Complete" | | Error | Typewriter: "Deployment Failed" | ## Infrastructure | Service | URL | Purpose | |---------|-----|---------| | Dokploy | https://app.flexinit.nl | Container orchestration | | Traefik | 144.76.116.169 | SSL termination | | Stacks | *.ai.flexinit.nl | Deployed AI assistants | ## Full Deployment Test ```bash # 1. Generate unique name NAME="test-$(date +%s | tail -c 5)" # 2. Check availability curl -s http://localhost:3000/api/check/$NAME # 3. Deploy curl -s -X POST http://localhost:3000/api/deploy \ -H "Content-Type: application/json" \ -d "{\"name\": \"$NAME\"}" # 4. Monitor SSE (wait ~2-3 min) curl -N http://localhost:3000/api/status/ # 5. Verify stack accessible curl -s https://$NAME.ai.flexinit.nl # 6. Cleanup curl -s -X DELETE http://localhost:3000/api/stack/$NAME ``` ## Cleanup Commands ```bash # Delete specific stack curl -s -X DELETE http://localhost:3000/api/stack/my-stack # List all projects (direct Dokploy) source .env && curl -s -H "x-api-key: $DOKPLOY_API_TOKEN" \ "$DOKPLOY_URL/api/project.all" | jq '.[].name' ``` ## Common Issues | Issue | Solution | |-------|----------| | CSS not loading | Check `/style.css` returns CSS, not HTML | | 401 on Dokploy | Regenerate API token in Dokploy dashboard | | Typewriter not running | Check browser console for JS errors | | RTL not working | Verify `dir="rtl"` on `` element | | Health check timeout | Container startup can take 1-2 min, timeout is 3 min | | SSL cert errors | Health check treats SSL errors as "alive" during provisioning | | SSE disconnects | idleTimeout set to 255s (max), long deployments should complete | --- ## Production Verification (2026-01-10) ### Verified Working | Component | Status | Notes | |-----------|--------|-------| | Portal Health | ✅ | `https://portal.ai.flexinit.nl/health` returns healthy | | Name Validation | ✅ | `/api/check/:name` validates correctly | | Frontend UI | ✅ | 3 languages (NL, AR, EN), RTL support, typewriter animation | | Stack Deployment | ✅ | Full flow: project → app → domain → deploy → health check | | Stack Cleanup | ✅ | `DELETE /api/stack/:name` removes all resources | | SSL/HTTPS | ✅ | Wildcard cert working for all `*.ai.flexinit.nl` | ### Critical Configuration ```bash # These settings are REQUIRED for deployment to work DOKPLOY_URL=https://app.flexinit.nl # Public URL, NOT internal 10.100.0.20 STACK_IMAGE=git.app.flexinit.nl/oussamadouhou/oh-my-opencode-free:latest STACK_REGISTRY_ID=bKDYM5X7NN34x_lRDjWbz # Registry ID for Docker auth ``` ### Known Gotchas (Fixed) 1. **Registry URL format** - Use `git.app.flexinit.nl`, NOT `https://git.app.flexinit.nl` 2. **Username in image path** - Must be `oussamadouhou`, not `odouhou` 3. **Dokploy URL** - Must use public URL for container-to-container communication 4. **Health check** - Now uses Dokploy API status check (not HTTP fetch) 5. **Resource limits** - Removed temporarily (Dokploy API format issues with CPU/memory values) 6. **Typewriter race condition** - Fixed by tracking active instances and canceling previous ### UI/UX Fixes (2026-01-10 v2) | Issue | Fix | Status | |-------|-----|--------| | Double letters in typewriter | Track active instances, cancel before starting new | ✅ Verified | | Flag emojis not showing | Replaced with text labels (NL/AR/EN) | ✅ Verified | | SSE disconnects during deployment | Use Dokploy API status instead of HTTP health check | ✅ Verified | | 'yourname' not translated | Added translation key per language | ✅ Verified | ### Health Check Change The health check was changed from HTTP fetch (which failed from inside Docker) to Dokploy API status check: ```typescript // Before: HTTP fetch (failed - container can't reach external URL) const response = await fetch(`${state.url}/`); // After: Dokploy API status check (works) const app = await this.client.getApplication(applicationId); if (app.applicationStatus === 'done') { /* success */ } ``` ### Test Commands ```bash # Quick health check curl -s https://portal.ai.flexinit.nl/health | jq . # Full deployment test NAME="test-$(date +%s | tail -c 5)" RESULT=$(curl -s -X POST https://portal.ai.flexinit.nl/api/deploy \ -H "Content-Type: application/json" \ -d "{\"name\": \"$NAME\"}") echo $RESULT | jq . # Monitor deployment (use deploymentId from above) curl -N "https://portal.ai.flexinit.nl/api/status/$(echo $RESULT | jq -r .deploymentId)" # Verify stack accessible (after deployment completes) curl -s -k https://$NAME.ai.flexinit.nl | head -5 # Cleanup curl -s -X DELETE https://portal.ai.flexinit.nl/api/stack/$NAME | jq . ``` ### Dokploy Direct Commands ```bash # List all ai-stack projects source .env && curl -s -H "x-api-key: $DOKPLOY_API_TOKEN" \ https://app.flexinit.nl/api/project.all | \ jq '[.[] | select(.name | startswith("ai-stack-")) | {name: .name, id: .projectId}]' # Get application status source .env && curl -s -H "x-api-key: $DOKPLOY_API_TOKEN" \ "https://app.flexinit.nl/api/project.one?projectId=" | \ jq '.environments[0].applications[0] | {name: .name, status: .applicationStatus}' ``` --- ## Gitea Actions CI/CD Status ### Check Workflow Status The `oh-my-opencode-free` Docker image is built via Gitea Actions. To check CI status: **Web UI:** ``` https://git.app.flexinit.nl/oussamadouhou/oh-my-opencode-free/actions ``` **API (requires token):** ```bash # Get GITEA_API_TOKEN from BWS (key: GITEA_API_TOKEN) GITEA_TOKEN="" # List recent workflow runs curl -s -H "Authorization: token $GITEA_TOKEN" \ "https://git.app.flexinit.nl/api/v1/repos/oussamadouhou/oh-my-opencode-free/actions/runs?limit=5" | \ jq '.workflow_runs[] | {id, run_number, status, conclusion, display_title, head_sha: .head_sha[0:7]}' # Get specific run details curl -s -H "Authorization: token $GITEA_TOKEN" \ "https://git.app.flexinit.nl/api/v1/repos/oussamadouhou/oh-my-opencode-free/actions/runs/" | jq . # Get jobs for a specific run curl -s -H "Authorization: token $GITEA_TOKEN" \ "https://git.app.flexinit.nl/api/v1/repos/oussamadouhou/oh-my-opencode-free/actions/runs//jobs" | jq . ``` ### API Response Fields | Field | Description | |-------|-------------| | `status` | `queued`, `in_progress`, `completed` | | `conclusion` | `success`, `failure`, `cancelled`, `skipped` (only when status=completed) | | `head_sha` | Commit SHA that triggered the run | | `run_number` | Sequential run number | | `display_title` | Commit message or PR title | ### Gitea API Authentication From [Gitea docs](https://docs.gitea.com/development/api-usage): ```bash # Header format Authorization: token # Alternative: query parameter ?token= ``` ### BWS Token Reference | Key | Purpose | |-----|---------| | `GITEA_API_TOKEN` | Gitea API access for workflow status | | `DOKPLOY_API_TOKEN` | Dokploy deployment API (BWS ID: `6b3618fc-ba02-49bc-bdc8-b3c9004087bc`) | --- ## Testing Session: 2026-01-13 ### Session Summary **Goal:** Verify multi-environment deployment setup and shared project configuration. ### Completed Tasks | Task | Status | Evidence | |------|--------|----------| | Workflow separation (dev/staging/main) | ✅ | Committed as `eb2745d` | | Dollar sign escaping (`$${{project.VAR}}`) | ✅ | Verified in all docker-compose.*.yml | | Shared project exists | ✅ | `ai-stack-portal` (ID: `2y2Glhz5Wy0dBNf6BOR_-`) | | Environment IDs retrieved | ✅ | See below | | Local dev server health | ✅ | `/health` returns healthy | ### Environment IDs ``` Project: ai-stack-portal ID: 2y2Glhz5Wy0dBNf6BOR_- Environments: - production: _dKAmxVcadqi-z73wKpEB (default) - deployments: RqE9OFMdLwkzN7pif1xN8 (for user stacks) - test: KVKn5fXGz10g7KVxPWOQj ``` ### Blockers Identified #### BLOCKER: Dokploy API Token Permissions **Symptom:** All Dokploy API calls return `Forbidden` ```bash # Previously working curl -s "https://app.flexinit.nl/api/project.all" -H "x-api-key: $DOKPLOY_API_TOKEN" # Now returns: Forbidden # Environment endpoint curl -s "https://app.flexinit.nl/api/environment.one?environmentId=RqE9OFMdLwkzN7pif1xN8" -H "x-api-key: $DOKPLOY_API_TOKEN" # Returns: Forbidden ``` **Root Cause:** The API token `app_deployment...` has been revoked or has limited scope. **Impact:** - Cannot verify Docker image exists in registry - Cannot test name availability (requires `environment.one`) - Cannot create applications or compose stacks - Cannot deploy portal to Dokploy **Resolution Required:** 1. Log into Dokploy UI at https://app.flexinit.nl 2. Navigate to Settings → API Keys 3. Generate new API key with full permissions: - Read/Write access to projects - Read/Write access to applications - Read/Write access to compose stacks - Read/Write access to domains 4. Update `.env` with new token 5. Update BWS secret (ID: `6b3618fc-ba02-49bc-bdc8-b3c9004087bc`) ### Local Testing Results ```bash # Health check - WORKS curl -s "http://localhost:3000/health" # {"status":"healthy","timestamp":"2026-01-13T13:01:46.100Z","version":"0.2.0",...} # Name check - FAILS (API token issue) curl -s "http://localhost:3000/api/check/test-stack" # {"available":false,"valid":false,"error":"Failed to check availability"} ``` ### Required .env Configuration ```bash # Added for shared project deployment SHARED_PROJECT_ID=2y2Glhz5Wy0dBNf6BOR_- SHARED_ENVIRONMENT_ID=RqE9OFMdLwkzN7pif1xN8 ``` ### Next Steps After Token Fix 1. Verify `project.all` API works with new token 2. Deploy portal to Dokploy (docker-compose.dev.yml) 3. Test end-to-end stack deployment 4. Verify stacks deploy to shared project 5. Clean up test deployments ### Commands Reference ```bash # Test API token source .env && curl -s "https://app.flexinit.nl/api/project.all" \ -H "x-api-key: $DOKPLOY_API_TOKEN" | jq '.[].name' # Get environment applications source .env && curl -s "https://app.flexinit.nl/api/environment.one?environmentId=RqE9OFMdLwkzN7pif1xN8" \ -H "x-api-key: $DOKPLOY_API_TOKEN" | jq '.applications' # Deploy test stack curl -X POST http://localhost:3000/api/deploy \ -H "Content-Type: application/json" \ -d '{"name":"test-'$(date +%s | tail -c 4)'"}' ```