feat(demo): bring up coordinator + userservice, seed root admin
make demo-ui now starts the userservice (own Postgres + migrations) beside the coordinator on a shared JWT secret, and seeds a root admin. The coordinator runs in UI session mode, so /ui opens a login page instead of a basic-auth prompt. - docker-compose.users.yml overlay: userservice stack + coordinator JWT wiring - demo-ui.sh: waits for the userservice, logs in as the seeded admin to poll the now session-gated dashboard, and prints the admin credentials - validated with docker compose config (6 services, merged env)
This commit is contained in:
@@ -35,7 +35,7 @@ help:
|
|||||||
' make demo-down Stop the demo services and workers.' \
|
' make demo-down Stop the demo services and workers.' \
|
||||||
' make test / make vet Run Go verification.' \
|
' make test / make vet Run Go verification.' \
|
||||||
'' \
|
'' \
|
||||||
'Demo UI: http://localhost:18080/ui (operator / demo-ui-secret).'
|
'Demo UI: http://localhost:18080/ui (login page; admin root@scimesh.local / rootpassword).'
|
||||||
|
|
||||||
demo-ui:
|
demo-ui:
|
||||||
@DEMO_PROJECT="$(DEMO_PROJECT)" \
|
@DEMO_PROJECT="$(DEMO_PROJECT)" \
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# Demo overlay: adds the userservice (its own Postgres + migrations) alongside
|
||||||
|
# the coordinator and wires the two together with a shared JWT secret, so the
|
||||||
|
# operator UI authenticates through userservice login/registration.
|
||||||
|
#
|
||||||
|
# Used only by scripts/demo-ui.sh, merged onto docker-compose.yml with a second
|
||||||
|
# -f. Not part of the plain `make up` stack.
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres-users:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER:-scimesh}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-scimesh}
|
||||||
|
POSTGRES_DB: scimesh_users
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-scimesh} -d scimesh_users"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
start_period: 5s
|
||||||
|
|
||||||
|
migrate-users:
|
||||||
|
image: migrate/migrate:v4.17.1
|
||||||
|
depends_on:
|
||||||
|
postgres-users:
|
||||||
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
- ../users/migrations:/migrations:ro
|
||||||
|
command:
|
||||||
|
- -path=/migrations
|
||||||
|
- -database=postgres://${POSTGRES_USER:-scimesh}:${POSTGRES_PASSWORD:-scimesh}@postgres-users:5432/scimesh_users?sslmode=disable
|
||||||
|
- up
|
||||||
|
restart: on-failure
|
||||||
|
|
||||||
|
userservice:
|
||||||
|
build:
|
||||||
|
context: ../users
|
||||||
|
depends_on:
|
||||||
|
postgres-users:
|
||||||
|
condition: service_healthy
|
||||||
|
migrate-users:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
environment:
|
||||||
|
USERSERVICE_ADDR: ":8081"
|
||||||
|
DATABASE_URL: postgres://${POSTGRES_USER:-scimesh}:${POSTGRES_PASSWORD:-scimesh}@postgres-users:5432/scimesh_users?sslmode=disable
|
||||||
|
JWT_SECRET: ${JWT_SECRET}
|
||||||
|
# Seeds the first admin the very first time it boots (idempotent after).
|
||||||
|
BOOTSTRAP_ADMIN_EMAIL: ${BOOTSTRAP_ADMIN_EMAIL:-root@scimesh.local}
|
||||||
|
BOOTSTRAP_ADMIN_PASSWORD: ${BOOTSTRAP_ADMIN_PASSWORD}
|
||||||
|
LOG_LEVEL: ${LOG_LEVEL:-info}
|
||||||
|
ports:
|
||||||
|
- "${USERSERVICE_PORT:-18081}:8081"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8081/health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Turn the coordinator UI into session mode: the same shared secret verifies
|
||||||
|
# userservice tokens locally, and USERSERVICE_URL is where login/register proxy.
|
||||||
|
coordinator:
|
||||||
|
environment:
|
||||||
|
JWT_SECRET: ${JWT_SECRET}
|
||||||
|
USERSERVICE_URL: http://userservice:8081
|
||||||
@@ -11,8 +11,15 @@ repo_dir=$(CDPATH= cd -- "$coordinator_dir/.." && pwd)
|
|||||||
project=${DEMO_PROJECT:-scimesh-demo}
|
project=${DEMO_PROJECT:-scimesh-demo}
|
||||||
postgres_port=${DEMO_POSTGRES_PORT:-55432}
|
postgres_port=${DEMO_POSTGRES_PORT:-55432}
|
||||||
coordinator_port=${DEMO_COORDINATOR_PORT:-18080}
|
coordinator_port=${DEMO_COORDINATOR_PORT:-18080}
|
||||||
|
userservice_port=${DEMO_USERSERVICE_PORT:-18081}
|
||||||
ui_token=${DEMO_UI_TOKEN:-demo-ui-secret}
|
ui_token=${DEMO_UI_TOKEN:-demo-ui-secret}
|
||||||
worker_token=${DEMO_WORKER_TOKEN:-demo-worker-token}
|
worker_token=${DEMO_WORKER_TOKEN:-demo-worker-token}
|
||||||
|
# Shared HS256 secret; the coordinator verifies userservice tokens with it. Must
|
||||||
|
# be at least 32 bytes (both services refuse a shorter one).
|
||||||
|
jwt_secret=${DEMO_JWT_SECRET:-demo-jwt-secret-please-change-me-0123456789}
|
||||||
|
# The first admin, seeded into the userservice on first boot.
|
||||||
|
admin_email=${DEMO_ADMIN_EMAIL:-root@scimesh.local}
|
||||||
|
admin_password=${DEMO_ADMIN_PASSWORD:-rootpassword}
|
||||||
workers=${DEMO_WORKERS:-2}
|
workers=${DEMO_WORKERS:-2}
|
||||||
demo_dir=${DEMO_DIR:-.demo}
|
demo_dir=${DEMO_DIR:-.demo}
|
||||||
case "$demo_dir" in
|
case "$demo_dir" in
|
||||||
@@ -26,9 +33,15 @@ logs_dir="$demo_dir/logs"
|
|||||||
compose() {
|
compose() {
|
||||||
POSTGRES_PORT="$postgres_port" \
|
POSTGRES_PORT="$postgres_port" \
|
||||||
COORDINATOR_PORT="$coordinator_port" \
|
COORDINATOR_PORT="$coordinator_port" \
|
||||||
|
USERSERVICE_PORT="$userservice_port" \
|
||||||
UI_AUTH_TOKEN="$ui_token" \
|
UI_AUTH_TOKEN="$ui_token" \
|
||||||
WORKER_AUTH_TOKEN="$worker_token" \
|
WORKER_AUTH_TOKEN="$worker_token" \
|
||||||
docker compose -p "$project" -f "$coordinator_dir/docker-compose.yml" "$@"
|
JWT_SECRET="$jwt_secret" \
|
||||||
|
BOOTSTRAP_ADMIN_EMAIL="$admin_email" \
|
||||||
|
BOOTSTRAP_ADMIN_PASSWORD="$admin_password" \
|
||||||
|
docker compose -p "$project" \
|
||||||
|
-f "$coordinator_dir/docker-compose.yml" \
|
||||||
|
-f "$coordinator_dir/docker-compose.users.yml" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_workers() {
|
stop_workers() {
|
||||||
@@ -57,10 +70,29 @@ wait_for_coordinator() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_for_userservice() {
|
||||||
|
local attempt=0
|
||||||
|
until curl --fail --silent --show-error "http://localhost:$userservice_port/health" >/dev/null; do
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
if (( attempt >= 45 )); then
|
||||||
|
echo "Userservice did not become ready. Recent logs:" >&2
|
||||||
|
compose logs --tail=80 userservice >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
wait_for_workers() {
|
wait_for_workers() {
|
||||||
local attempt=0 registered overview
|
local attempt=0 registered overview cookie="$demo_dir/session.cookies"
|
||||||
|
# The dashboard API is behind a userservice session now, not basic auth. Log in
|
||||||
|
# as the seeded admin (who sees every worker) to obtain a session cookie.
|
||||||
|
curl --fail --silent -c "$cookie" \
|
||||||
|
--data-urlencode "email=$admin_email" \
|
||||||
|
--data-urlencode "password=$admin_password" \
|
||||||
|
"http://localhost:$coordinator_port/ui/login" >/dev/null 2>&1 || true
|
||||||
until false; do
|
until false; do
|
||||||
overview=$(curl --fail --silent --show-error --user "operator:$ui_token" \
|
overview=$(curl --fail --silent --show-error -b "$cookie" \
|
||||||
"http://localhost:$coordinator_port/ui/api/overview" 2>/dev/null || true)
|
"http://localhost:$coordinator_port/ui/api/overview" 2>/dev/null || true)
|
||||||
# The overview contains no jobs at demo startup, so every `id` belongs to
|
# The overview contains no jobs at demo startup, so every `id` belongs to
|
||||||
# a registered worker. Avoid adding jq just for this local helper.
|
# a registered worker. Avoid adding jq just for this local helper.
|
||||||
@@ -96,6 +128,8 @@ start() {
|
|||||||
compose up -d --build
|
compose up -d --build
|
||||||
echo "Waiting for the coordinator on http://localhost:$coordinator_port ..."
|
echo "Waiting for the coordinator on http://localhost:$coordinator_port ..."
|
||||||
wait_for_coordinator
|
wait_for_coordinator
|
||||||
|
echo "Waiting for the userservice on http://localhost:$userservice_port ..."
|
||||||
|
wait_for_userservice
|
||||||
|
|
||||||
: > "$pid_file"
|
: > "$pid_file"
|
||||||
for index in $(seq 1 "$workers"); do
|
for index in $(seq 1 "$workers"); do
|
||||||
@@ -115,13 +149,15 @@ start() {
|
|||||||
|
|
||||||
SciMesh manual demo is ready.
|
SciMesh manual demo is ready.
|
||||||
|
|
||||||
UI: http://localhost:$coordinator_port/ui
|
UI: http://localhost:$coordinator_port/ui (shows a login page)
|
||||||
Username: operator
|
Admin login: $admin_email / $admin_password
|
||||||
Password: $ui_token
|
Userservice: http://localhost:$userservice_port
|
||||||
Workers: $workers local reference workers
|
Workers: $workers local reference workers
|
||||||
|
|
||||||
Upload a small ChEMBL TSV through “New similarity search”, then watch the job
|
Sign in with the admin above, or register a new account from the login page.
|
||||||
page update. Worker logs are in $logs_dir. Stop everything with:
|
The admin sees every job; a plain user sees only their own. Upload a small
|
||||||
|
ChEMBL TSV through “New similarity search”, then watch the job page update.
|
||||||
|
Worker logs are in $logs_dir. Stop everything with:
|
||||||
|
|
||||||
make demo-down
|
make demo-down
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
Reference in New Issue
Block a user