Files
hamkadr/DEPLOY.md
T
soroush.asadi 3c08c1a265
CI/CD / CI · dotnet build (push) Successful in 6m22s
CI/CD / Deploy · hamkadr (push) Failing after 3s
Move ingestion + Telegram/Bale/Divar config to DB-backed admin settings
- AppSetting gains source config: AutoIngestEnabled, IngestIntervalMinutes, Telegram/Bale/Divar enabled+channels/token/queries
- IListingSource.FetchAsync(AppSetting) — sources read config from DB, not IOptions/appsettings; sample source dev-only
- IngestionWorker reads AutoIngest+interval from DB each cycle (toggle at runtime, no redeploy)
- /Admin/Settings gets a 'منابع جمع‌آوری' section; removed Ingestion env/appsettings + compose env vars
- ENV_FILE shrinks to HOST_PORT + POSTGRES_* + ADMIN_PHONE (AI + sources are all in-admin); migration

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 00:44:11 +03:30

108 lines
4.6 KiB
Markdown

# Deploying همکادر / hamkadr.ir
CI/CD via the **soroush method**: push to Gitea → Gitea Actions builds (through the Nexus mirror)
and the self-hosted runner deploys with Docker Compose. nginx (already on the server) terminates
TLS for `hamkadr.ir` and reverse-proxies to the app.
## Architecture & open ports
```
Internet ──443/80──► nginx (host, existing) ──► 127.0.0.1:8090 ──► hamkadr_api (container :8080)
│ internal docker net
hamkadr_db (postgres, no host port)
```
| Port | Open? | Purpose |
|------|-------|---------|
| 22 | ✅ (ideally IP-restricted) | SSH |
| 80 | ✅ | HTTP → 443 redirect + Let's Encrypt ACME |
| 443 | ✅ | HTTPS `hamkadr.ir` |
| 8090 | ❌ host-localhost only | app, reached only by nginx |
| 5432 | ❌ internal docker net only | Postgres — never published |
`ufw` should be exactly: `allow 22, 80, 443`. Nothing else. (80/443 are already open since nginx
serves git./mirror. — no firewall change needed.)
## Files in this repo
| File | Role |
|------|------|
| `Dockerfile` | multi-stage build, images + NuGet via `mirror.soroushasadi.com` |
| `nuget.docker.config` | NuGet → Nexus `nuget-group` |
| `docker-compose.yml` | production stack: `api` (127.0.0.1:${HOST_PORT}) + `db` (internal) + named volume |
| `docker-compose.dev.yml` | local-dev Postgres only (host 5433) for `dotnet run` |
| `.gitea/workflows/ci-cd.yml` | build job + self-hosted deploy (backup → rollback tag → recreate → health-wait) |
| `deploy/nginx-hamkadr.ir.conf` | nginx vhost for hamkadr.ir |
## One-time setup
### 1. DNS
A records → server IP:
```
hamkadr.ir A <server-ip>
www.hamkadr.ir A <server-ip>
```
### 2. Gitea runner
Confirm the `act_runner` on this server has the **`self-hosted:host`** label (the deploy job needs it)
and its user is in the `docker` group. (Already true if other soroush projects deploy here.)
### 3. ENV_FILE secret
Set at `https://git.soroushasadi.com/soroushdes/hamkadr/settings/secrets` → key **`ENV_FILE`**:
`docker-compose.yml` substitutes these into the `api`/`db` services (it derives
`ConnectionStrings__Default` and `Auth__AdminPhone` for you), so the secret is just:
```dotenv
# host port nginx proxies to (must match deploy/nginx-hamkadr.ir.conf)
HOST_PORT=8090
# Postgres — generate a strong password: openssl rand -hex 24
POSTGRES_DB=hamkadr
POSTGRES_USER=hamkadr
POSTGRES_PASSWORD=__CHANGE_ME__
# Platform admin phone (gets the Admin role on login)
ADMIN_PHONE=09XXXXXXXXX
```
> **That's the whole secret.** Everything else — the **AI audit layer** *and* the **channel
> sources** (Telegram channels, Bale bot token, Divar queries, auto-ingest on/off + interval) — is
> configured at runtime in the admin panel (`/Admin/Settings`), stored in the DB. No redeploy to
> change them. Defaults: AI off, mode = Manual, all sources off ⇒ nothing publishes without admin
> review.
> `ASPNETCORE_ENVIRONMENT=Production` is set by the compose file ⇒ only **reference data**
> (roles/cities/districts) is seeded — no demo facilities/shifts.
### 4. nginx vhost + TLS
```bash
sudo cp deploy/nginx-hamkadr.ir.conf /etc/nginx/sites-available/hamkadr.ir
sudo ln -s /etc/nginx/sites-available/hamkadr.ir /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d hamkadr.ir -d www.hamkadr.ir
```
### 5. First deploy
```bash
git push gitea main # add the gitea remote first if needed
```
Watch `https://git.soroushasadi.com/soroushdes/hamkadr/actions`. The app auto-applies EF migrations
on startup and seeds reference data; nginx already proxies hamkadr.ir to it.
## Operations
- **Backups:** every deploy runs `pg_dump``/opt/hamkadr-backups/hamkadr-<timestamp>.sql` before touching containers.
- **Rollback:** the previous image is tagged `mirror.soroushasadi.com/hamkadr/api:rollback` each deploy:
```bash
docker stop hamkadr_api && docker rm hamkadr_api
API_TAG=rollback docker compose up -d --no-deps api
```
- **Rotate a secret:** edit `ENV_FILE` in Gitea, push any commit to redeploy.
- **Logs:** `docker logs -f hamkadr_api`
- **Restore a backup:** `cat /opt/hamkadr-backups/<file>.sql | docker exec -i hamkadr_db psql -U hamkadr -d hamkadr`
## Safety (never do these)
- ❌ `docker compose down -v` — deletes the database volume.
- ❌ bare `docker compose down` / `restart` — would stop other projects on the shared host. The
workflow always uses `--no-deps <service>` and explicit `stop`/`rm`.