Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion .agents/skills/m3tacron/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: m3tacron
description: SSH access and database connection info for the m3tacron project servers (m3tacron.com, dev.m3tacron.com). Load whenever you need to connect to the server or inspect the database.
description: SSH access, database connection info, and Coolify deployment info for the m3tacron project (m3tacron.com, dev.m3tacron.com). Load whenever you need to connect to the server, inspect the database, or trigger/manage Coolify preview deployments via the coolify CLI.
---

## SSH Access
Expand Down Expand Up @@ -30,3 +30,29 @@ The `audit-bot` user has docker group access. Use `docker ps` to list containers
```bash
docker exec <db-container> psql -U postgres -c "SELECT COUNT(*) FROM tournament;"
```

## Coolify CLI (preview deployments)

The `coolify` CLI is installed locally and configured with context `m3tacron`
(`https://coolify.m3tacron.com`, deploy-scoped API token in `~/.config/coolify/config.json`).

### Trigger a PR preview redeploy
```bash
coolify deploy uuid d1237bghfe3rxgm7ahj00z8l --pull-request-id <PR_NUMBER> --force
```
Application UUID `d1237bghfe3rxgm7ahj00z8l` = `francespo/m3tacron:main` (preview deployments are per-PR).

### Prerequisites / gotchas (already configured)
- Coolify API must be enabled: `instance_settings.is_api_enabled = true` (was disabled → CLI returned 403 "API is disabled").
- Token is a Laravel Sanctum personal access token created via tinker with abilities `["deploy","read","write"]`.
Coolify's custom `createToken` requires `session(['currentTeam' => $team])` to be set first (team_id is NOT NULL).

### Preview proxy architecture (for debugging why a preview shows stale data)
- Each PR preview has its own backend container, reachable as `backend-pr-<N>` on the shared coolify network.
The generic `backend` hostname belongs to the OLD shared backend — never use it for previews.
- Frontend proxy: `frontend/src/routes/api/[...path]/+server.js` resolves the backend via
`previewBackendHost()` reading `COOLIFY_BRANCH`.
- Gotcha: Coolify sets `COOLIFY_BRANCH` **with literal quotes** (e.g. `"pull/131/head"`) — strip quotes
before matching `/^pull\/(\d+)/`. Also the value has a `/head` suffix.
- Verify data freshness: `GET /api/pilot/ricolie` returns `cost` (old=4 on 20-pt scale, new=11 on 50-pt scale).
- Preview domains: `131.dev.m3tacron.com` (frontend) / `131.dev.api.m3tacron.com` (backend, internal only).
2 changes: 1 addition & 1 deletion external_data
19 changes: 17 additions & 2 deletions frontend/src/routes/api/[...path]/+server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,27 @@ function normalizeBackendApiBase(raw) {
return trimmed.endsWith('/api') ? trimmed : `${trimmed}/api`;
}

function previewBackendHost() {
// PR-specific backend containers are reachable as `backend-pr-<N>` on the
// shared coolify network. The generic `backend` alias belongs to the old
// shared backend, so previews must target their own container.
// COOLIFY_BRANCH is like `pull/131/head` but Coolify quotes the value
// (e.g. `"pull/131/head"`), so strip any surrounding quotes first.
const branch = String(process.env.COOLIFY_BRANCH || '').replace(/^"|"$/g, '');
const fromBranch = branch.match(/^pull\/(\d+)/);
if (fromBranch) {
return `backend-pr-${fromBranch[1]}`;
}
return null;
}

function resolvePreviewBackendApiBase(requestUrl) {
try {
const host = new URL(requestUrl).hostname;
const match = host.match(/^(\d+)\.dev\.m3tacron\.com$/);
if (match) {
// Preview deployment - talk directly to local backend container via docker network
return 'http://backend:8888/api';
return `http://backend-pr-${match[1]}:8888/api`;
}
} catch {
// Fall through to the default proxy target.
Expand Down Expand Up @@ -42,7 +56,8 @@ export async function GET({ params, url, fetch, request }) {
const path = params.path || '';
// Preview deployments: always use internal Docker network to reach backend
const isPreview = process.env.ENV_VAR_SOURCE === 'preview' || (process.env.COOLIFY_BRANCH || '').startsWith('pull/');
const backendBase = isPreview ? 'http://backend:8888/api' : (resolveBackendApiBase() || resolvePreviewBackendApiBase(request.url));
const previewHost = isPreview ? previewBackendHost() : null;
const backendBase = previewHost ? `http://${previewHost}:8888/api` : (resolveBackendApiBase() || resolvePreviewBackendApiBase(request.url));
const target = new URL(`${backendBase}/${path}`);

for (const [key, value] of url.searchParams.entries()) {
Expand Down