From 42e3ed7c50be53a32553d182b3c7e952a9328af1 Mon Sep 17 00:00:00 2001 From: kianiadee Date: Thu, 11 Jun 2026 23:38:11 +0300 Subject: [PATCH] docs: add deployment.html (architecture flow diagram + runbook) Co-Authored-By: Claude Opus 4.8 --- docs/deployment.html | 248 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 docs/deployment.html diff --git a/docs/deployment.html b/docs/deployment.html new file mode 100644 index 0000000..a99731f --- /dev/null +++ b/docs/deployment.html @@ -0,0 +1,248 @@ + + + + + +FleetFuel — Deployment & Architecture + + + +
+ +
+
Deployment & Architecture · 17_fleetfuel
+

FleetFuel — How it runs

+

From the RustFS fuel bucket to the FleetOps Fuel Log tab — the data flow, where each piece runs on the Coolify VPS, and the runbook to deploy it.

+
See also: plan.html (implementation plan) · host: twala.rahamafresh.com
+
+ +

1Solution flow

+

Five stages. The WhatsApp fuel feed is exported to object storage by n8n; FleetFuel pulls it, normalizes +and stores it, the API reads it back, and the SPA renders it.

+ +
+ + + + + + + + + + + + RustFS · bucket + fuel + latest.json + changes/ + + n8n CDC of logistics_department.fuel_records + + + + + fleetfuel + run_migrations.py + import_fuel.py + --snapshot --apply + + Coolify cron · hourly + + + + + tracksolid_db + fuel.records + + normalizers/trigger + reporting.v_fuel_fills + reporting.v_fuel_efficiency + + timescale_db container + + + + + dashboard_api + /analytics/fuel-fills + …/recent + /analytics/filters + + fleetapi.rahamafresh.com + + + + + FleetOps SPA + “Fuel Log” tab + + fleetops.rahamafresh.com → + + + + S3 GET + + upsert + + SELECT + + + HTTP + +
+ owned by fleetfuel / data-plane + read-side (existing repos, edited) + data flow +
+
+ +
The middle three stages all live in the same shared tracksolid_db. FleetFuel +only owns the fuel schema + the two reporting.* views; the API and SPA are existing +repos that gained a fuel surface. This is the same module shape as fleettickets.
+ +

2Where it runs (Coolify VPS)

+

Host twala.rahamafresh.com runs everything under Coolify. The tracksolid stack (Coolify +app bo3nov2ija7g8wn9b1g2paxs) and the RustFS server are co-located:

+ + + + + + + + + + +
ContainerRole
timescale_db-…Postgres 16 + TimescaleDB — the shared tracksolid_db (target of the migration + ingest)
dashboard_api-… / dashboard_api_stagingFastAPI read-API (fleetapi.rahamafresh.com / staging)
ingest_worker-…, webhook_receiver-…Tracksolid telematics ingestion (poll + push)
db_backup-…Nightly DB dump → RustFS (already holds RUSTFS_* env)
rustfs-…The S3-compatible object store serving the fuel bucket
forgejo-runnerCI runner for repo.rahamafresh.com (auto-deploys on push)
+

FleetFuel deploys as a new Coolify resource in the same project so it shares the internal Docker +network and can reach timescale_db:5432 and the RustFS endpoint.

+ +

3Deployment runbook

+ +

3a · First-time: schema + backfill

+

One-off, to create the fuel schema and load the full history.

+
# on the VPS, in the fleetfuel checkout (pulled from repo.rahamafresh.com)
+uv sync                              # installs psycopg2-binary + boto3
+cp .env.example .env && $EDITOR .env  # DATABASE_URL + RUSTFS_* (see §4)
+
+python run_migrations.py             # creates fuel.* + reporting.v_fuel_fills/efficiency (idempotent)
+python import_fuel.py --snapshot     # DRY-RUN: parse + log counts, writes nothing
+python import_fuel.py --snapshot --apply   # full reconcile from fuel_records/latest.json
+ +

3b · Recurring: the ingest cron

+

A scheduled container that keeps the DB current. The --snapshot full-reconcile is idempotent and +self-healing (picks up edits + soft-deletes), so a simple hourly run is safe:

+
# hourly, matching the n8n export cadence
+python run_migrations.py && python import_fuel.py --snapshot --apply
+

Lower-latency alternative: import_fuel.py --changes --apply processes only new +fuel_records/changes/*.json since the watermark in fuel.ingest_state.

+ +

3c · Read-side: API + SPA edit

+
    +
  1. Commit + push the dashboard_api_rev.py change (new /analytics/fuel-fills endpoints) on the + tracksolid repo → Coolify auto-deploys via the Forgejo webhook.
  2. +
  3. Commit + push the 15_fleetops/src/index.html change (Fuel Log tab) → Coolify rebuilds the SPA image.
  4. +
  5. Promotion path per repo: feature → stagingmain.
  6. +
+
Order matters: the migration (3a) must run before the API deploy hits +/analytics/fuel-fills. The extended /analytics/filters is savepoint-guarded, so deploying the +API before the migration won’t break the existing Logistics dropdowns — the fuel dropdowns just stay empty +until the view exists.
+ +

4Configuration

+ + + + + + + + + +
Env varValue / source
DATABASE_URLpostgresql://tracksolid_owner:<pw>@timescale_db:5432/tracksolid_db (internal Docker host)
RUSTFS_ENDPOINThttps://s3.rahamafresh.com
RUSTFS_ACCESS_KEY / RUSTFS_SECRET_KEYRustFS keypair (same store the db_backup service uses)
RUSTFS_REGIONus-east-1
FUEL_BUCKETfuel
+
Secret hygiene: the real .env is gitignored and never committed. The RustFS key +used during development was shared in plaintext and should be rotated.
+ +

5Verification

+
    +
  1. Schema: \dt fuel.* shows fuel.records / fuel.ingest_state; + \dv reporting.v_fuel_* shows the two views.
  2. +
  3. Data: SELECT count(*), count(*) FILTER (WHERE deleted_at IS NULL) FROM fuel.records; + (≈1922 / ≈1888 at first load).
  4. +
  5. Join health: SELECT count(*) FILTER (WHERE vehicle_number IS NOT NULL) FROM reporting.v_fuel_fills; + — the plate-match rate against tracksolid.devices.
  6. +
  7. API: curl "https://fleetapi.rahamafresh.com/analytics/fuel-fills?period=90d" → + totals / rows / by_department / trend populated; + /analytics/filters includes departments.
  8. +
  9. SPA: open FleetOps → Fuel Log tab → KPI strip, spend/litres chart, per-vehicle & recent tables render.
  10. +
+ +

6Rollback

+
    +
  • Read-side: revert the two commits (dashboard_api + fleetops); Coolify redeploys the prior image.
  • +
  • Data: the fuel schema is additive and isolated — nothing else reads it, so it can be left + in place or dropped (DROP SCHEMA fuel CASCADE; + DROP VIEW reporting.v_fuel_fills, reporting.v_fuel_efficiency;) + with no impact on the existing Logistics/Tickets surfaces.
  • +
  • Cron: pause/stop the FleetFuel Coolify resource — ingestion halts, existing rows remain.
  • +
+ + + +
+ +