Skip to content

dsi-icl/create-ove-demo

Repository files navigation

create-ove-demo

A full-stack scaffold combining a FastAPI backend with a React/Vite frontend, complete with database migrations, caching, rate-limiting, authentication, WebSocket support, metrics, code generation and an in-browser debug overlay.


Table of Contents


Prerequisites

  • nvm/nvm-windows
  • Node.js ≥ 18.x (see correspending nvm documentation for installation steps)
  • pnpm
  • uv
  • Python 3.13
  • sqlite3 (for SQLite) or Docker & Docker Compose (for Postgres)
  • Git

Installation & Setup

  1. Clone the project:

    git clone https://github.com/dsi-icl/create-ove-demo.git
    cd create-ove-demo
  2. Install dependencies, build and link:

    pnpm install
    pnpm run link
  3. Create the project (can be run anywhere as create-ove-demo command is available globally after linking)

    create-ove-demo
    cd <your new project>
    • Ensure uv is configured to use the correct Python version, this can be done via the command: uv python pin 3.13 --global

    • You will be asked for a legacy auth key and a metrics passwords, use a secure password or generate one with a tool like pwgen or nanoid.

  4. Review and customize environment files:

    • backend/.env (generated by setup)
    • For production, create backend/.env.production

Configuration

Edit backend/.env (or .env.production) to control:

DATABASE_URL=# e.g. sqlite+aiosqlite:///data/main.db
CACHE_HOST=
CACHE_PORT=6379
CACHE_PASSWORD=
CACHE_ENABLED=true
CACHE_EXPIRATION=60

DISABLE_AUTH=false
USE_LEGACY_AUTH=true
LEGACY_AUTH_KEY=<your-secret>
PROTECT_METRICS=true
METRICS_USERNAME=metrics
METRICS_PASSWORD=<your-pass>
TOKEN_EXPIRY=3600

FRONTEND_ORIGIN=http://localhost:5173
WEBSOCKET_ORIGIN=ws://localhost:5173
PORT=8000
INTERVAL=30# tick interval in seconds

Frontend picks up VITE_ vars from backend/.env:

VITE_BACKEND=http://localhost:8000
VITE_ENABLE_DEBUG=true

Database Setup

SQLite (default)

No extra setup—backend/data/main.db is created automatically.
To run future migrations:

cd backend
uv run alembic upgrade head

Postgres

docker-compose up -d
cd backend
uv run alembic upgrade head

Authentication

  • Legacy OTP
    GET /auth/redirect?security_token=<LEGACY_KEY>&to=<URL>
    Issues session cookie, then redirects to <URL>.

  • Cookie validation
    GET /auth/validate → returns true if cookie valid.

  • API-Key flow
    GET /auth/token + Authorization: Bearer <API_KEY> → returns a one-time token for ?security_token=<token>.

Respect DISABLE_AUTH, USE_LEGACY_AUTH and cookie expiry (TOKEN_EXPIRY).


Metrics

Prometheus metrics exposed at GET /metrics (port 8000). Protected via HTTP Basic (METRICS_USERNAME/ METRICS_PASSWORD) unless DISABLE_AUTH=true.

Key metrics:

  • socket_active_connections
  • socket_events_total{event="…"}
  • socket_event_duration_seconds{event="…"}
  • Standard FastAPI HTTP counters

Caching & Rate Limiting

  • HTTP caching: decorate routes with @cached(expire=…) (Redis backend, JSON coder). Toggle with CACHE_ENABLED.
  • Rate limiting: decorate with @limiter.limit("100/second"). Defaults in app/core/rate_limiter.py.

Example:

@router.get("/example")
@limiter.limit("50/minute")
@cached
async def get_example(...):
    return [...]

WebSocket API

Socket.IO mounted at /ws/socket.io under namespace /v1. Supply:

  • room query param
  • valid session cookie (or DISABLE_AUTH=true)

Client example (TS):

import {createSocketClient} from "@/api/sockets-v1";

const client = createSocketClient(env.VITE_BACKEND, {
  path: "/ws/socket.io",
  withCredentials: true,
  query: {room: "myroom"},
});

client.connect();
client.onConnect(() => console.log("connected"));
client.emitStart();
client.onTick(payload => console.log("tick", payload));
client.emitStop();
client.emitReset();

Example events:

Event Direction Payload Description
get_state client→server none Ack: returns { status, timestamp }
start client→server none Begin periodic “tick”
stop client→server none Stop ticking
reset client→server none Reset state & timestamp
tick server→clients { timestamp } Broadcast every INTERVAL seconds

Rooms & Synchronization

