# Deploy — api.codebyte-software.com (bare-metal + systemd, TLS on :443)

Your setup: **Ubuntu host**, API runs **bare-metal under systemd**, **Mongo in Docker**,
app terminates **TLS on port 443** using your provided **crt + ca + key** files.

Placeholders to replace: `<APP_DIR>` (e.g. `/opt/vt-api`), `<MONGO_CONTAINER>`,
`<APP_DB_PASSWORD>`, `<MONGO_ROOT_PASSWORD>`, and the three cert paths.

> Code change already made for you: the app now supports **`SSL_CA_PATH`** (verified — it
> serves the full chain: leaf cert + CA). So you point three env vars at your three files, no
> concatenation needed. Rebuild the app (Step 5) to pick it up.

---

## 1. DNS check
```bash
dig +short api.codebyte-software.com     # must return your server's public IP
```
Fix the A-record and wait for propagation before continuing.

## 2. Expose the port (firewall)
Only 443 (HTTPS) and SSH should be public. **Do not** expose 3838 or 27017.
```bash
sudo ufw allow OpenSSH
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose      # confirm 443 ALLOW, 27017 NOT listed
```
If your provider has an external security group/firewall (AWS/Hetzner/etc.), open **443** there too.

## 3. MongoDB in Docker — dedicated `vt_db` + `vt_user`

> **Your setup:** container `mongo-server` already hosts an unrelated DB (`andd_db`/`captain`).
> This app gets its **own isolated** DB `vt_db` with user `vt_user` (`readWrite` on `vt_db` →
> `authSource=vt_db`). Steps 3a–3b create and verify it. Replace `<APP_DB_PASSWORD>` with your
> chosen password (avoid `@ : / ? # %` or URL-encode them).

### 3a. Check whether auth is on, then create the user
```bash
# Is auth enforced?
docker exec -i mongo-server mongosh --quiet --eval \
  'try { db.adminCommand({listDatabases:1}); print("NO_AUTH") } catch(e){ print("AUTH_ON: "+e.codeName) }'

# If AUTH_ON — use your Mongo root creds:
docker exec -i mongo-server mongosh -u <ROOT_USER> -p '<ROOT_PASS>' --authenticationDatabase admin <<'EOF'
db = db.getSiblingDB('vt_db');
db.createUser({ user: 'vt_user', pwd: '<APP_DB_PASSWORD>', roles: [ { role: 'readWrite', db: 'vt_db' } ] });
print('vt_user created');
EOF

# If NO_AUTH — same, without credentials:
docker exec -i mongo-server mongosh <<'EOF'
db = db.getSiblingDB('vt_db');
db.createUser({ user: 'vt_user', pwd: 'ViataLaTara94x07!2026#WithParTyPeople', roles: [ { role: 'readWrite', db: 'vt_db' } ] });
print('vt_user created');
EOF
```

### (reference) Generic check whether auth is already on
```bash
docker ps                                   # note the Mongo container NAME → <MONGO_CONTAINER>

# Does it require auth? Run an admin command with NO credentials:
docker exec -i <MONGO_CONTAINER> mongosh --quiet --eval \
  'try { db.adminCommand({listDatabases:1}); print("NO_AUTH") } catch(e){ print("AUTH_ON: "+e.codeName) }'
```
- Prints **`NO_AUTH`** → follow **Case B** (auth is off; enable it).
- Prints **`AUTH_ON: Unauthorized`** → follow **Case A** (root user already exists).

### Case A — auth already enabled (root user exists)
Create just the application database + user (readWrite on `vt_api`):
```bash
docker exec -i <MONGO_CONTAINER> mongosh -u root -p '<MONGO_ROOT_PASSWORD>' --authenticationDatabase admin <<'EOF'
use vt_api
db.createUser({
  user: "vtuser",
  pwd:  "<APP_DB_PASSWORD>",
  roles: [ { role: "readWrite", db: "vt_api" } ]
})
EOF
```

