Add slim Phase 1 task 1.11 (Dockerfile + Gitea workflow) for pilot deploy

- Multi-stage Dockerfile (Node 22 alpine, BuildKit cache, non-root user).
  HEALTHCHECK and metrics port (9090) deferred until task 1.10 ships;
  comments document the resume.
- .gitea/workflows/build.yml — single build job following the pattern
  of other TRM repos (no services/container, ubuntu-latest direct).
  Tests + typecheck + lint inline; image tagged :main.
- compose.dev.yaml — local-build variant for verifying Dockerfile
  changes pre-push. Production deploy lives in the sibling deploy/ repo.
- .env.example documenting all runtime env vars.
- README updated to point at deploy/ for production and explain CI.
- Task 1.11 marked done (slim variant) in ROADMAP and task file.
This commit is contained in:
2026-04-30 17:29:45 +02:00
parent 90d6a73a60
commit dda53bec16
8 changed files with 282 additions and 3 deletions
+73
View File
@@ -0,0 +1,73 @@
# tcp-ingestion
Node.js TCP server that accepts persistent connections from Teltonika GPS hardware (FMB/FMC/FMM/FMU series), parses Codec 8, 8E, and 16 AVL frames, and publishes normalized `Position` records to a Redis Stream for downstream consumers.
For the full architectural specification see `../docs/wiki/`. For the work plan and task status see `.planning/ROADMAP.md`.
---
## Quick start (local)
**Prerequisites:** Node.js 22+, pnpm, a local Redis instance (or use compose below).
```bash
git clone <repo-url>
cd tcp-ingestion
pnpm install
cp .env.example .env
# Edit .env — at minimum set REDIS_URL
pnpm dev
```
`pnpm dev` uses `tsx watch` for hot-reload during development. The server listens on `TELTONIKA_PORT` (default `5027`).
---
## Test the Docker build locally
`compose.dev.yaml` builds the image from source and runs it next to a Redis container. Useful for verifying Dockerfile changes before pushing:
```bash
docker compose -f compose.dev.yaml up --build
```
For day-to-day development, prefer `pnpm dev` directly — it has hot reload and faster iteration.
---
## Production / stage deployment
This service is **not** deployed standalone. It runs as part of the platform stack defined in the [`deploy/`](https://git.dev.microservices.al/trm/deploy) repo, which Portainer pulls and runs on the stage and production hosts.
The image itself is published to `git.dev.microservices.al/trm/tcp-ingestion:main` on every push to `main` (see CI behavior below). The `deploy/` repo's `compose.yaml` references that image; updates flow through there, not through this repo.
To pin a specific commit in production, set `TCP_INGESTION_TAG=<sha>` in the deploy stack's environment variables.
---
## Environment variables
See `.env.example` for all variables with descriptions and defaults. The only required variable is `REDIS_URL` — all others have sensible defaults.
---
## CI behavior
Gitea Actions workflow is at `.gitea/workflows/build.yml`.
- **Push to `main`** (only when `src/`, `test/`, build config, Dockerfile, or the workflow file itself changes): runs `typecheck`, `lint`, `test`, then builds and pushes the Docker image tagged `:main`. Auto-deploys to stage if a Portainer webhook is configured.
- **Manual trigger** (`workflow_dispatch`): same flow, run on demand.
The workflow uses `secrets.REGISTRY_USERNAME` and `secrets.REGISTRY_PASSWORD` for the Gitea registry login — these must be configured in the repo's (or org's) Actions secrets.
---
## Pilot deployment notes
This service is running in pilot form. The following tasks are **paused** — they are not missing by accident, they are deferred by design to get onto real Teltonika hardware first:
- **Observability (task 1.10):** No `/metrics`, `/healthz`, or `/readyz` HTTP endpoints exist yet. `METRICS_PORT` is in the config schema but nothing listens on it. The Docker `HEALTHCHECK` is also absent for this reason.
- **Production hardening (task 1.12):** Graceful shutdown is a functional stub; uncaught-exception handling is minimal.
- **Device authority (task 1.13):** `AllowAllAuthority` is active — every IMEI is accepted. `STRICT_DEVICE_AUTH=true` is wired but the Redis allow-list refresher is not yet implemented.
See `.planning/ROADMAP.md` for the resume triggers for each deferred task.