Add PostGIS compose service and update public integration docs.
CI / test (push) Successful in 5s

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
This commit is contained in:
2026-03-01 12:08:47 +00:00
parent f51126419c
commit a2aae391ad
4 changed files with 57 additions and 5 deletions
+13 -2
View File
@@ -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
+28
View File
@@ -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
+7 -1
View File
@@ -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`.
+9 -2
View File
@@ -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