# Railway Deployment SOP for GFAVIP Wallet (Node.js/TypeScript)

**Last Updated:** March 2026
**Stack:** Node.js v20+, TypeScript, Express, PostgreSQL (Drizzle ORM)

This guide documents the correct procedure for deploying the GFAVIP Wallet to Railway, specifically addressing database migration challenges in a CI/CD environment.

---

## 1. Critical "Gotchas" (Read First)

### 🚨 The Interactive Migration Trap
**Problem:** The command `drizzle-kit push` is **interactive**. If schema changes are ambiguous (e.g., renaming a column), it pauses and asks for user input.
**Impact:** On Railway, there is no user to answer. The deployment hangs or proceeds without running the migration, causing the app to crash with "Column does not exist" errors.
**Solution:** Do NOT use `drizzle-kit push` in the start command. Use a custom non-interactive script.

### 🚨 The Start Command Trap
**Problem:** Railway defaults to `npm start`. If your `start` script is just `node dist/index.js`, the app starts **before** the database is updated.
**Solution:** Chain the migration command *before* the start command in `package.json`.

---

## 2. Configuration Setup

### 2.1 `package.json` Scripts
Ensure your `start` script runs the migration first.

```json
"scripts": {
  "build": "vite build && esbuild server/index.ts ...",
  // CORRECT: Runs migration script first, then starts server
  "start": "node scripts/manual-migrate.js && NODE_ENV=production node dist/index.js"
}
```

### 2.2 Migration Script (`scripts/manual-migrate.js`)
Use a pure Node.js script (ESM compatible) that executes raw SQL `IF NOT EXISTS`. This guarantees idempotency and zero prompts.

**Why pure JS?** Avoids `tsx` or `ts-node` dependency issues in the production container.

```javascript
import pg from 'pg';
const { Client } = pg;

async function main() {
  const client = new Client({ connectionString: process.env.DATABASE_URL });
  await client.connect();
  
  // Example: Add column safely
  await client.query(`
    DO $$ 
    BEGIN 
      IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name='agent_owner_id') THEN 
        ALTER TABLE users ADD COLUMN agent_owner_id varchar; 
      END IF; 
    END $$;
  `);
  
  await client.end();
}
main();
```

---

## 3. Railway Project Settings

### 3.1 Environment Variables
Ensure these are set in the Railway Dashboard:

| Variable | Value | Notes |
|----------|-------|-------|
| `DATABASE_URL` | `postgresql://...` | Auto-set by Railway Postgres plugin |
| `NODE_ENV` | `production` | |
| `PORT` | `8080` (or auto) | App listens on `process.env.PORT` |
| `SESSION_SECRET` | `...` | Must match previous deployment to keep logins |
| `COOKIE_DOMAIN` | `.gfavip.com` | Critical for SSO |

### 3.2 Build & Start Commands
*   **Build Command:** `npm install && npm run build`
*   **Start Command:** `npm run start` (or leave blank to use package.json default)

---

## 4. Deployment Workflow

1.  **Code Changes:**
    *   Modify `shared/schema.ts` (if needed).
    *   **CRITICAL:** Update `scripts/manual-migrate.js` to include the raw SQL for any new columns. Drizzle won't do this for you in this setup.

2.  **Commit & Push:**
    ```bash
    git add .
    git commit -m "feat: add new columns and update migration script"
    git push origin main
    ```

3.  **Verify Deployment:**
    *   Watch Railway logs.
    *   Look for: `🚀 Running manual migration...`
    *   Look for: `🎉 Migration complete!`
    *   Look for: `[express] serving on port...`

---

## 5. Troubleshooting

### 5.1 App Crashes / 502 Bad Gateway
*   **Check Logs:** Did the migration run? Did it fail?
*   **Check Connectivity:** Is the database "Active"?
*   **Debug Route:** Visit `https://wallet.gfavip.com/debug/db-check` (if configured) to see connection status and schema validation.

### 5.2 "Column does not exist" Error
*   **Cause:** The migration script didn't run or didn't include the new column.
*   **Fix:** Update `scripts/manual-migrate.js`, commit, and push.

### 5.3 "ETIMEDOUT" Error
*   **Cause:** App cannot reach Database.
*   **Fix:** Restart the PostgreSQL service in Railway. Check if `DATABASE_URL` is correct.

---

## 6. Emergency Manual Fix
If the deployment is broken and you can't push code:

1.  **Install Railway CLI:** `npm i -g @railway/cli`
2.  **Login:** `railway login`
3.  **Link:** `railway link` (select project)
4.  **Run Migration:** `railway run node scripts/manual-migrate.js`
