From a2aae391adcac9c1aa754b7363e8fba3b745b6bf Mon Sep 17 00:00:00 2001 From: Andriy Oblivantsev Date: Sun, 1 Mar 2026 12:08:47 +0000 Subject: [PATCH] Add PostGIS compose service and update public integration docs. Wire API services to PostGIS in docker-compose, broaden CORS handling for cross-origin clients, and align developer docs with the production momswap URL. Made-with: Cursor --- README.md | 15 +++++++++++-- docker-compose.yml | 28 +++++++++++++++++++++++++ docs/typescript-frontend-integration.md | 8 ++++++- internal/http/handlers.go | 11 ++++++++-- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index daab701..fe0fb3d 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,9 @@ go test ./... go run ./cmd/api ``` -Server default: `http://localhost:8080`. +Primary deployed base URL: `https://momswap.produktor.duckdns.org/`. + +Local default (for development): `http://localhost:8080`. Optional environment variables: @@ -35,6 +37,11 @@ Build and run the backend service: COMPOSE_BAKE=true docker compose up --build -d ``` +This starts: + +- `db` (`postgis/postgis`) on `5432` +- `api` on `8080`, wired with `DATABASE_URL` to the `db` service + Stop the service: ```bash @@ -52,6 +59,7 @@ Notes: - `api` service uses the production `runtime` image target. - `api-dev` profile uses the `dev` image target and Docker Compose watch. - Build cache is persisted at `.docker/buildx-cache` via `cache_from`/`cache_to`. +- DB defaults can be overridden via `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`. ## Frontend @@ -63,7 +71,10 @@ Example: go run ./cmd/api ``` -Then visit `http://localhost:8080/web/`. +Then visit: + +- Production: `https://momswap.produktor.duckdns.org/web/` +- Local: `http://localhost:8080/web/` ## API client library diff --git a/docker-compose.yml b/docker-compose.yml index f4e584d..cd98195 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,23 @@ services: + db: + image: postgis/postgis:17-3.5 + container_name: momswap-backend-db + environment: + POSTGRES_DB: "${POSTGRES_DB:-momswap}" + POSTGRES_USER: "${POSTGRES_USER:-momswap}" + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-momswap}" + ports: + - "5432:5432" + volumes: + - ./var/posrgres:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-momswap} -d ${POSTGRES_DB:-momswap}"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 10s + restart: unless-stopped + api: build: context: . @@ -13,6 +32,10 @@ services: environment: ADDR: ":8080" ADMIN_PUBLIC_KEY: "${ADMIN_PUBLIC_KEY:-}" + DATABASE_URL: "postgres://${POSTGRES_USER:-momswap}:${POSTGRES_PASSWORD:-momswap}@db:5432/${POSTGRES_DB:-momswap}?sslmode=disable" + depends_on: + db: + condition: service_healthy ports: - "8080:8080" restart: unless-stopped @@ -32,6 +55,10 @@ services: environment: ADDR: ":8080" ADMIN_PUBLIC_KEY: "${ADMIN_PUBLIC_KEY:-}" + DATABASE_URL: "postgres://${POSTGRES_USER:-momswap}:${POSTGRES_PASSWORD:-momswap}@db:5432/${POSTGRES_DB:-momswap}?sslmode=disable" + depends_on: + db: + condition: service_healthy ports: - "8080:8080" restart: unless-stopped @@ -50,3 +77,4 @@ services: path: ./go.mod - action: rebuild path: ./Dockerfile + diff --git a/docs/typescript-frontend-integration.md b/docs/typescript-frontend-integration.md index 79458d2..8fe704a 100644 --- a/docs/typescript-frontend-integration.md +++ b/docs/typescript-frontend-integration.md @@ -2,6 +2,10 @@ This document explains how frontend developers should integrate with the backend through the reusable TypeScript client at `libs/geo-api-client`. +Primary backend URL for integration: + +- `https://momswap.produktor.duckdns.org/` + ## Goals - Keep cryptographic signing logic in one place. @@ -68,7 +72,7 @@ const storageLike = { removeItem: (key: string) => storage.removeItem(key), }; -const client = new GeoApiClient("http://localhost:8080", storageLike); +const client = new GeoApiClient("https://momswap.produktor.duckdns.org", storageLike); const keys = await client.ensureKeysInStorage(); await client.loginWithSignature(keys.publicKey, keys.privateKey); @@ -97,3 +101,5 @@ For no-bundler apps, import the built ESM file: ``` The backend itself serves static UI at `/web/`, but this library can be consumed by any frontend runtime that supports `fetch`, `TextEncoder`, and ES modules. + +For local development you can switch the client base URL to `http://localhost:8080`. diff --git a/internal/http/handlers.go b/internal/http/handlers.go index 0348330..62c6a3f 100644 --- a/internal/http/handlers.go +++ b/internal/http/handlers.go @@ -45,8 +45,15 @@ func (a *API) Routes() http.Handler { func withCORS(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type") - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS") + w.Header().Set("Vary", "Origin, Access-Control-Request-Method, Access-Control-Request-Headers") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD") + w.Header().Set("Access-Control-Max-Age", "86400") + requestHeaders := r.Header.Get("Access-Control-Request-Headers") + if requestHeaders != "" { + w.Header().Set("Access-Control-Allow-Headers", requestHeaders) + } else { + w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Origin, X-Requested-With") + } if r.Method == http.MethodOptions { w.WriteHeader(http.StatusNoContent) return