New demo mode updates

This commit is contained in:
seniorswe
2026-01-19 02:07:55 -05:00
parent 811e6c896a
commit c595d95e31
9 changed files with 160 additions and 48 deletions

79
Dockerfile.demo Normal file
View File

@@ -0,0 +1,79 @@
# Demo image: Python backend (Doorman) + Next.js web client
# All demo environment is baked into the image (no .env required).
FROM python:3.11-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
# Install Node.js + npm and useful tools
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
nodejs npm curl ca-certificates git \
&& npm i -g npm@^10 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Backend dependencies first for better layer caching
COPY backend-services/requirements.txt /app/backend-services/requirements.txt
RUN python -m pip install --upgrade pip \
&& pip install -r /app/backend-services/requirements.txt
# Prepare web client dependencies separately for better caching
WORKDIR /app/web-client
COPY web-client/package*.json ./
RUN npm ci --include=dev
# Copy backend source only (avoid copying entire repo)
WORKDIR /app
COPY backend-services /app/backend-services
# Copy web client sources (excluding node_modules via .dockerignore)
WORKDIR /app/web-client
COPY web-client/ .
# Build-time args for frontend env (baked into Next.js bundle)
# For demo, leave gateway URL empty so client uses same-origin and Next.js rewrites proxy to the API.
ARG NEXT_PUBLIC_PROTECTED_USERS=demo@doorman.dev
ARG NEXT_PUBLIC_GATEWAY_URL=
# Build Next.js (production) with baked public env
RUN echo "export NEXT_PUBLIC_PROTECTED_USERS=${NEXT_PUBLIC_PROTECTED_USERS}" > /tmp/build-env.sh && \
echo "export NEXT_PUBLIC_GATEWAY_URL=${NEXT_PUBLIC_GATEWAY_URL}" >> /tmp/build-env.sh && \
echo "export NODE_ENV=production" >> /tmp/build-env.sh && \
echo "export NEXT_TELEMETRY_DISABLED=1" >> /tmp/build-env.sh && \
echo "export DEMO_MODE=true" >> /tmp/build-env.sh && \
. /tmp/build-env.sh && \
npm run build && \
npm prune --omit=dev
# Runtime configuration baked for demo
WORKDIR /app
# Demo defaults (no external .env needed)
ENV ENV=development \
MEM_OR_EXTERNAL=MEM \
THREADS=1 \
HTTPS_ONLY=false \
DOORMAN_ADMIN_EMAIL=demo@doorman.dev \
DOORMAN_ADMIN_PASSWORD=DemoPassword123! \
JWT_SECRET_KEY=demo-secret-change-me-please-32-bytes-min \
DEMO_SEED=false \
DEMO_MODE=true \
ENABLE_STARLETTE_CORS=true \
ALLOWED_ORIGINS=http://localhost:3000 \
ALLOW_METHODS="GET,POST,PUT,DELETE,OPTIONS,PATCH,HEAD" \
ALLOW_HEADERS="*" \
ALLOW_CREDENTIALS=true \
PORT=3001 \
WEB_PORT=3000
# Add entrypoint
COPY docker/entrypoint.sh /app/docker/entrypoint.sh
RUN chmod +x /app/docker/entrypoint.sh
EXPOSE 3001 3000
CMD ["/app/docker/entrypoint.sh"]

View File

@@ -42,22 +42,31 @@ When ready:
- Web UI: `http://localhost:3000`
- Gateway API: `http://localhost:3001`
### OneCommand Demo (inmemory + autoseed)
### OneCommand Demo (inmemory)
Spin up a preconfigured demo (autocleans on exit) without editing `.env`:
```bash
# First time (build the demo image to include frontend proxy config)
docker compose -f docker-compose.yml -f docker-compose.demo.yml up --build
# Next runs (no rebuild needed)
docker compose -f docker-compose.yml -f docker-compose.demo.yml up
```
Defaults:
Defaults (demoonly):
- Admin: `demo@doorman.dev` / `DemoPassword123!`
- Web UI: `http://localhost:3000`
- API: `http://localhost:3001`
- Mode: inmemory (no Redis/Mongo), demo data autoseeded inprocess on start
- Mode: inmemory (no Redis/Mongo); no seed data created
- Isolation: uses separate image tag (`doorman-demo:latest`) and project name to avoid overwriting any existing `doorman`
- Cleanup: containers, volumes, and networks are removed automatically when you stop (Ctrl+C)
Notes:
- Demo serves the API through the frontend (sameorigin proxy) so browser cookies work without crossport CORS issues. The first run with `--build` bakes this Next.js proxy configuration.
- Demo runs fully incontainer: it does not mount the host `generated/` directory and disables memory autosave to avoid crosscontamination with a nondemo instance.
- If you ran an older demo before this change and see demo data in a nondemo run, stop the container and delete host memory dumps: `rm -rf generated/memory_dump*`.
## Frontend Gateway Configuration
The web client needs to know the backend gateway URL. Set `NEXT_PUBLIC_GATEWAY_URL` in the root `.env` file:

View File

@@ -154,11 +154,17 @@ class Database:
# for tests that adjust the env and call initialize_collections again.
try:
env_pwd = os.getenv('DOORMAN_ADMIN_PASSWORD')
env_email = os.getenv('DOORMAN_ADMIN_EMAIL')
if env_pwd:
users.update_one(
{'username': 'admin'},
{'$set': {'password': password_util.hash_password(env_pwd)}},
)
if env_email:
users.update_one(
{'username': 'admin'},
{'$set': {'email': env_email}},
)
except Exception:
pass

View File

