63 lines
2.5 KiB
Markdown
63 lines
2.5 KiB
Markdown
|
|
# Daily Quote
|
||
|
|
|
||
|
|
A small full-stack "quote of the day" app, built for the privatecloud VM `private-cloud-daily-quote`.
|
||
|
|
|
||
|
|
## Stack
|
||
|
|
|
||
|
|
- **Frontend**: Vite + React, styled with [MUI](https://mui.com) using a Material Design 3 theme (color roles, type scale, shape/elevation).
|
||
|
|
- **API**: Express (Node.js), serving both `/api/*` and the built SPA on port **3000**.
|
||
|
|
- **Database**: Postgres 16, run via Docker Compose (`quotes`, `users`, `favorites` tables — see `db/init.sql`).
|
||
|
|
- **Platform auth**: [Zitadel](https://zitadel.com), self-hosted via Docker, OIDC Authorization Code + PKCE. Gates "submit a quote" and "favorites"; browsing is public.
|
||
|
|
- **Git hosting**: [Gitea](https://gitea.io), self-hosted via Docker (this repo's `origin`).
|
||
|
|
- **CI/CD**: Gitea Actions, using a local `act_runner` container. See `.gitea/workflows/ci.yml`.
|
||
|
|
- **Observability**: `prom-client` metrics at `/api/metrics`, scraped by Prometheus, visualized in a provisioned Grafana dashboard.
|
||
|
|
- **Backup**: nightly `pg_dump` via a systemd timer (`quoteapp-backup.timer`), 7-day retention, dumps to `~/backups`.
|
||
|
|
|
||
|
|
## Local services (Docker)
|
||
|
|
|
||
|
|
| Service | Internal port | Host port | Notes |
|
||
|
|
|-------------|---------------|-----------|-------|
|
||
|
|
| Postgres | 5432 | 5432 (loopback only) | app + zitadel databases |
|
||
|
|
| Zitadel | 8080 | 8080 | public via `https://<host>:8080` |
|
||
|
|
| Gitea | 3000 | 3001 | public via `https://<host>:3001` |
|
||
|
|
| Gitea SSH | 22 | 2222 (loopback only) | |
|
||
|
|
| act_runner | - | - | registered against Gitea Actions |
|
||
|
|
| Prometheus | 9090 | 9090 (loopback only) | |
|
||
|
|
| Grafana | 3000 | 3002 | public via `https://<host>:3002`, `admin`/`admin` |
|
||
|
|
|
||
|
|
Bring the stack up with:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
docker compose up -d
|
||
|
|
```
|
||
|
|
|
||
|
|
## Running the app
|
||
|
|
|
||
|
|
The app itself runs directly on the VM (not in Docker) as a systemd service on port 3000:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
sudo systemctl status quote-app
|
||
|
|
```
|
||
|
|
|
||
|
|
To rebuild after a frontend change:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
cd client && npm run build
|
||
|
|
sudo systemctl restart quote-app
|
||
|
|
```
|
||
|
|
|
||
|
|
## Auth notes
|
||
|
|
|
||
|
|
Zitadel is reachable at `https://<vm-host>:8080`. Since exe.dev's proxy only marks one
|
||
|
|
port ("3000", the app) fully public, signing in requires being an authorized user of
|
||
|
|
this workspace to reach the Zitadel login page on port 8080 — browsing and reading
|
||
|
|
quotes on the main app is public regardless. A demo user (`demo`) was created for
|
||
|
|
testing; see `.zitadel-demo-user` (not committed) for its password.
|
||
|
|
|
||
|
|
## Backups
|
||
|
|
|
||
|
|
```sh
|
||
|
|
./backup/backup.sh # run manually
|
||
|
|
systemctl list-timers quoteapp-backup.timer
|
||
|
|
```
|