🚧 Add frontend
This commit is contained in:
@@ -4,15 +4,16 @@ A Portainer / docker compose stack that:
|
||||
|
||||
1. **mqtt-listener** — subscribes to MQTT topics on the form `/<prefix>/<imei>`, injects `imei` and a `device` name into each JSON object of the payload (a single object or an array of objects), and stores every object as its own row in PostgreSQL.
|
||||
2. **api** — a FastAPI read API over the database, protected by app tokens stored in the database as SHA-256 digests.
|
||||
3. **db** — PostgreSQL 16 storing the messages (JSONB payloads) and the config tables (`app_tokens`, `imei_map`).
|
||||
3. **ui** — a Vue management frontend (own nginx container) that proxies `/api` to the api: devices & settings, attribute timelines, tokens, message browsing.
|
||||
4. **db** — PostgreSQL 16 storing the messages (JSONB payloads) and the config tables (`app_tokens`, `imei_map`, `imei_attributes`).
|
||||
|
||||
```
|
||||
┌─────────────┐ /xxx/IMEI ┌───────────────┐ INSERT ┌────┐
|
||||
│ MQTT broker │ ──────────────► │ mqtt-listener │ ──────────► │ db │
|
||||
└─────────────┘ JSON payloads └───────────────┘ └────┘
|
||||
injects imei + device ▲
|
||||
┌─────────────┐ GET /messages ┌───────────────┐ SELECT │
|
||||
│ API clients │ ──────────────► │ api │ ──────────────┘
|
||||
┌─────────────┐ /api (nginx) ┌───────────────┐ SELECT │
|
||||
│ Browser (ui) │ ──────────────► │ api │ ──────────────┘
|
||||
└─────────────┘ X-App-Token └───────────────┘
|
||||
```
|
||||
|
||||
@@ -21,9 +22,10 @@ A Portainer / docker compose stack that:
|
||||
```
|
||||
docker-compose.yml stack definition (paste into Portainer)
|
||||
.env.example environment variables (POSTGRES_PASSWORD etc.)
|
||||
init/01_schema.sql creates mqtt_messages + app_tokens + imei_map on first db start
|
||||
init/01_schema.sql creates mqtt_messages + app_tokens + imei_map + imei_attributes on first db start
|
||||
listener/ mqtt -> db service (paho-mqtt, psycopg)
|
||||
api/ FastAPI service (uvicorn)
|
||||
frontend/ Vue management UI (own nginx container, proxies /api)
|
||||
mosquitto/ config for the optional test broker (profile "test")
|
||||
tools/ host-side test scripts and pytest suite
|
||||
```
|
||||
@@ -35,7 +37,7 @@ cp .env.example .env # then edit POSTGRES_PASSWORD
|
||||
docker compose --profile test up -d --build
|
||||
```
|
||||
|
||||
This starts db, mqtt-listener, api and an anonymous Mosquitto broker (ports 1883 → host 2883, API 18080 → host 18080). Set `BOOTSTRAP_APP_TOKEN` to a long random value in `.env` first — it seeds the initially empty `app_tokens` table on the first authenticated request. Then publish some messages:
|
||||
This starts db, mqtt-listener, api, ui and an anonymous Mosquitto broker (ports 1883 → host 2883, API 18080 → host 18080, UI 18090 → host 18090). Set `BOOTSTRAP_APP_TOKEN` to a long random value in `.env` first — it seeds the initially empty `app_tokens` table on the first authenticated request. Then publish some messages:
|
||||
|
||||
```bash
|
||||
pip install -r tools/requirements.txt
|
||||
@@ -58,6 +60,20 @@ PASS: valid token accepted, N message(s) returned
|
||||
PASS: count endpoint returned {'count': N}
|
||||
```
|
||||
|
||||
Open the management UI at `http://localhost:18090` and paste the bootstrap token when prompted (it is kept in the browser's localStorage; a real login flow will replace this later). Everything the UI does goes through the same authenticated JSON API — there is no separate backend.
|
||||
|
||||
### Frontend development
|
||||
|
||||
The UI is a plain Vue 3 + TypeScript + Vite app in `frontend/`, served by its own nginx container (it is NOT bundled into the api image). For hot-reload development against a running api:
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev # serves the SPA on :5173, proxying /api to localhost:18080
|
||||
```
|
||||
|
||||
`npm run build` type-checks (vue-tsc) and builds to `frontend/dist`. The container build does exactly this — see `frontend/README.md`.
|
||||
|
||||
## Deploying in Portainer
|
||||
|
||||
1. Create a stack from **Stacks → Add stack**, choose "Web editor", paste the contents of `docker-compose.yml`.
|
||||
@@ -69,14 +85,16 @@ PASS: count endpoint returned {'count': N}
|
||||
3. Because the stack references local files (`init/01_schema.sql`, Dockerfiles), the stack files must be present on the Docker host. Two options:
|
||||
- **Recommended:** put the repository in a folder on the host (e.g. `/opt/nodeledge`) and use **Stacks → Add stack → Repository** pointing at your git repo, or
|
||||
- Upload the files via "Custom template" including all files.
|
||||
4. **Tokens:** set `BOOTSTRAP_APP_TOKEN` (a long random secret) in the stack's environment. On the first authenticated request after a fresh deploy, the api seeds its SHA-256 digest into the empty `app_tokens` table. Rotate to real per-app tokens via `PUT /configs/app_tokens`, then remove the bootstrap variable (it is ignored once the table has rows).
|
||||
5. Deploy the stack. The `db` service runs `init/01_schema.sql` on its first start (an empty volume), creating `mqtt_messages`, `app_tokens` and `imei_map`.
|
||||
6. Verify: Stacks → your stack → check that db, mqtt-listener and api are all healthy (green).
|
||||
4. **Tokens:** set `BOOTSTRAP_APP_TOKEN` (a long random secret) in the stack's environment. On the first authenticated request after a fresh deploy, the api seeds its SHA-256 digest into the empty `app_tokens` table. Rotate to real per-app tokens via the UI (Tokens screen) or `PUT /configs/app_tokens`, then remove the bootstrap variable (it is ignored once the table has rows).
|
||||
5. Deploy the stack. The `db` service runs `init/01_schema.sql` on its first start (an empty volume), creating `mqtt_messages`, `app_tokens`, `imei_map` and `imei_attributes`.
|
||||
6. Verify: Stacks → your stack → check that db, mqtt-listener, api and ui are all healthy (green). The UI is reachable on the `UI_HOST_PORT` (default 18090).
|
||||
|
||||
> The `broker` service only runs when the compose profile `test` is active (Portainer: enable the `test` profile in the stack settings), so production deploys never spin up an extra broker.
|
||||
|
||||
## How the IMEI injection works
|
||||
|
||||
> The UI talks to the exact same endpoints documented below — everything it sends (including the token prompt) is plain `X-App-Token` requests against `/api/...`, so this reference applies to both human and machine clients. When running the ui container the API is reachable under the `/api` prefix (e.g. `http://localhost:18090/api/messages`); direct api access stays on its own port (default 18080).
|
||||
|
||||
- The IMEI is the **last segment** of the topic, e.g. `/vivab/350123456789012` → `350123456789012`.
|
||||
- It must be exactly **15 digits**; anything else (letters, separators, other lengths) is skipped.
|
||||
- It looks the IMEI up in the `imei_map` table and injects the device's settings document's `device` value as the device name; on no mapping the IMEI itself is used. The listener re-reads the table every 5 seconds, so API edits take effect without a restart.
|
||||
|
||||
Reference in New Issue
Block a user