# Meezi — Deployment Guide

## Architecture overview

```
Server: 171.22.25.73
│
├── Gitea          :3000   ← source control + CI runner
├── Nexus          :8081   ← package mirror (NuGet, npm, Docker)
│
├── meezi-api      :5080   ← .NET main API
├── meezi-admin-api:5081   ← .NET admin API
├── meezi-web      :3101   ← Next.js cafe owner dashboard
├── meezi-admin-web:3102   ← Next.js super-admin panel
├── meezi-website  :3010   ← Next.js marketing website
├── meezi-koja     :3103   ← Next.js public discovery (Koja)
├── meezi-db       :5434   ← PostgreSQL (not internet-facing)
└── meezi-redis    :6381   ← Redis (not internet-facing)
```

Docker Compose files:

```
docker-compose.yml          main services (postgres, redis, api, web, website, koja)
docker-compose.admin.yml    admin overlay (+admin-api, +admin-web)
docker-compose.mirror.yml   Nexus mirror — run once separately, stays running
docker-compose.caddy.yml    Caddy HTTPS proxy — add when domain is ready
```

---

## First-time setup

### Step 1 — Set the `ENV_FILE` secret in Gitea

Open in browser:
```
http://171.22.25.73:3000/soroushdes/meezi/settings/secrets
```

Click **Add Secret**, name it exactly **`ENV_FILE`**, paste the block below, click Save.

```env
ASPNETCORE_ENVIRONMENT=Production

# ── Database ──────────────────────────────────────────────────────────────────
DB_PASSWORD=YOUR_STRONG_PASSWORD
DB_CONNECTION_STRING=Host=postgres;Port=5432;Database=meezi;Username=meezi;Password=YOUR_STRONG_PASSWORD

# ── JWT ───────────────────────────────────────────────────────────────────────
# Generate: openssl rand -hex 32
JWT_KEY=YOUR_64_CHAR_HEX

# ── Migrations ────────────────────────────────────────────────────────────────
RUN_MIGRATIONS=true

# ── Public URLs ───────────────────────────────────────────────────────────────
# ⚠️  NEXT_PUBLIC_* are baked into Next.js images at build time.
#     Changing them requires a CI re-run to rebuild images.
NEXT_PUBLIC_API_URL=http://171.22.25.73:5080
NEXT_PUBLIC_ADMIN_API_URL=http://171.22.25.73:5081
NEXT_PUBLIC_SITE_URL=http://171.22.25.73:3010
NEXT_PUBLIC_KOJA_URL=http://171.22.25.73:3103

APP_QR_BASE_URL=http://171.22.25.73:3101
BILLING_DASHBOARD_URL=http://171.22.25.73:3101

# ── CORS ──────────────────────────────────────────────────────────────────────
CORS_ORIGIN_0=http://171.22.25.73:3101
CORS_ORIGIN_1=http://171.22.25.73:3010
CORS_ORIGIN_2=http://171.22.25.73:3103
CORS_ADMIN_ORIGIN_0=http://171.22.25.73:3102

# ── Host ports ────────────────────────────────────────────────────────────────
API_PORT=5080
ADMIN_API_PORT=5081
WEB_PORT=3101
ADMIN_WEB_PORT=3102
WEBSITE_PORT=3010
KOJA_PORT=3103
POSTGRES_PORT=5434
REDIS_PORT=6381

# ── Payment: ZarinPal ─────────────────────────────────────────────────────────
ZARINPAL_MERCHANT_ID=
ZARINPAL_SANDBOX=true

# ── SMS: Kavenegar ────────────────────────────────────────────────────────────
# Empty = OTP printed to API container logs (ok for testing)
KAVENEGAR_API_KEY=

# ── Snappfood webhook ─────────────────────────────────────────────────────────
SNAPPFOOD_WEBHOOK_SECRET=YOUR_RANDOM_SECRET
```

> The actual generated values were set directly in Gitea during initial setup.
> To view or rotate them: Gitea → Settings → Secrets → ENV_FILE → Edit.

---

### Step 2 — Trigger the first deployment

On the server:

```bash
cd ~/meezi
git pull origin main
git push gitea main
```

Watch the pipeline:
```
http://171.22.25.73:3000/soroushdes/meezi/actions
```

CI takes ~5–10 minutes: builds 6 Docker images, runs all checks, then deploys.

---

## Service URLs (no domain, IP-based)