Each physical Data Observatory maps to a unique room. All clients — whether controllers (which emit commands) or views (read-only pages) — connect to the same room and share state in real time.

  1. Controllers connect:

    import { createSocketClient } from "@/api/sockets-v1";
    const ctrl = createSocketClient(env.VITE_BACKEND, {
      path: "/ws/socket.io",
      withCredentials: true,
      query: { room: "data-observatory" },
    });
    ctrl.connect();
    // start the simulation for all viewers:
    ctrl.emitStart();
    // later…
    ctrl.emitStop();
    ctrl.emitReset();
  2. Views connect:

    import { createSocketClient } from "@/api/sockets-v1";
    const view = createSocketClient(env.VITE_BACKEND, {
      path: "/ws/socket.io",
      withCredentials: true,
      query: { room: "data-observatory" },
    });
    view.connect();
    view.emitGetState().then(state => renderState(state));
    view.onTick(payload => renderTick(payload));

All events are scoped to "data-observatory". When a controller emits start, every connected view in that room begins receiving tick broadcasts and stays in sync.


Code Generation

After backend schema or API changes:

  1. Generate JSON-Schemas & AsyncAPI

    cd backend
    uv run scripts/schemas.py

    Outputs:

    • backend/schemas/openapi.json
    • backend/schemas/asyncapi/v1.json
    • backend/schemas/entities/*.schema.json
  2. Sync frontend types & clients

    cd ../frontend
    pnpm run sync

    Runs:

    • cli/codegen/schemas.tssrc/api/schemas.ts
    • cli/codegen/api.tssrc/api/api.ts
    • cli/codegen/sockets.tssrc/api/sockets-v1.ts

Debug Overlay

Toggle the in-browser debug panel with Ctrl+K (or +K). It shows:

  • Live Socket.IO events & payloads
  • HTTP request/response log
  • Application log entries
  • Inline editing of client state via Zustand
  • Toasts on errors or forbidden actions

Enable in production via VITE_ENABLE_DEBUG=true.


Running in Development

Backend

cd backend
uv run fastapi dev app/main.py --port 8000
  • OpenAPI UI → http://localhost:$PORT/docs
  • AsyncAPI viewer → http://localhost:$PORT/public/asyncapi.html

Frontend

cd frontend
pnpm run dev

Visit http://localhost:5173


Running in Production (Docker)

docker-compose

docker-compose up -d

Services:

  • on port 80
  • redis for caching (port 6379)

Manual build & run

docker build -t registry.example.com/<your project>:latest .
docker run -d \
  --name <your project> \
  -p 80:80 \
  -v $(pwd)/backend/.env.production:/.env:ro \
  -v $(pwd)/backend/data:/data \
  registry.example.com/<your project>:latest

Project Structure

├── backend │ ├── app/ # FastAPI app: auth, cache, sockets, api, db │ ├── data/ # SQLite DB or Postgres volume │ ├── migrations/ # Alembic configs & versions │ ├── scripts/ # Codegen: OpenAPI, AsyncAPI, JSON schemas │ ├── public/ # Static docs (asyncapi.html, docs.html) │ ├── .env(.production) # Env vars │ └── main.py # Entrypoint ├── frontend │ ├── src/ │ │ ├── api/ # Generated API clients & schemas │ │ ├── components/ # UI primitives (shadcn) │ │ ├── lib/ # sockets.ts, store.ts, logger.ts │ │ ├── hooks/ # custom React hooks │ │ └── Debug.tsx # in-app debug overlay │ ├── cli/ │ │ └── codegen/ # TS codegen scripts │ ├── public/ # Vite static assets │ ├── package.json # scripts, deps, pnpm lock │ └── vite.config.ts ├── docker-compose.yml ├── Dockerfile └── README.md


CLI Scripts (Root-Level)

These npm scripts live in the root package.json (this CLI/scaffold tool):

"scripts": {
  "build":        "esbuild index.ts --bundle --format=cjs --platform=node --target=node22 --outfile=dist/index.cjs && rm -rf dist/assets/ && cp -R assets/ dist/assets/",
  "check":        "prettier --write . && eslint --fix",
  "clean":        "rm -rf dist",
  "format":       "prettier",
  "link":         "pnpm run build && pnpm link --global",
  "lint":         "eslint",
  "prepublishOnly":"pnpm run build"
}
  • build: bundle index.ts into dist/index.cjs, copy assets/.

  • check: run Prettier and ESLint fixes.

  • clean: remove dist/.

  • format: run Prettier.

  • lint: run ESLint.

  • link: build and globally link this CLI (pnpm link).

  • prepublishOnly: ensures a build before pnpm publish.

Tips & Tricks

  • Keep backend/.env* out of Git — store secrets securely.

  • After backend/API changes, always run:

	cd backend && uv run scripts/schemas.py
	cd ../frontend && pnpm run sync
  • Use React-Query DevTools in development.

  • Tune auth & cache via app/core/config.py.

  • Generate UI primitives with pnpx shadcn add button card ….

  • Monitor Prometheus metrics in Grafana or similar.

About

Scaffold an interactive demo using the Open Visualisation Environment

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •