QDG Knowledge Base Read-only viewer QWebHub
overview

Architecture

Version 2 · Document the fourth mounted module and QWebHub-owned shared theme architecture.

QWebHub Architecture

QWebHub is a shared Django hub that mounts several otherwise-independent Django projects (QDBAdmin, QProcess, QDG KB Viewer) under one server, each under its own URL prefix, plus a landing page listing them.

The module registry (the core pattern)

Everything routes through one file: qwebhub/modules.py. A Module is a frozen dataclass:

@dataclass(frozen=True)
class Module:
    key: str
    url_prefix: str
    type: str  # "django" | "asgi" | "proxy"
    project_subdir: str = ""
    urlconf: str = ""
    app: str = ""
    db_alias: str = "qdb"
    asgi_factory: str = ""   # future
    proxy_target: str = ""   # future
    enabled: bool = True
    notes: str = ""

MODULES is a plain list of these. Today it has 4 entries, all type="django": qdbadmin (→ AdminManagement/QDBAdmin), qprocess (→ QProcess), qdgkb (→ QDG KB Viewer, the read-only Knowledge Base viewer, db_alias="qdgwiki"), and garybreeding (→ Gary Breeding Report). asgi/proxy types are already scaffolded in the dataclass for future non-Django modules but nothing uses them yet.

Adding a new linked project is one Module(...) entry appended to this list — nothing else needs to change. This was confirmed by direct code reading (see ClickUp task 86d40jy36), tracing through every layer below.

How one registry entry becomes a working link

  1. qwebhub/urls.py loops enabled_django_modules() and appends path(m.url_prefix, include(m.urlconf)) for each — the root URLconf is entirely generated from the registry, never hand-edited per module.
  2. core/views.py's index view reads the same enabled_django_modules() list and passes {key, url_prefix, notes} per module to the template. A separate healthz view returns {"status": "ok", "modules": [list of keys]} as JSON — a lightweight liveness/inventory check.
  3. core/templates/hub/index.html loops over that list and renders one card per module (key, notes, link to /{{ url_prefix }}) — nothing about any specific project is hardcoded in the template.

Each Module.project_root resolves project_subdir against PROJECTS_ROOT (from QWEBHUB_PROJECTS_ROOT env var, or auto-detected as the folder containing QWebHub itself) — so the same registry works unmodified on a dev laptop and on the server, as long as sibling project folders exist at the same relative layout.

Shared theme ownership

QWebHub is the controlling light/dark theme layer for every mounted Django module. SharedThemeMiddleware injects the QWebHub-owned bootstrap script near the start of each HTML <head> and the palette override at the end. Non-HTML, streaming, and encoded responses are left unchanged.

The established qdb-theme selection is stored in browser local storage and a path-wide cookie. The bootstrap sets data-theme and data-bs-theme before module styling renders, so one selection follows the user between the hub, QDBAdmin, QProcess, QDG KB Viewer, and Gary Breeding Reports. Modules should use compatible CSS variables rather than create independent theme preferences.

QWebHub v1.2.2 defines light mode as a neutral light-grey background (#eceff1) with near-white cards, white inputs, dark text, 500-weight body copy, and 600-weight labels. Dark-mode tokens remain unchanged.

Constraint that must never be reintroduced: template/static namespacing

Django's template loader is one flat search list across every configured directory. Each module's own top-level templates (e.g. base.html) must live under a subfolder namespaced to that module (templates/core/base.html for QDBAdmin, templates/racing/base.html for QProcess, core/templates/hub/base.html for the hub itself) — never a bare templates/base.html. A shared, un-namespaced file would silently shadow another module's layout with no error, just a broken-looking page.

This was a real regression, fixed 2026-08-01 across all three modules, and is documented as a hard rule in qwebhub/config.py's module_template_dirs(). The same namespacing applies to static assets (module_static_dirs()) — modules namespace assets below static/<module-key>/ for the same collision-safety reason.

Related pages

Updated by Codex on Aug. 20, 2026, 7:24 a.m. · Task: Document QWebHub v1.2.2 theme and deployment delivery · Commit: 0509d036b5134568451a380834937832d44bb810