| Service | URL |
|---|---|
| Marketing website | http://171.22.25.73:3010/fa |
| Cafe owner dashboard | http://171.22.25.73:3101/fa/login |
| Public Koja | http://171.22.25.73:3103/fa |
| Super-admin panel | http://171.22.25.73:3102/fa/admin/login |
| Main API (Swagger) | http://171.22.25.73:5080/swagger |
| Admin API (Swagger) | http://171.22.25.73:5081/swagger |
| Gitea | http://171.22.25.73:3000 |
| Nexus | http://171.22.25.73:8081 |

---

## Day-to-day: pushing code

```bash
git add .
git commit -m "your message"
git push origin main     # GitHub backup
git push gitea main      # ← triggers CI + auto-deploy
```

Every push to `main` on Gitea runs all CI jobs. If all pass, deploy runs automatically.

---

## Checking containers on the server

```bash
# Status of all app containers
docker compose -f docker-compose.yml -f docker-compose.admin.yml ps

# Live logs for a service
docker compose logs -f api
docker compose logs -f web
docker compose logs -f admin-api

# All containers on the machine
docker ps
```

---

## When domain is ready (tomorrow)

### 1. Point DNS at the server

Create these A records — all pointing to `171.22.25.73`:

| Hostname | Service |
|---|---|
| `meezi.ir` | Marketing website |
| `app.meezi.ir` | Cafe dashboard |
| `api.meezi.ir` | Main API |
| `koja.meezi.ir` | Koja |
| `admin.meezi.ir` | Admin panel |
| `admin-api.meezi.ir` | Admin API |

### 2. Update `ENV_FILE` secret in Gitea

Remove the IP-based section and replace with:

```env
# ── Domain ────────────────────────────────────────────────────────────────────
DOMAIN=meezi.ir
ACME_EMAIL=you@example.com

NEXT_PUBLIC_API_URL=https://api.meezi.ir
NEXT_PUBLIC_ADMIN_API_URL=https://admin-api.meezi.ir
NEXT_PUBLIC_SITE_URL=https://meezi.ir
NEXT_PUBLIC_KOJA_URL=https://koja.meezi.ir

APP_QR_BASE_URL=https://app.meezi.ir
BILLING_DASHBOARD_URL=https://app.meezi.ir

CORS_ORIGIN_0=https://app.meezi.ir
CORS_ORIGIN_1=https://meezi.ir
CORS_ORIGIN_2=https://koja.meezi.ir
CORS_ADMIN_ORIGIN_0=https://admin.meezi.ir

# Remove all PORT= lines — Caddy is the only public endpoint
```

### 3. Update CI deploy steps

In `.gitea/workflows/ci-cd.yml` find the deploy job and add `docker-compose.caddy.yml`:

```yaml
- name: Start all services
  run: |
    docker compose \
      -f docker-compose.yml \
      -f docker-compose.admin.yml \
      -f docker-compose.caddy.yml \
      up -d --remove-orphans
```

Open port 80 and 443 in the server firewall:
```bash
ufw allow 80
ufw allow 443
```

### 4. Push to Gitea

```bash
git push gitea main
```

CI rebuilds all images with the new domain URLs baked in. Caddy starts and gets
Let's Encrypt certificates automatically — no certbot or manual renewal needed.

---

## Secrets to rotate before going live

| Secret | How to generate |
|---|---|
| `JWT_KEY` | `openssl rand -hex 32` |
| `DB_PASSWORD` | Strong random password — update both `DB_PASSWORD=` and inside `DB_CONNECTION_STRING=` |
| `ZARINPAL_MERCHANT_ID` | panel.zarinpal.com → API → MerchantID |
| `ZARINPAL_SANDBOX` | Change to `false` for live payments |
| `KAVENEGAR_API_KEY` | kavenegar.com dashboard |

After updating any secret in Gitea: push a commit to trigger a redeploy.

---

## Mirror server (Nexus)

Nexus runs separately and should always be running:

```bash
# Start (first time or after server reboot)
docker compose -f docker-compose.mirror.yml up -d

# Health check
curl -s http://localhost:8081/service/rest/v1/status
```

Provisioned repos:

| Repo | Type | Upstream |
|---|---|---|
| `nuget-group` | NuGet group | Liara → Runflare fallback |
| `npm-group` | npm group | Liara → Runflare fallback |
| `docker-hub-proxy` | Docker proxy :5000 | Docker Hub |
| `mcr-proxy` | Docker proxy :5002 | mcr.microsoft.com |
| `pypi-proxy` | PyPI proxy | Liara |
| `ubuntu-proxy` | APT proxy | Liara (jammy) |
| `ubuntu-security-proxy` | APT proxy | Liara (jammy-security) |

To add new mirrors or switch upstreams:
```bash
./mirrors/nexus/add-liara-mirrors.sh
./mirrors/nexus/update-docker-upstream.sh
```
