summaryrefslogtreecommitdiff
path: root/scripts/systemd/README.md
blob: 7e3fdaa720dd78d2ccffaacc3e011d609e44a210 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# fund-lab production deployment (systemd --user)

Loopback-only services behind the existing `cloudflared` tunnel. The tunnel
ingress already maps `fund.somhairle.bid -> http://127.0.0.1:8098`.

## Topology

| Component | Listen | Unit / mechanism |
|-----------|--------|------------------|
| API (`FundLab.Api.dll`, Release publish) | `127.0.0.1:5080` | `fundlab-api.service` |
| Web gateway (nginx: static `dist/` + `/api`,`/health` proxy) | `127.0.0.1:5176` (frontend) and `127.0.0.1:8098` (tunnel origin) | `fundlab-gateway.service` |
| PostgreSQL 16 (real data dir, docker named volume) | `127.0.0.1:55435` | docker container `fundlab-pg` (`--restart unless-stopped`) |

Public request path: `cloudflared` -> `127.0.0.1:8098` (gateway) -> static
`src/FundLab.Web/dist` for `/`, and reverse-proxy to the API for `/api/*` and
`/health`.

### Port note (necessary deviation)

The tunnel origin is `8098`, but a single origin must serve **both** the
frontend (`/`) and the API (`/api`, `/health`). The API therefore listens on
`127.0.0.1:5080` and the nginx gateway owns `8098` (and `5176`). `curl
http://127.0.0.1:8098/health` still returns the API health payload because the
gateway proxies it.

## Files

- `fundlab-api.service`, `fundlab-gateway.service` — systemd user units.
- `nginx-fundlab.conf` — gateway config (high ports, unprivileged user; temp
  dirs under `var/fund-lab/`).
- Runtime env/secrets: `.env.deploy` at the repo root (git-ignored via
  `.env.*`; contains `FUND_LAB_DATABASE_URL`, `FUND_LAB_AUTH_TOKEN`,
  `ASPNETCORE_URLS`, `FUND_LAB_AKSHARE_PYTHON`). Never commit it.
- Logs: `var/fund-lab/{api,gateway,nginx-*.log}` (git-ignored via `var/`).

## Build

```sh
# API (Release; FS3511 warning suppressed only for publish)
dotnet publish src/FundLab.Api/FundLab.Api.fsproj -c Release \
  -p:TreatWarningsAsErrors=false -o src/FundLab.Api/publish

# Frontend -> src/FundLab.Web/dist
cd src/FundLab.Web
PATH="$PWD/../../.tools/node-v22.23.2/bin:$PATH" npm run build
```

## Install / run

```sh
systemctl --user link "$PWD/scripts/systemd/fundlab-api.service"
systemctl --user link "$PWD/scripts/systemd/fundlab-gateway.service"
systemctl --user daemon-reload
systemctl --user enable --now fundlab-api.service fundlab-gateway.service
```

`loginctl` linger is already enabled for this user, so the units start on boot.

## Verify

```sh
curl -sS http://127.0.0.1:5080/health
curl -sS http://127.0.0.1:5176/            # frontend HTML
curl -sS http://127.0.0.1:8098/health
curl -sS https://fund.somhairle.bid/health
curl -sS https://fund.somhairle.bid/       # frontend HTML (not Somhairle's Dream)
curl -sS -H "Authorization: Bearer $FUND_LAB_AUTH_TOKEN" \
  https://fund.somhairle.bid/api/portfolio/summary
```

Auth is a static Bearer token (`FUND_LAB_AUTH_TOKEN`); there is no `/login`
endpoint. `/health` is anonymous; everything under `/api` requires the token.