Files
dsutanto 1c7b78b580
CI / lint (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / build (push) Canceled after 0s
ai-dev: graceful shutdown, tests, stdlib context, lint/CI, error logging
- T1: SIGTERM/SIGINT → client.Disconnect(250) → os.Exit(0)
- T2: 11 tests with miniredis mock (handleMessage, getEnv, parseInt, randomString)
- T3: golang.org/x/net/context → stdlib context, math/rand → math/rand/v2
- T4: .golangci.yml + .github/workflows/ci.yml
- T5: Redis SETEX error log includes key and memberID
- Dockerfile: add git for go mod tidy
2026-09-03 12:52:21 +07:00

118 lines
5.2 KiB
Markdown

# Repository Guidelines
## Project Overview
MQTT presence service written in Go. Subscribes to an MQTT topic (`presence`), parses incoming messages (`memberID;...`), and stores presence data in Redis with a TTL. Purpose: real-time member presence tracking for the Backone platform (port of a Django/Python original).
## Architecture & Data Flow
```
MQTT Broker ──publish──▶ MQTT Client (paho.mqtt)
│
▼
handleMessage()
- parse memberID from payload (split on ";")
- truncate memberID to 50 chars
- build JSON {mqtt, ts}
- SETEX to Redis key "presence:<memberID>"
│
▼
Redis
```
Single goroutine architecture. `main()` connects Redis, connects MQTT, subscribes on connect, then blocks forever (`select {}`). All message handling is synchronous in the MQTT callback.
## Key Directories
```
backone-manage-go/
├── mqtt_presence_redis_go/ # ALL source code lives here
│ ├── mqtt_presence_redis_go.go # Single-file application (package main)
│ ├── go.mod / go.sum
│ └── Dockerfile
├── .gitignore
└── README.md
```
No subpackages, no internal/, no cmd/. The entire application is one `.go` file.
## Development Commands
```bash
# Build
cd mqtt_presence_redis_go && go build -o mqtt_presence_redis_go mqtt_presence_redis_go.go
# Run locally (requires MQTT broker + Redis running)
cd mqtt_presence_redis_go && go run mqtt_presence_redis_go.go
# Docker build
cd mqtt_presence_redis_go && docker build -t mqtt-presence-redis-go .
# No Makefile, no CI pipeline, no lint config
```
## Code Conventions & Common Patterns
### Configuration
- **All config via environment variables** — no config files, no viper/yaml.
- Pattern: `getEnv("ENV_KEY", "default")` helper at package level.
- Env vars: `MQTT_HOST`, `MQTT_PORT`, `MQTT_USER`, `MQTT_PASS`, `MQTT_TOPIC_PRESENCE`, `MQTT_REDIS_HOST`, `MQTT_REDIS_PORT`, `MQTT_REDIS_DB`, `MQTT_REDIS_PREFIX`, `MQTT_REDIS_SETEX`, `MQTT_REDIS_PASSWORD`.
### Naming
- Package-level vars: `camelCase` (e.g. `mqttHost`, `redisPrefix`).
- Functions: `camelCase` (e.g. `handleMessage`, `getEnv`, `parseInt`, `randomString`).
- Constants: no `const` block used; inline defaults.
- File naming: `<package_name>.go` matches module directory.
### Error Handling
- `log.Fatalf` for startup failures (Redis ping, MQTT connect).
- `log.Printf` + `return` for runtime errors (message handling).
- No custom error types; no error wrapping.
### Dependencies
- `github.com/eclipse/paho.mqtt.golang` — MQTT client
- `github.com/go-redis/redis/v8` — Redis client
- `golang.org/x/net/context` — context (stdlib `context` preferred in Go 1.7+)
### Patterns
- No interfaces, no DI, no dependency injection.
- No middleware, no routing — single topic subscription.
- No graceful shutdown handling (`select {}` blocks forever).
- `math/rand` for client ID (not `crypto/rand`) — acceptable for non-security use.
- JSON marshaling via `map[string]interface{}` — no typed struct for presence data.
## Important Files
| File | Purpose |
|------|---------|
| `mqtt_presence_redis_go/mqtt_presence_redis_go.go` | Entire application |
| `mqtt_presence_redis_go/go.mod` | Module: `git.proit.id/dsutanto/backone-manage-go/mqtt_presence_redis_go`, Go 1.24.3 |
| `mqtt_presence_redis_go/Dockerfile` | Multi-stage build: `golang:1.24-alpine` → `alpine:3.20` |
## Runtime/Tooling Preferences
- **Go 1.24.3** (specified in go.mod and Dockerfile)
- **No package manager** beyond `go mod`
- **No linter configured** (no `.golangci.yml`)
- **No formatter config** (standard `gofmt` assumed)
- **No CI/CD** — deployment via Docker only
- **Redis 8 client** (`go-redis/v8`) — requires Redis 6+ for SETEX
- **MQTT 3.1.1** via paho client
## Testing & QA
- **No tests exist.** No `*_test.go` files anywhere.
- **No test framework configured.**
- No coverage tooling, no test scripts.
- If adding tests: use standard `testing` package. Table-driven tests for `handleMessage` message parsing logic. Mock Redis with `miniredis` or `go-redis/mock`. Mock MQTT with a test broker or interface-based mock.
## Key Observations for AI Assistants
1. **Single-file architecture** — all changes go in `mqtt_presence_redis_go.go`. No cross-file imports to track.
2. **No tests** — any refactor carries risk. Consider adding tests for `handleMessage` before significant changes.
3. **No graceful shutdown** — `select {}` means no signal handling, no MQTT disconnect on SIGTERM. Docker sends SIGTERM then SIGKILL.
4. **`golang.org/x/net/context`** is used instead of stdlib `context` — this is a legacy import; stdlib `context` is standard since Go 1.7.
5. **Payload parsing** is fragile: splits on `;`, takes first element, truncates to 50 chars. No validation of payload format.
6. **Redis key pattern**: `presence:<memberID>` with configurable prefix and TTL (default 86400s / 24h).
7. **The comment about Django** indicates this is a port — check Python original for behavioral reference if behavior questions arise.