feat: first commit

This commit is contained in:
sittichok Ouamsiri
2026-06-15 21:25:57 +07:00
commit 3395ab6dd3
88 changed files with 10034 additions and 0 deletions
+143
View File
@@ -0,0 +1,143 @@
# Example compose: one image (built from ./Dockerfile) run as three services,
# fronted by a gateway that enforces the path rules:
# /api/* -> api
# /, /login, /dashboard, … -> frontend (static SPA + assets)
# everything else -> redirect
#
# Bring it up: docker compose up --build
# Then visit: http://localhost:8080
#
# Anonymous flows work out of the box. For login, set GOOGLE_* / OIDC_* below
# (and add the callback URLs http://localhost:8080/api/v1/auth/<provider>/callback
# in your provider console).
x-backend-env: &backend-env
STORE: postgres
DATABASE_URL: postgres://snip:snip@postgres:5432/snip?sslmode=disable
REDIS_ADDR: redis:6379
SESSION_SECRET: ${SESSION_SECRET:-change-me-in-prod}
PUBLIC_URL: ${PUBLIC_URL:-http://localhost:8080}
SHORT_DOMAIN: ${SHORT_DOMAIN:-localhost:8080}
POST_LOGIN_REDIRECT: /dashboard
COOKIE_SECURE: "false"
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
# Generic OIDC points at the bundled mock provider (see mock-oidc service).
# The issuer host:port must be identical from the browser AND the backend, so
# we use the docker service name `mock-oidc:8090` for both. To log in from a
# real browser, add this line to your /etc/hosts: 127.0.0.1 mock-oidc
OIDC_ISSUER: ${OIDC_ISSUER:-http://mock-oidc:8090/default}
OIDC_CLIENT_ID: ${OIDC_CLIENT_ID:-snip-web}
OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET:-snip-secret}
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: snip
POSTGRES_PASSWORD: snip
POSTGRES_DB: snip
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snip"]
interval: 3s
timeout: 3s
retries: 10
redis:
image: redis:7-alpine
command: ["redis-server", "--save", "", "--appendonly", "no"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
timeout: 3s
retries: 10
migrate:
image: snip:latest # built by the `api` service above
command: ["migrate"] # runs schema DDL (idempotent) then exits 0
environment: *backend-env
depends_on:
postgres: { condition: service_healthy }
restart: "no"
api:
build: .
image: snip:latest
command: ["api"] # serves /api/v1/* on :8080
environment: *backend-env
depends_on:
migrate: { condition: service_completed_successfully }
redis: { condition: service_healthy }
mock-oidc: { condition: service_started }
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/api/v1/healthz"]
interval: 5s
timeout: 3s
retries: 10
frontend:
image: snip:latest # built by the `api` service above
command: ["frontend"] # serves the SPA on :8081
environment: *backend-env
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8081/healthz"]
interval: 5s
timeout: 3s
retries: 10
redirect:
image: snip:latest # built by the `api` service above
command: ["redirect"] # serves short-code redirects on :8082
environment: *backend-env
depends_on:
migrate: { condition: service_completed_successfully }
redis: { condition: service_healthy }
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8082/healthz"]
interval: 5s
timeout: 3s
retries: 10
# Mock OpenID Connect provider for local login. It accepts any client and
# auto-issues a token (no real account needed). The service name + port
# (mock-oidc:8090) is used as the issuer by BOTH the backend and the browser,
# which is why /etc/hosts needs `127.0.0.1 mock-oidc` for browser logins.
mock-oidc:
image: ghcr.io/navikt/mock-oauth2-server:2.1.10
ports:
- "8090:8090"
environment:
SERVER_PORT: "8090"
# Non-interactive: issue a token immediately for a demo identity.
JSON_CONFIG: >-
{
"interactiveLogin": false,
"tokenCallbacks": [
{
"issuerId": "default",
"requestMappings": [
{
"requestParam": "grant_type",
"match": "authorization_code",
"claims": { "sub": "demo-user", "email": "[email protected]", "name": "Demo User" }
}
]
}
]
}
gateway:
image: caddy:2-alpine
ports:
- "8080:80"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
depends_on:
api: { condition: service_healthy }
frontend: { condition: service_healthy }
redirect: { condition: service_healthy }
volumes:
pgdata: