infra(infrastructure): 🧱 Configure local TLS certs, Docker Compose for platform services (DB, MinIO, mail, Caddy), DB schema initialization, and CI type-checking workflow setup
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
bf2df14f07
commit
bf1e394ec0
7 changed files with 327 additions and 0 deletions
33
.forgejo/workflows/typecheck.yml
Normal file
33
.forgejo/workflows/typecheck.yml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
name: typecheck
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- '@platform/**/*.ts'
|
||||
- '@platform/**/*.tsx'
|
||||
- '@platform/**/tsconfig*.json'
|
||||
- '@platform/package.json'
|
||||
- '@platform/bun.lock'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: false # never fetch the .archive/*.tar.zst blobs in CI
|
||||
|
||||
- name: setup bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: install
|
||||
working-directory: '@platform'
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: typecheck
|
||||
working-directory: '@platform'
|
||||
run: bun run typecheck
|
||||
132
@platform/infrastructure/Caddyfile.local
Normal file
132
@platform/infrastructure/Caddyfile.local
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# Local development reverse proxy — atlilith V3
|
||||
#
|
||||
# Resolves *.atlilith.apricot.lan domains to their Vite dev servers.
|
||||
# Run: caddy run --config @platform/infrastructure/Caddyfile.local
|
||||
#
|
||||
# Uses internal TLS (mkcert) for .lan domains.
|
||||
#
|
||||
# Adding a new dev subdomain → add it to gen-local-certs.sh DOMAINS array
|
||||
# (or include in the wildcard), add a server block below with `import local_tls`.
|
||||
|
||||
{
|
||||
# auto_https off — TLS is explicit per-site via mkcert certs.
|
||||
http_port 80
|
||||
https_port 443
|
||||
default_bind 0.0.0.0 ::
|
||||
auto_https off
|
||||
}
|
||||
|
||||
# Unified mkcert wildcard for all *.atlilith.apricot.lan dev hosts.
|
||||
# Regenerate the cert: @platform/scripts/gen-local-certs.sh
|
||||
(local_tls) {
|
||||
tls /var/home/lilith/Code/@projects/@atlilith/@platform/infrastructure/certs/_wildcard.atlilith.apricot.lan.crt /var/home/lilith/Code/@projects/@atlilith/@platform/infrastructure/certs/_wildcard.atlilith.apricot.lan.key
|
||||
}
|
||||
|
||||
# HTTP → HTTPS redirect for all .atlilith.apricot.lan domains
|
||||
:80 {
|
||||
redir https://{host}{uri} 301
|
||||
}
|
||||
|
||||
# ─── Provider sites (per-instance) ─────────────────────────────────────────
|
||||
# Quinn's instance keeps the quinn.apricot.lan hostnames during cutover;
|
||||
# new providers get {provider}.atlilith.apricot.lan.
|
||||
|
||||
https://atlilith.apricot.lan {
|
||||
import local_tls
|
||||
# Marketing landing (Vite on :5220)
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:5220 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Provider portal (generic) ─────────────────────────────────────────────
|
||||
https://portal.atlilith.apricot.lan {
|
||||
import local_tls
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:5274 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─── AI assistant ──────────────────────────────────────────────────────────
|
||||
https://ai.atlilith.apricot.lan {
|
||||
import local_tls
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:5276 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Messenger ─────────────────────────────────────────────────────────────
|
||||
https://m.atlilith.apricot.lan {
|
||||
import local_tls
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:5275 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Admin (platform-wide) ─────────────────────────────────────────────────
|
||||
https://admin.atlilith.apricot.lan {
|
||||
import local_tls
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:5221 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─── SSO (auth) ────────────────────────────────────────────────────────────
|
||||
https://sso.atlilith.apricot.lan {
|
||||
import local_tls
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:3045 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─── API gateway (Hono) ────────────────────────────────────────────────────
|
||||
https://api.atlilith.apricot.lan {
|
||||
import local_tls
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:3050 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Analytics (org-analytics) ─────────────────────────────────────────────
|
||||
https://data.atlilith.apricot.lan {
|
||||
import local_tls
|
||||
|
||||
# SSO auth gate (DEV_MODE: SSO always returns 200, transparent passthrough).
|
||||
@protected not path /analytics/track/*
|
||||
forward_auth @protected localhost:3045 {
|
||||
uri /auth/validate
|
||||
|
||||
@unauthed status 401
|
||||
handle_response @unauthed {
|
||||
redir https://sso.atlilith.apricot.lan/login?redirect=https://{host}{uri} 302
|
||||
}
|
||||
}
|
||||
|
||||
# Public ingest path (write-key authenticated by collector).
|
||||
handle /analytics/track/* {
|
||||
reverse_proxy 127.0.0.1:4201 {
|
||||
header_up X-Write-Key "dev-write-key"
|
||||
}
|
||||
}
|
||||
|
||||
# Dashboard SPA
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:5211 {
|
||||
header_up Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
@platform/infrastructure/compose.platform-db.yml
Normal file
22
@platform/infrastructure/compose.platform-db.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
services:
|
||||
platform-db:
|
||||
image: postgres:16-alpine
|
||||
container_name: atlilith-platform-db
|
||||
environment:
|
||||
POSTGRES_DB: platform
|
||||
POSTGRES_USER: platform
|
||||
POSTGRES_PASSWORD: devpassword
|
||||
ports:
|
||||
- "25440:5432"
|
||||
volumes:
|
||||
- atlilith-platform-db-data:/var/lib/postgresql/data
|
||||
- ./platform-db-init.sql:/docker-entrypoint-initdb.d/000_init.sql:ro
|
||||
- ./sql/migrations:/docker-entrypoint-initdb.d/migrations:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U platform -d platform"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
atlilith-platform-db-data:
|
||||
20
@platform/infrastructure/compose.platform-mail.yml
Normal file
20
@platform/infrastructure/compose.platform-mail.yml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
services:
|
||||
platform-mail:
|
||||
image: axllent/mailpit:latest
|
||||
container_name: atlilith-platform-mail
|
||||
ports:
|
||||
- "1026:1025"
|
||||
- "8026:8025"
|
||||
environment:
|
||||
MP_MAX_MESSAGES: 5000
|
||||
MP_DATABASE: /data/mailpit.db
|
||||
volumes:
|
||||
- atlilith-platform-mail-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8025/api/v1/info > /dev/null || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
atlilith-platform-mail-data:
|
||||
21
@platform/infrastructure/compose.platform-minio.yml
Normal file
21
@platform/infrastructure/compose.platform-minio.yml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
services:
|
||||
platform-minio:
|
||||
image: minio/minio:latest
|
||||
container_name: atlilith-platform-minio
|
||||
command: server /data --console-address ":9101"
|
||||
environment:
|
||||
MINIO_ROOT_USER: platform-dev
|
||||
MINIO_ROOT_PASSWORD: devpassword
|
||||
ports:
|
||||
- "9100:9000"
|
||||
- "9101:9101"
|
||||
volumes:
|
||||
- atlilith-platform-minio-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
atlilith-platform-minio-data:
|
||||
45
@platform/infrastructure/gen-local-certs.sh
Executable file
45
@platform/infrastructure/gen-local-certs.sh
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env bash
|
||||
# Generate mkcert certificates for all *.atlilith.apricot.lan dev subdomains.
|
||||
# Run once on a new machine or when adding a new subdomain.
|
||||
# Requires: mkcert (brew install mkcert / apt install mkcert)
|
||||
#
|
||||
# Usage:
|
||||
# bash @platform/scripts/gen-local-certs.sh # generate missing
|
||||
# bash @platform/scripts/gen-local-certs.sh --force # regenerate all
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CERTS_DIR="$REPO_ROOT/@platform/infrastructure/certs"
|
||||
FORCE=false
|
||||
|
||||
for arg in "$@"; do [[ "$arg" == "--force" ]] && FORCE=true; done
|
||||
|
||||
if ! command -v mkcert &>/dev/null; then
|
||||
echo "ERROR: mkcert not found. Install it first:" >&2
|
||||
echo " Linux: brew install mkcert OR sudo apt install mkcert" >&2
|
||||
echo " macOS: brew install mkcert" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$CERTS_DIR"
|
||||
|
||||
# Single wildcard cert covers all subdomains.
|
||||
WILDCARD_CRT="$CERTS_DIR/_wildcard.atlilith.apricot.lan.crt"
|
||||
WILDCARD_KEY="$CERTS_DIR/_wildcard.atlilith.apricot.lan.key"
|
||||
|
||||
if [[ "$FORCE" == "false" && -f "$WILDCARD_CRT" && -f "$WILDCARD_KEY" ]]; then
|
||||
echo " [skip] wildcard cert already exists at $WILDCARD_CRT"
|
||||
else
|
||||
echo " [gen] wildcard cert for *.atlilith.apricot.lan + atlilith.apricot.lan"
|
||||
mkcert \
|
||||
-cert-file "$WILDCARD_CRT" \
|
||||
-key-file "$WILDCARD_KEY" \
|
||||
"atlilith.apricot.lan" "*.atlilith.apricot.lan"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Certs at: $CERTS_DIR"
|
||||
echo ""
|
||||
echo "Next: (re)start Caddy to pick up changes:"
|
||||
echo " manage-apps restart atlilith.proxy apricot"
|
||||
54
@platform/infrastructure/platform-db-init.sql
Normal file
54
@platform/infrastructure/platform-db-init.sql
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
-- atlilith V3 — base schema bootstrap.
|
||||
-- Runs once when the docker postgres volume is fresh.
|
||||
-- Migrations in sql/migrations/ run AFTER this (alphabetical by docker-entrypoint).
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS platform;
|
||||
CREATE SCHEMA IF NOT EXISTS analytics;
|
||||
CREATE SCHEMA IF NOT EXISTS messenger;
|
||||
CREATE SCHEMA IF NOT EXISTS booking;
|
||||
CREATE SCHEMA IF NOT EXISTS content;
|
||||
CREATE SCHEMA IF NOT EXISTS ops;
|
||||
|
||||
-- Common extensions
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- gen_random_uuid()
|
||||
CREATE EXTENSION IF NOT EXISTS "citext"; -- case-insensitive text (emails)
|
||||
|
||||
-- Tenancy core: users table (Person tenant).
|
||||
-- Migration 001_add_orgs.sql adds the Org overlay.
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
slug TEXT UNIQUE NOT NULL CHECK (slug ~ '^[a-z][a-z0-9-]{1,62}[a-z0-9]$'),
|
||||
email CITEXT UNIQUE NOT NULL,
|
||||
display_name TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
|
||||
-- Updated_at maintenance (reused by org tables in migration 001)
|
||||
CREATE OR REPLACE FUNCTION touch_updated_at() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_users_updated_at ON users;
|
||||
CREATE TRIGGER trg_users_updated_at
|
||||
BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION touch_updated_at();
|
||||
|
||||
-- Seed: transquinnftw (inaugural Person tenant; Cocotte org seeded in 002).
|
||||
INSERT INTO users (slug, email, display_name)
|
||||
VALUES ('transquinnftw', 'booking@transquinnftw.com', 'Quinn')
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- Placeholder analytics_events table referenced by migration 001
|
||||
-- (real definition comes from the org-analytics feature later).
|
||||
CREATE TABLE IF NOT EXISTS analytics_events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
event_name TEXT NOT NULL,
|
||||
user_id UUID NULL REFERENCES users(id),
|
||||
occurred_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
Loading…
Add table
Reference in a new issue