Skip to content

Commit 6f1fc9e

Browse files
feat: add hub-worker binary and refactor config to cleanenv (#50)
* chore: split into api and worker * chore: lint fix * chore: renamed property * chore: river fix * chore: expose additional river config * chore: improvements * chore: align run-worker with run (allow env vars without .env) Made-with: Cursor * chore: rename * chore: fix stale comment in backfill-embeddings referencing workers in API * docs: fix AGENTS.md run-worker to reflect .env is optional * chore: fixes --------- Co-authored-by: Bhagya Amarasinghe <b.sithumini@yahoo.com>
1 parent 8e6255c commit 6f1fc9e

22 files changed

Lines changed: 884 additions & 678 deletions

.env.example

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Formbricks Hub Configuration
2-
# Copy this file to .env and update the values as needed
2+
# Copy this file to .env and update the values as needed.
3+
# Config is loaded via cleanenv: .env is optional; environment variables override .env values.
34

45
# API Key for authentication (required)
56
# This key is used to authenticate requests to protected endpoints
@@ -40,6 +41,16 @@ MESSAGE_PUBLISHER_PER_EVENT_TIMEOUT_SECONDS=10
4041
# Graceful shutdown timeout in seconds (optional). Default: 30
4142
SHUTDOWN_TIMEOUT_SECONDS=30
4243

44+
# River worker (hub-worker only). API does not run workers; these affect job execution and cleanup.
45+
# RIVER_JOB_TIMEOUT_SECONDS: max time a job may run before context is cancelled. 0 = River default (1m).
46+
# RIVER_RESCUE_STUCK_JOBS_AFTER_SECONDS: time after which a running job is considered stuck and retried/discarded. 0 = River default (1h).
47+
# RIVER_COMPLETED_JOB_RETENTION_SECONDS: how long to keep completed jobs before cleanup; -1 = disable. Default: 86400 (24h).
48+
# RIVER_CLIENT_ID: optional identifier for this worker (logs, attempted_by); empty = auto-generated.
49+
# RIVER_JOB_TIMEOUT_SECONDS=0
50+
# RIVER_RESCUE_STUCK_JOBS_AFTER_SECONDS=0
51+
# RIVER_COMPLETED_JOB_RETENTION_SECONDS=86400
52+
# RIVER_CLIENT_ID=
53+
4354
# Webhook max fan-out per event (optional)
4455
# Max number of webhook jobs enqueued per event; excess is capped and logged. Default: 500
4556
WEBHOOK_MAX_FAN_OUT_PER_EVENT=500

.github/workflows/api-contract-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787

8888
- name: Start API server
8989
run: |
90-
./bin/api &
90+
./bin/hub-api &
9191
API_PID=$!
9292
echo "API_PID=$API_PID" >> $GITHUB_ENV
9393
@@ -147,4 +147,4 @@ jobs:
147147
if [ ! -z "$API_PID" ]; then
148148
kill $API_PID || true
149149
fi
150-
pkill -f "./bin/api" || true
150+
pkill -f "./bin/hub-api" || true

AGENTS.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
# Repository Guidelines
22

33
## Project Structure & Module Organization
4-
- `cmd/api/` holds the API server (package `main`: `main.go`, `app.go`). Build/run the package, e.g. `go run ./cmd/api` or `make run`.
5-
- `internal/` contains core application layers: `api/handlers`, `api/middleware`, `service`, `repository`, `models`, and `config`.
4+
- `cmd/api/` holds the API server (hub-api): HTTP API, ingestion, record retrieval, tenant/auth; enqueues jobs to River (insert-only). Build/run: `go run ./cmd/api` or `make run`.
5+
- `cmd/worker/` holds the worker (hub-worker): runs River job workers (webhook delivery, embeddings). No HTTP. Build/run: `go run ./cmd/worker` or `make run-worker`.
6+
- `internal/` contains core application layers: `api/handlers`, `api/middleware`, `service`, `repository`, `models`, `config`, and `workers`.
67
- `pkg/` provides shared utilities (currently `pkg/database`).
78
- `migrations/` stores SQL migration files (goose); use `-- +goose up` / `-- +goose down` annotations.
89
- `tests/` contains integration tests.
910

1011
## Build, Test, and Development Commands
1112
- `make dev-setup`: start Postgres via Docker, install Go deps/tools, and initialize database schema.
12-
- `make run`: create a default `.env` if missing and run the API server.
13-
- `make build`: build the API binary to `bin/api`.
13+
- `make run`: run hub-api (config from `.env` if present and environment variables; copy `.env.example` to `.env` or set env vars).
14+
- `make run-worker`: run hub-worker (requires DATABASE_URL from `.env` or environment variables).
15+
- `make build`: build both `bin/hub-api` and `bin/hub-worker`. Use `make build-api` or `make build-worker` for a single binary.
1416
- `make tests`: run integration tests in `tests/`.
1517
- `make tests-coverage`: generate `coverage.html`.
16-
- `make check-coverage`: run all tests with coverage and fail if below COVERAGE_THRESHOLD (excludes cmd/api: app.go, main.go).
18+
- `make check-coverage`: run all tests with coverage and fail if below COVERAGE_THRESHOLD (excludes cmd/api and cmd/worker main packages).
1719
- `make init-db`: run goose migrations up using `DATABASE_URL`. `make migrate-status` and `make migrate-validate` for status and validation. New migrations go in `migrations/` with goose annotations (`-- +goose up` / `-- +goose down`). Name files with a sequential number and short description (e.g. `002_add_webhooks_table.sql`); goose orders by the numeric prefix. For webhook delivery, run `make river-migrate` after `init-db` to apply River job queue migrations.
1820
- `make fmt`: format code (runs `golangci-lint run --fix`; uses gofumpt/gci from config).
1921
- `make lint`: run `golangci-lint` (includes format checks; requires `make install-tools`).

Dockerfile

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,26 @@ RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go install github.com/pr
1818
COPY go.mod go.sum ./
1919
RUN go mod download
2020

21-
# Build the application
21+
# Build the application (hub-api and hub-worker)
2222
COPY . .
23-
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /build/bin/api ./cmd/api
23+
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /build/bin/hub-api ./cmd/api && \
24+
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /build/bin/hub-worker ./cmd/worker
2425

2526
# =============================================================================
26-
# Stage 2: Runtime
27+
# Stage 2: Runtime (default: hub-api)
2728
# =============================================================================
2829
FROM alpine:3.21
2930

30-
RUN apk add --no-cache ca-certificates tzdata
31+
RUN apk add --no-cache ca-certificates tzdata wget
3132

3233
# Create non-root user
3334
RUN addgroup -S app && adduser -S app -G app
3435

3536
WORKDIR /app
3637

37-
# Copy binary and migration tools from builder
38-
COPY --from=builder /build/bin/api /app/api
38+
# Copy binaries and migration tools from builder
39+
COPY --from=builder /build/bin/hub-api /app/hub-api
40+
COPY --from=builder /build/bin/hub-worker /app/hub-worker
3941
COPY --from=builder /go/bin/goose /usr/local/bin/goose
4042
COPY --from=builder /go/bin/river /usr/local/bin/river
4143

@@ -47,4 +49,10 @@ USER app
4749

4850
EXPOSE 8080
4951

50-
ENTRYPOINT ["/app/api"]
52+
# Health check for hub-api. Disable or override when running hub-worker (e.g. docker run ... hub-worker)
53+
# since workers do not expose HTTP.
54+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
55+
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
56+
57+
# Default: run hub-api. Override with command to run hub-worker: docker run ... hub-worker
58+
ENTRYPOINT ["/app/hub-api"]

Makefile

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: all test help tests tests-coverage check-coverage build build-backfill-embeddings run run-backfill-embeddings init-db clean docker-up docker-down docker-clean deps install-tools fmt lint lint-new lint-openapi dev-setup test-all test-unit schemathesis install-hooks migrate-status migrate-validate river-migrate
1+
.PHONY: all test help tests tests-coverage check-coverage build build-api build-worker build-backfill-embeddings run run-worker run-backfill-embeddings init-db clean docker-up docker-down docker-clean deps install-tools fmt lint lint-new lint-openapi dev-setup test-all test-unit schemathesis install-hooks migrate-status migrate-validate river-migrate
22

33
# Aliases for checkmake/lint expectations
44
all: build
@@ -9,9 +9,12 @@ help:
99
@echo "Available targets:"
1010
@echo " make help - Show this help message"
1111
@echo " make dev-setup - Set up development environment (docker, deps, tools, schema, hooks)"
12-
@echo " make build - Build the API server"
12+
@echo " make build - Build hub-api and hub-worker"
13+
@echo " make build-api - Build hub-api only (bin/hub-api)"
14+
@echo " make build-worker - Build hub-worker only (bin/hub-worker)"
1315
@echo " make build-backfill-embeddings - Build the backfill-embeddings command"
14-
@echo " make run - Run the API server"
16+
@echo " make run - Run the API server (hub-api)"
17+
@echo " make run-worker - Run the worker (hub-worker)"
1518
@echo " make run-backfill-embeddings - Run the backfill-embeddings command (enqueues embedding jobs; loads .env)"
1619
@echo " make test-unit - Run unit tests (fast, no database)"
1720
@echo " make tests - Run integration tests"
@@ -79,11 +82,21 @@ check-coverage:
7982
fi && \
8083
echo "✅ Coverage $$COV% meets threshold $(COVERAGE_THRESHOLD)%"
8184

82-
# Build the API server
83-
build:
84-
@echo "Building API server..."
85-
go build -o bin/api ./cmd/api
86-
@echo "Binary created: bin/api"
85+
# Build hub-api and hub-worker
86+
build: build-api build-worker
87+
@echo "Binaries created: bin/hub-api, bin/hub-worker"
88+
89+
# Build the API server (hub-api)
90+
build-api:
91+
@echo "Building hub-api..."
92+
go build -o bin/hub-api ./cmd/api
93+
@echo "Binary created: bin/hub-api"
94+
95+
# Build the worker (hub-worker)
96+
build-worker:
97+
@echo "Building hub-worker..."
98+
go build -o bin/hub-worker ./cmd/worker
99+
@echo "Binary created: bin/hub-worker"
87100

88101
# Build the backfill-embeddings command (enqueues embedding jobs; requires DATABASE_URL)
89102
build-backfill-embeddings:
@@ -96,27 +109,17 @@ run-backfill-embeddings:
96109
@if [ ! -f .env ]; then echo "Error: .env file required. Copy .env.example to .env and configure."; exit 1; fi && \
97110
(set -a && . ./.env && set +a && go run ./cmd/backfill-embeddings)
98111

99-
# Run the API server
112+
# Run the API server (hub-api).
113+
# Config: .env if present, else environment variables; env vars override .env. Copy .env.example to .env or set env vars.
100114
run:
101-
@echo "Checking for .env file..."
102-
@if [ ! -f .env ]; then \
103-
echo "Creating .env file with default values..."; \
104-
echo "# Formbricks Hub Configuration" > .env; \
105-
echo "# Auto-generated by 'make run' - modify as needed" >> .env; \
106-
echo "" >> .env; \
107-
echo "# API Key for authentication (required)" >> .env; \
108-
echo "API_KEY=test-api-key-12345" >> .env; \
109-
echo "" >> .env; \
110-
echo "# Database connection URL (optional: set POSTGRES_PORT in .env if 5432 is in use; keep port in sync here)" >> .env; \
111-
echo "DATABASE_URL=postgres://postgres:postgres@localhost:5432/test_db?sslmode=disable" >> .env; \
112-
echo "" >> .env; \
113-
echo "# Server port (default: 8080)" >> .env; \
114-
echo "PORT=8080" >> .env; \
115-
echo ".env file created with default values."; \
116-
fi
117-
@echo "Starting API server..."
115+
@echo "Starting hub-api..."
118116
go run ./cmd/api
119117

118+
# Run the worker (hub-worker). Config: .env if present, else env vars (same as run). Requires DATABASE_URL; API_KEY not required.
119+
run-worker:
120+
@echo "Starting hub-worker..."
121+
go run ./cmd/worker
122+
120123
# Initialize database schema (run goose migrations up)
121124
init-db:
122125
@echo "Running migrations..."
@@ -200,7 +203,7 @@ deps:
200203

201204
# Install development tools
202205
# Tool versions - update these periodically
203-
GOLANGCI_LINT_VERSION := v2.10.1
206+
GOLANGCI_LINT_VERSION := v2.11.3
204207
GOVULNCHECK_VERSION := v1.1.4
205208
GOOSE_VERSION := v3.27.0
206209
RIVER_VERSION := v0.31.0

0 commit comments

Comments
 (0)