### Case B — no auth yet (secure it)
1. Create a root admin **and** the app user (works while auth is still off):
```bash
docker exec -i <MONGO_CONTAINER> mongosh --quiet <<'EOF'
use admin
db.createUser({ user: "root", pwd: "<MONGO_ROOT_PASSWORD>", roles: ["root"] })
use vt_api
db.createUser({ user: "vtuser", pwd: "<APP_DB_PASSWORD>", roles: [ { role: "readWrite", db: "vt_api" } ] })
EOF
```
2. **Turn auth ON.** The container must run with `--auth`. If it was started via
   `docker run`, recreate it with auth + a **localhost-only** published port and a persistent volume:
```bash
docker stop <MONGO_CONTAINER> && docker rm <MONGO_CONTAINER>
docker run -d --name vt_mongo --restart unless-stopped \
  -p 127.0.0.1:27017:27017 \
  -v vt_mongo_data:/data/db \
  mongo:7 --auth
```
   (If you use a compose file instead, add `command: ["--auth"]` and
   `ports: ["127.0.0.1:27017:27017"]`, then `docker compose up -d`.)
   Your existing data on the named volume is preserved; the users you created in step 1 remain.

3. Re-run the check from 3a — it should now print `AUTH_ON: Unauthorized`.

### 3b. Confirm the app user works + port binding
```bash
# The app (on the host) reaches Mongo via the published localhost port:
docker exec -i mongo-server mongosh \
  "mongodb://vt_user:<APP_DB_PASSWORD>@localhost:27017/vt_db?authSource=vt_db" \
  --quiet --eval 'db.getName(); print("VT_USER_OK")'

# Verify the container publishes 27017 to the host (needed since the API is bare-metal).
# Expect 127.0.0.1:27017 (preferred) or 0.0.0.0:27017. If it prints nothing, republish it.
docker port mongo-server
```
Your connection string (user `vt_user` lives in `vt_db`, so `authSource=vt_db`):
```
mongodb://vt_user:<APP_DB_PASSWORD>@127.0.0.1:27017/vt_db?authSource=vt_db
```

## 4. Place the SSL cert files
Put the three files somewhere stable and make them readable by the service user only.
```bash
sudo mkdir -p /etc/ssl/vt-api
sudo cp your.key  /etc/ssl/vt-api/privkey.key
sudo cp your.crt  /etc/ssl/vt-api/domain.crt
sudo cp your.ca   /etc/ssl/vt-api/ca-bundle.ca

# Service user (created in step 6) must read them; the private key stays non-world-readable:
sudo chown root:vtapi /etc/ssl/vt-api/*
sudo chmod 640 /etc/ssl/vt-api/privkey.key
sudo chmod 644 /etc/ssl/vt-api/domain.crt /etc/ssl/vt-api/ca-bundle.ca
```

## 4b. Image upload directory (server-side WebP compression)
The API compresses uploaded images and writes them to `UPLOAD_DIR`. Use a **persistent
path outside `<APP_DIR>`** so redeploys never wipe user images, and make the service user
own it:
```bash
sudo mkdir -p /var/lib/vt-api/uploads
sudo chown -R vtapi:vtapi /var/lib/vt-api
```
Your `.env` already points there (`UPLOAD_DIR=/var/lib/vt-api/uploads`,
`PUBLIC_BASE_URL=https://api.codebyte-software.com`). The systemd unit in Step 6 grants
write access to this path — don't forget it, or the app can't save images.
Include `/var/lib/vt-api/uploads` in your **backups** (it's user data now).

## 5. Get the app on the server, configure, build
```bash
sudo mkdir -p <APP_DIR> && cd <APP_DIR>
# clone or copy your repo here, then:
npm ci
npm run build            # compiles to dist/  (picks up the new SSL_CA_PATH support)
```

Create `<APP_DIR>/.env` (mode 600). Generate two DIFFERENT secrets first:
```bash
echo "JWT_ACCESS_SECRET=$(openssl rand -base64 48)"
echo "JWT_REFRESH_SECRET=$(openssl rand -base64 48)"
```
`.env`:
```dotenv
NODE_ENV=production
PORT=443
MONGO_URI=mongodb://vt_user:<APP_DB_PASSWORD>@127.0.0.1:27017/vt_db?authSource=vt_db
CORS_ORIGINS=https://app.codebyte-software.com
JWT_ACCESS_SECRET=<paste>
JWT_REFRESH_SECRET=<paste different>
ACCESS_TTL=15m
REFRESH_TTL=30d
# Firebase (omit if you're not using social login / push yet)
FIREBASE_SERVICE_ACCOUNT_PATH=<APP_DIR>/firebase-service-account.json
# SSL — all three of your files:
SSL_KEY_PATH=/etc/ssl/vt-api/privkey.key
SSL_CERT_PATH=/etc/ssl/vt-api/domain.crt
SSL_CA_PATH=/etc/ssl/vt-api/ca-bundle.ca
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=100
AUTH_RATE_LIMIT_MAX=10
```
```bash
chmod 600 <APP_DIR>/.env
# Seed the categories the app expects (run once):
npm run seed
```
> Set `CORS_ORIGINS` to the exact origins of any **web** frontends (comma-separated, no `*`).
> The Flutter mobile app doesn't need a CORS entry.

## 6. systemd service (runs as non-root, allowed to bind :443)
Create the service user and the unit:
```bash
sudo useradd --system --no-create-home --shell /usr/sbin/nologin vtapi || true
sudo chown -R vtapi:vtapi <APP_DIR>
```
`/etc/systemd/system/vt-api.service`:
```ini
[Unit]
Description=Viata la Tara API
After=network.target docker.service
Wants=docker.service

[Service]
Type=simple
User=vtapi
WorkingDirectory=<APP_DIR>
EnvironmentFile=<APP_DIR>/.env
ExecStart=/usr/bin/node dist/server.js
Restart=on-failure
RestartSec=5
# Allow binding to privileged port 443 without running as root:
AmbientCapabilities=CAP_NET_BIND_SERVICE
# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
# App dir AND the persistent upload dir must be writable:
ReadWritePaths=<APP_DIR> /var/lib/vt-api/uploads

[Install]
WantedBy=multi-user.target
```
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now vt-api
sudo systemctl status vt-api --no-pager
journalctl -u vt-api -f          # expect: "Server listening on https://0.0.0.0:443 (production)" + "[db] connected"
```

## 7. Smoke test through the real domain
```bash
curl -s https://api.codebyte-software.com/health
# → {"data":{"status":"ok"},"error":null}

# Chain must validate with no -k flag (this is what mobile clients require):
echo | openssl s_client -connect api.codebyte-software.com:443 -servername api.codebyte-software.com 2>/dev/null | grep "Verify return code"
# → Verify return code: 0 (ok)

# Full auth round-trip:
curl -s -X POST https://api.codebyte-software.com/api/auth/register \
  -H 'Content-Type: application/json' \
  -d '{"email":"admin@codebyte-software.com","password":"changeme123","name":"Admin"}'
curl -s https://api.codebyte-software.com/api/categories | head -c 200
```
Point the Flutter app at `https://api.codebyte-software.com` and `wss://api.codebyte-software.com/ws`.

## 8. Redeploy after code changes
```bash
cd <APP_DIR> && git pull && npm ci && npm run build && sudo systemctl restart vt-api
```
Graceful shutdown is built in (drains HTTP, closes WS, disconnects Mongo), so restarts are safe.

---

## Troubleshooting
| Symptom | Cause / fix |
|---|---|
| `EACCES` / `permission denied 0.0.0.0:443` | `AmbientCapabilities=CAP_NET_BIND_SERVICE` missing or user is root-only; `daemon-reload` + restart. |
| `EACCES` reading the key file | `vtapi` can't read `/etc/ssl/vt-api/privkey.key` → re-check `chown root:vtapi` + `chmod 640`. |
| Client "unable to get local issuer certificate" | CA not being sent → confirm `SSL_CA_PATH` is set and the file is your CA/chain bundle. |
| `[db] could not connect ... after all retries` then exit | Wrong `MONGO_URI` creds/authSource, or Mongo not published to `127.0.0.1:27017`. Test with the mongosh command in 3b. |
| `MongoServerError: Authentication failed` | `authSource` must match where `vtuser` was created (`vt_api` here), password/URL-encode special chars in the password. |
| Config exits immediately at boot | A required env var is missing/invalid (secrets must be ≥32 chars). Check `journalctl -u vt-api`. |
| 502/timeout only on `/ws` | You're not using a proxy here, so this shouldn't occur; if it does, confirm the client uses `wss://` (not `ws://`) against :443. |
```
