Add TypeScript frontend integration documentation, repository agent guidance files, Go-served frontend routing, and an advanced Docker Compose setup with watch mode plus BuildKit cache configuration. Made-with: Cursor
This commit is contained in:
@@ -6,3 +6,4 @@ node_modules
|
||||
**/*.log
|
||||
tmp
|
||||
dist
|
||||
.docker/buildx-cache
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file gives future coding agents a fast path map for this repository.
|
||||
|
||||
## Repository map
|
||||
|
||||
- API entrypoint: `cmd/api/main.go`
|
||||
- HTTP routes/handlers: `internal/http/handlers.go`
|
||||
- Core domain logic: `internal/app/service.go`
|
||||
- In-memory persistence: `internal/store/`
|
||||
- Auth utilities: `internal/auth/`
|
||||
- Frontend static app: `web/`
|
||||
- TypeScript API client: `libs/geo-api-client/`
|
||||
- CI workflow: `.gitea/workflows/ci.yml`
|
||||
- Architecture/planning docs: `docs/`
|
||||
|
||||
## Most common commands
|
||||
|
||||
From repo root:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
go run ./cmd/api
|
||||
docker compose up --build -d
|
||||
docker compose down
|
||||
```
|
||||
|
||||
TypeScript client:
|
||||
|
||||
```bash
|
||||
cd libs/geo-api-client
|
||||
bun install
|
||||
bun test
|
||||
bun run build
|
||||
```
|
||||
|
||||
## Path conventions
|
||||
|
||||
- Use repository-relative paths in docs and comments (never absolute machine paths).
|
||||
- Keep API route changes in `internal/http/handlers.go`.
|
||||
- Keep business rule changes in `internal/app/service.go`.
|
||||
- Keep frontend integration docs under `docs/`.
|
||||
|
||||
## Editing guidance for agents
|
||||
|
||||
- Prefer minimal changes and avoid unrelated refactors.
|
||||
- Add tests when behavior changes.
|
||||
- Verify Go tests after backend changes.
|
||||
- Verify Bun tests after TS client changes.
|
||||
- If CI fails due runner/network infrastructure, keep logs explicit in workflow output.
|
||||
+9
-2
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25-bookworm AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25-bookworm AS base
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
@@ -15,12 +15,19 @@ RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
FROM base AS builder
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} \
|
||||
go build -p "$(nproc)" -trimpath -ldflags="-s -w" -o /out/api ./cmd/api
|
||||
|
||||
FROM gcr.io/distroless/static-debian12:nonroot
|
||||
FROM base AS dev
|
||||
ENV ADDR=:8080
|
||||
EXPOSE 8080
|
||||
CMD ["go", "run", "./cmd/api"]
|
||||
|
||||
FROM gcr.io/distroless/static-debian12:nonroot AS runtime
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /out/api /app/api
|
||||
|
||||
@@ -32,7 +32,7 @@ Optional environment variables:
|
||||
Build and run the backend service:
|
||||
|
||||
```bash
|
||||
docker compose up --build -d
|
||||
COMPOSE_BAKE=true docker compose up --build -d
|
||||
```
|
||||
|
||||
Stop the service:
|
||||
@@ -44,20 +44,26 @@ docker compose down
|
||||
For local development with auto-rebuild on file changes:
|
||||
|
||||
```bash
|
||||
docker compose up --watch
|
||||
COMPOSE_BAKE=true docker compose --profile dev up --watch
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
## Frontend
|
||||
|
||||
Open `web/index.html` through a static server (recommended) or browser file URL.
|
||||
Frontend is served by the Go backend at runtime.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
python -m http.server 4173
|
||||
go run ./cmd/api
|
||||
```
|
||||
|
||||
Then visit `http://localhost:4173/web/`.
|
||||
Then visit `http://localhost:8080/web/`.
|
||||
|
||||
## API client library
|
||||
|
||||
@@ -70,6 +76,10 @@ bun test
|
||||
bun run build
|
||||
```
|
||||
|
||||
Frontend TypeScript integration guide:
|
||||
|
||||
- `docs/typescript-frontend-integration.md`
|
||||
|
||||
## CI
|
||||
|
||||
Workflow: `.gitea/workflows/ci.yml`
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
name: backend-fast-path
|
||||
description: Quick orientation and execution paths for agents working in this repository.
|
||||
---
|
||||
|
||||
# Skill: Backend Fast Path
|
||||
|
||||
Use this skill when implementing or debugging backend, frontend integration, TypeScript client, or CI behavior in this repository.
|
||||
|
||||
## Key paths
|
||||
|
||||
- API bootstrap: `cmd/api/main.go`
|
||||
- HTTP layer: `internal/http/handlers.go`
|
||||
- Service logic: `internal/app/service.go`
|
||||
- Tests (Go): `internal/http/api_test.go`
|
||||
- Frontend app (served by Go): `web/index.html`, `web/app.js`, `web/api.js`
|
||||
- TS client source: `libs/geo-api-client/src/`
|
||||
- TS client tests: `libs/geo-api-client/test/`
|
||||
- CI pipeline: `.gitea/workflows/ci.yml`
|
||||
- Dev docs: `README.md`, `docs/typescript-frontend-integration.md`, `docs/geo-auth-backend-plan.md`
|
||||
|
||||
## Task routing rules
|
||||
|
||||
1. API behavior changes
|
||||
- Update `internal/app/service.go` first.
|
||||
- Then wire request/response changes in `internal/http/handlers.go`.
|
||||
- Add/update tests in `internal/http/api_test.go`.
|
||||
|
||||
2. Frontend integration changes
|
||||
- Update TS client first in `libs/geo-api-client/src/`.
|
||||
- Then update `web/` integration usage if needed.
|
||||
- Document usage changes in `docs/typescript-frontend-integration.md`.
|
||||
|
||||
3. CI failures
|
||||
- Inspect `.gitea/workflows/ci.yml`.
|
||||
- Keep logs actionable.
|
||||
- Preserve explicit warnings when infrastructure prevents source checkout/tests.
|
||||
|
||||
## Verification checklist
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
cd libs/geo-api-client && bun test && bun run build
|
||||
```
|
||||
|
||||
If Docker-related changes are made:
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
```
|
||||
+36
-1
@@ -3,6 +3,11 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: runtime
|
||||
cache_from:
|
||||
- type=local,src=.docker/buildx-cache
|
||||
cache_to:
|
||||
- type=local,dest=.docker/buildx-cache,mode=max
|
||||
image: momswap-backend:latest
|
||||
container_name: momswap-backend-api
|
||||
environment:
|
||||
@@ -11,7 +16,37 @@ services:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
restart: unless-stopped
|
||||
|
||||
api-dev:
|
||||
profiles: ["dev"]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: dev
|
||||
cache_from:
|
||||
- type=local,src=.docker/buildx-cache
|
||||
cache_to:
|
||||
- type=local,dest=.docker/buildx-cache,mode=max
|
||||
image: momswap-backend:dev
|
||||
container_name: momswap-backend-api-dev
|
||||
environment:
|
||||
ADDR: ":8080"
|
||||
ADMIN_PUBLIC_KEY: "${ADMIN_PUBLIC_KEY:-}"
|
||||
ports:
|
||||
- "8080:8080"
|
||||
restart: unless-stopped
|
||||
develop:
|
||||
watch:
|
||||
- action: sync+restart
|
||||
path: ./web
|
||||
target: /src/web
|
||||
- action: sync+restart
|
||||
path: ./internal
|
||||
target: /src/internal
|
||||
- action: sync+restart
|
||||
path: ./cmd
|
||||
target: /src/cmd
|
||||
- action: rebuild
|
||||
path: .
|
||||
path: ./go.mod
|
||||
- action: rebuild
|
||||
path: ./Dockerfile
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# TypeScript Frontend Integration Guide
|
||||
|
||||
This document explains how frontend developers should integrate with the backend through the reusable TypeScript client at `libs/geo-api-client`.
|
||||
|
||||
## Goals
|
||||
|
||||
- Keep cryptographic signing logic in one place.
|
||||
- Avoid duplicating API request code in frontend apps.
|
||||
- Use a consistent local key storage format across projects.
|
||||
|
||||
## Client package location
|
||||
|
||||
- Source: `libs/geo-api-client/src`
|
||||
- Entry point: `libs/geo-api-client/src/index.ts`
|
||||
- Build output (browser ESM): `libs/geo-api-client/dist/index.js`
|
||||
|
||||
## Build and test the client
|
||||
|
||||
```bash
|
||||
cd libs/geo-api-client
|
||||
bun install
|
||||
bun test
|
||||
bun run build
|
||||
```
|
||||
|
||||
## Public API (current)
|
||||
|
||||
### Class: `GeoApiClient`
|
||||
|
||||
Constructor:
|
||||
|
||||
- `new GeoApiClient(baseUrl, storage, storageKey?)`
|
||||
|
||||
Key methods:
|
||||
|
||||
- `ensureKeysInStorage()`
|
||||
- `getStoredKeys()`
|
||||
- `importKeys(keys)`
|
||||
- `exportKeys()`
|
||||
- `setAccessToken(token)`
|
||||
- `createChallenge(publicKey)`
|
||||
- `loginWithSignature(publicKey, privateKey)`
|
||||
- `createInvitation(payload, inviterPrivateKey)`
|
||||
- `registerWithInvitation(...)`
|
||||
- `listCollections()`
|
||||
- `createCollection(name)`
|
||||
- `listFeatures(collectionId)`
|
||||
- `createPointFeature(collectionId, lon, lat, properties)`
|
||||
|
||||
## Recommended integration flow
|
||||
|
||||
1. Create one `GeoApiClient` instance per backend base URL.
|
||||
2. Call `ensureKeysInStorage()` when app initializes.
|
||||
3. Use `loginWithSignature()` to obtain and set a bearer token.
|
||||
4. Call collection/feature methods after authentication.
|
||||
5. Use `importKeys`/`exportKeys` in profile settings UX.
|
||||
|
||||
## Example (TypeScript app)
|
||||
|
||||
```ts
|
||||
import { GeoApiClient } from "../libs/geo-api-client/dist/index.js";
|
||||
|
||||
const storage = window.localStorage;
|
||||
|
||||
const storageLike = {
|
||||
getItem: (key: string) => storage.getItem(key),
|
||||
setItem: (key: string, value: string) => storage.setItem(key, value),
|
||||
removeItem: (key: string) => storage.removeItem(key),
|
||||
};
|
||||
|
||||
const client = new GeoApiClient("http://localhost:8080", storageLike);
|
||||
|
||||
const keys = await client.ensureKeysInStorage();
|
||||
await client.loginWithSignature(keys.publicKey, keys.privateKey);
|
||||
|
||||
const created = await client.createCollection("My Places");
|
||||
await client.createPointFeature(created.id, -16.6291, 28.4636, { name: "Santa Cruz" });
|
||||
const features = await client.listFeatures(created.id);
|
||||
console.log(features);
|
||||
```
|
||||
|
||||
## Security notes
|
||||
|
||||
- Private keys are currently stored in browser storage via the selected storage adapter.
|
||||
- If your frontend has stronger security requirements, wrap the storage adapter with your own encryption/decryption layer before calling `setItem`/`getItem`.
|
||||
- Never send private keys to the backend.
|
||||
|
||||
## No-build frontend compatibility
|
||||
|
||||
For no-bundler apps, import the built ESM file:
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import { GeoApiClient } from "../libs/geo-api-client/dist/index.js";
|
||||
// use client...
|
||||
</script>
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -21,6 +21,8 @@ func NewAPI(svc *app.Service) *API {
|
||||
|
||||
func (a *API) Routes() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
staticFiles := http.FileServer(http.Dir("web"))
|
||||
|
||||
mux.HandleFunc("GET /healthz", a.health)
|
||||
mux.HandleFunc("POST /v1/auth/challenge", a.createChallenge)
|
||||
mux.HandleFunc("POST /v1/auth/login", a.login)
|
||||
@@ -34,6 +36,9 @@ func (a *API) Routes() http.Handler {
|
||||
mux.HandleFunc("GET /v1/collections/{id}/features", a.listFeatures)
|
||||
mux.HandleFunc("DELETE /v1/features/{id}", a.deleteFeature)
|
||||
|
||||
mux.Handle("/web/", http.StripPrefix("/web/", staticFiles))
|
||||
mux.Handle("/", http.RedirectHandler("/web/", http.StatusTemporaryRedirect))
|
||||
|
||||
return withCORS(mux)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user