Head of Product & Engineering a40a4aa626
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
feat: initial implementation of Cast GHL Provider
Complete MVP implementation of the Cast GHL Conversation Provider bridge:
- Go module setup with chi router and mongo-driver dependencies
- Config loading with env var validation and defaults
- MongoDB token store with upsert, get, update, delete operations
- Cast.ph SMS client with 429 retry logic and typed errors
- Phone number normalization (E.164 ↔ Philippine local format)
- GHL OAuth 2.0 install/callback/refresh flow
- GHL webhook handler with ECDSA signature verification (async dispatch)
- GHL API client for message status updates and inbound message stubs
- Multi-stage Dockerfile, docker-compose with MongoDB, Woodpecker CI pipeline
- Unit tests for phone normalization, Cast client, GHL webhook, and OAuth handlers

Co-Authored-By: SideKx <sidekx.ai@sds.dev>
2026-04-04 17:27:05 +02:00

179 lines
4.3 KiB
Markdown

# Task 09: Docker & Deployment
## Objective
Finalize Dockerfile, docker-compose, Woodpecker CI, and deployment docs.
## Part A: Dockerfile (finalize)
```dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /cast-ghl-provider ./cmd/server/
FROM alpine:3.19
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /cast-ghl-provider /cast-ghl-provider
EXPOSE 3002
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:3002/health || exit 1
CMD ["/cast-ghl-provider"]
```
Key points:
- Multi-stage build: ~15MB final image
- `CGO_ENABLED=0` for static binary
- `-ldflags="-s -w"` strips debug info
- `ca-certificates` for HTTPS calls to Cast and GHL APIs
- `tzdata` for timezone handling
- `HEALTHCHECK` built into the image
## Part B: docker-compose.yaml (finalize)
```yaml
services:
bridge:
build: .
ports:
- "${PORT:-3002}:${PORT:-3002}"
env_file: .env
depends_on:
mongo:
condition: service_started
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
mongo:
image: mongo:7
volumes:
- mongo-data:/data/db
restart: unless-stopped
# NOT exposed to host — only accessible from bridge service
volumes:
mongo-data:
```
## Part C: Woodpecker CI (`.woodpecker.yml`)
```yaml
steps:
- name: build
image: golang:1.22-alpine
commands:
- go build ./cmd/server/
- name: vet
image: golang:1.22-alpine
commands:
- go vet ./...
- name: test
image: golang:1.22-alpine
commands:
- go test ./...
- name: docker-build
image: plugins/docker
settings:
repo: registry.sds.dev/cast/cast-ghl-provider
registry: registry.sds.dev
tags:
- latest
- "${CI_COMMIT_TAG}"
username:
from_secret: docker_username
password:
from_secret: docker_password
when:
event: tag
ref: refs/tags/v*
- name: deploy
image: appleboy/drone-ssh
settings:
host:
from_secret: deploy_host
username:
from_secret: deploy_user
key:
from_secret: deploy_key
script:
- cd /opt/cast-ghl-provider
- docker compose pull
- docker compose up -d --remove-orphans
when:
event: tag
ref: refs/tags/v*
```
### Woodpecker secrets to configure:
- `docker_username` — container registry username
- `docker_password` — container registry password
- `deploy_host` — Vultr server IP
- `deploy_user` — SSH user
- `deploy_key` — SSH private key
### Release workflow:
```bash
git tag v0.1.0
git push origin v0.1.0
# Woodpecker: build → vet → test → docker build+push → SSH deploy
```
## Part D: Nginx reverse proxy config
```nginx
server {
listen 443 ssl;
server_name ghl.cast.ph;
ssl_certificate /etc/letsencrypt/live/ghl.cast.ph/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ghl.cast.ph/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3002;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name ghl.cast.ph;
return 301 https://$server_name$request_uri;
}
```
## Part E: Server setup checklist
1. Create directory: `mkdir -p /opt/cast-ghl-provider`
2. Copy `docker-compose.yaml` and `.env` to server
3. Configure `.env` with production values
4. Set up Nginx + Let's Encrypt
5. `docker compose up -d`
6. Verify: `curl https://ghl.cast.ph/health`
7. Update GHL Marketplace app:
- Delivery URL: `https://ghl.cast.ph/api/ghl/v1/webhook/messages`
- Redirect URI: `https://ghl.cast.ph/oauth-callback`
## Acceptance Criteria
- [ ] `docker compose build` succeeds
- [ ] `docker compose up` starts bridge + mongo
- [ ] Health check passes: `curl localhost:3002/health`
- [ ] Docker image is <20MB
- [ ] MongoDB not exposed to host network
- [ ] `.woodpecker.yml` has build, vet, test steps
- [ ] Docker build+push only on `v*` tags
- [ ] Deploy step uses SSH to pull and restart
- [ ] Nginx config handles HTTPS + proxy
- [ ] Log rotation configured (10MB, 3 files)