@@ -189,6 +189,12 @@ class AsyncDatabase:
raise RuntimeError(
'Admin user missing password and DOORMAN_ADMIN_PASSWORD not set'
)
# Ensure admin email matches env if provided
env_email = os.getenv('DOORMAN_ADMIN_EMAIL')
if adm and env_email and adm.get('email') != env_email:
await self.db.users.update_one(
{'username': 'admin'}, {'$set': {'email': env_email}}
)
except Exception:
pass

View File

@@ -1,33 +1,25 @@
name: doorman-demo
version: '3.8'
services:
doorman:
# Reuse the same build but inject demo-friendly frontend URL
# Build from demo Dockerfile with baked env
build:
context: .
dockerfile: Dockerfile
args:
NEXT_PUBLIC_GATEWAY_URL: http://localhost:3001
NEXT_PUBLIC_PROTECTED_USERS: demo@doorman.dev
dockerfile: Dockerfile.demo
image: doorman-demo:latest
container_name: null
# Provide critical runtime env so it works even without a fresh build
environment:
# App mode
ENV: development
MEM_OR_EXTERNAL: MEM
THREADS: 1
HTTPS_ONLY: "false"
# Admin bootstrap
DOORMAN_ADMIN_EMAIL: demo@doorman.dev
DOORMAN_ADMIN_PASSWORD: DemoPassword123!
JWT_SECRET_KEY: demo-secret-change-me-please-32-bytes-min
# Demo seeding
DEMO_SEED: "true"
# Ports (compose defaults already match these)
# Keep demo memory isolated; avoid writing/reading host dumps
MEM_AUTO_SAVE_ENABLED: "false"
MEM_DUMP_PATH: "/tmp/demo_memory_dump.bin"
ENABLE_STARLETTE_CORS: "true"
ALLOWED_ORIGINS: http://localhost:3000
ALLOW_METHODS: GET,POST,PUT,DELETE,OPTIONS,PATCH,HEAD
ALLOW_HEADERS: "*"
ALLOW_CREDENTIALS: "true"
PORT: 3001
WEB_PORT: 3000
@@ -39,6 +31,7 @@ services:
# Ensure it doesn't restart and doesn't persist bind mounts from base compose
restart: "no"
volumes: []
container_name: doorman-demo
demo-cleanup:
image: docker:27.3.1-cli

View File

@@ -8,8 +8,6 @@
# 1. Set MEM_OR_EXTERNAL=REDIS in .env
# 2. Run: docker compose --profile production up
version: '3.8'
services:
doorman:
build:

View File

@@ -67,6 +67,27 @@ load_env_files
trap graceful_stop SIGTERM SIGINT
# Start backend (Doorman) in the foreground so logs go to container stdout
echo "[entrypoint] Starting Doorman backend..."
(
cd /app/backend-services
# Ensure required directories
mkdir -p proto generated logs
python doorman.py run
) &
BACK_PID=$!
# Start web client (Next.js)
echo "[entrypoint] Starting web client..."
(
cd /app/web-client
# Start Next.js on WEB_PORT, bind to 0.0.0.0 for container networking
PORT="${WEB_PORT:-3000}" npm run start -- -H 0.0.0.0 -p "${WEB_PORT:-3000}"
) &
WEB_PID=$!
echo "[entrypoint] Services launched. Backend PID=$BACK_PID Web PID=$WEB_PID"
# Optional: Demo seeding (in-memory, for quick start demos)
if [ "${DEMO_SEED:-false}" = "true" ]; then
(
@@ -95,27 +116,6 @@ if [ "${DEMO_SEED:-false}" = "true" ]; then
) &
fi
# Start backend (Doorman) in the foreground so logs go to container stdout
echo "[entrypoint] Starting Doorman backend..."
(
cd /app/backend-services
# Ensure required directories
mkdir -p proto generated logs
python doorman.py run
) &
BACK_PID=$!
# Start web client (Next.js)
echo "[entrypoint] Starting web client..."
(
cd /app/web-client
# Start Next.js on WEB_PORT, bind to 0.0.0.0 for container networking
PORT="${WEB_PORT:-3000}" npm run start -- -H 0.0.0.0 -p "${WEB_PORT:-3000}"
) &
WEB_PID=$!
echo "[entrypoint] Services launched. Backend PID=$BACK_PID Web PID=$WEB_PID"
# Wait on either process to exit, then stop gracefully
wait -n || true
# Wait on either main service to exit, then stop gracefully
wait -n "$BACK_PID" "$WEB_PID" || true
graceful_stop

19
web-client/next.config.js Normal file
View File

@@ -0,0 +1,19 @@
/** @type {import('next').NextConfig} */
const isDemo = process.env.DEMO_MODE === 'true'
const nextConfig = {
// Keep production builds resilient (regular and demo)
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
async rewrites() {
if (!isDemo) return []
// Demo: proxy API to backend on 3001, keep browser same-origin (3000)
const target = process.env.GATEWAY_INTERNAL_URL || 'http://localhost:3001'
return [
{ source: '/platform/:path*', destination: `${target}/platform/:path*` },
{ source: '/api/:path*', destination: `${target}/api/:path*` },
]
},
}
module.exports = nextConfig

View File

@@ -1,6 +1,8 @@
// Gateway URL from environment - required for API calls
// Set NEXT_PUBLIC_GATEWAY_URL in root .env file (loaded via dotenv-cli for dev, build arg for Docker)
const GATEWAY_URL_RAW = process.env.NEXT_PUBLIC_GATEWAY_URL || ''
const DEMO = process.env.DEMO_MODE === 'true'
// In demo, force same-origin by ignoring NEXT_PUBLIC_GATEWAY_URL
const GATEWAY_URL_RAW = DEMO ? '' : (process.env.NEXT_PUBLIC_GATEWAY_URL || '')
let _cachedUrl: string | null = null