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 @@ + + +
+ + +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.
twala.rahamafresh.comFive 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.
+ +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.Host twala.rahamafresh.com runs everything under Coolify. The tracksolid stack (Coolify
+app bo3nov2ija7g8wn9b1g2paxs) and the RustFS server are co-located:
| Container | Role |
|---|---|
timescale_db-… | Postgres 16 + TimescaleDB — the shared tracksolid_db (target of the migration + ingest) |
dashboard_api-… / dashboard_api_staging | FastAPI 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-runner | CI 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.
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
+
+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.
dashboard_api_rev.py change (new /analytics/fuel-fills endpoints) on the
+ tracksolid repo → Coolify auto-deploys via the Forgejo webhook.15_fleetops/src/index.html change (Fuel Log tab) → Coolify rebuilds the SPA image.staging → main./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.| Env var | Value / source |
|---|---|
DATABASE_URL | postgresql://tracksolid_owner:<pw>@timescale_db:5432/tracksolid_db (internal Docker host) |
RUSTFS_ENDPOINT | https://s3.rahamafresh.com |
RUSTFS_ACCESS_KEY / RUSTFS_SECRET_KEY | RustFS keypair (same store the db_backup service uses) |
RUSTFS_REGION | us-east-1 |
FUEL_BUCKET | fuel |
.env is gitignored and never committed. The RustFS key
+used during development was shared in plaintext and should be rotated.\dt fuel.* shows fuel.records / fuel.ingest_state;
+ \dv reporting.v_fuel_* shows the two views.SELECT count(*), count(*) FILTER (WHERE deleted_at IS NULL) FROM fuel.records;
+ (≈1922 / ≈1888 at first load).SELECT count(*) FILTER (WHERE vehicle_number IS NOT NULL) FROM reporting.v_fuel_fills;
+ — the plate-match rate against tracksolid.devices.curl "https://fleetapi.rahamafresh.com/analytics/fuel-fills?period=90d" →
+ totals / rows / by_department / trend populated;
+ /analytics/filters includes departments.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.