About This Guide
This developer guide is built with Docusaurus and lives inside the monorepo at apps/upp-guide/. At runtime it is not served by Node or Docusaurus: the built HTML/JS/CSS is delivered by Nginx inside the Docker stack.
How It Is Served (Docker Stack)
In docker/nx/docker-compose.yml, the documentation service (container upp-docs, image Nginx) mounts the entire host folder docs/ read-only at /usr/share/nginx/html/docs (Compose paths are relative to docker/nx/). Its config (docker/nx/nginx-docs.conf) then uses paths under that single mount:
how.*:rootis/usr/share/nginx/html/docs/upp-guide(built guide from hostdocs/upp-guide/).doc.*:locationblocks such as/swagger/,/codedoc/,/coverage/,/e2e/useroot /usr/share/nginx/html/docs, so each URL maps to the matching subdirectory on the host (docs/swagger/,docs/codedoc/, etc.).
Canonical path on the host for the built guide output remains docs/upp-guide/ (upp-guide:build writes there). There is no separate container path alias for the guide beyond .../docs/upp-guide.
| Piece | Role |
|---|---|
| Traefik (same stack) | Terminates TLS and forwards https://how.nx.{domain}/ and https://doc.nx.{domain}/ to the documentation service |
documentation / Nginx | HTTP server; how.* serves the guide from docs/upp-guide inside the mount; doc.* serves swagger, codedoc, coverage, e2e, and the guide at / |
| Volumes | One bind: host docs/ → /usr/share/nginx/html/docs; see docker-compose.yml |
The same Nginx container also serves doc.* (codedoc, coverage, swagger, e2e, etc.) under other server / location blocks. how.* is the hostname dedicated to the developer guide.
With the stack up, open https://how.nx.{domain}/ — you are hitting that Nginx in Docker, not a dev process on the host.
Build and Update Flow
To build or update the guide (required for https://how.nx.{domain}/ — static files come from docs/upp-guide/, not from apps/upp-guide/ at request time):
cd docker/nx
docker compose exec nx-dev bash -c "cd /app && npx nx run upp-guide:build"
This:
- Builds
upp-test(demos) with--base-href=/demos/ - Runs Docusaurus build, outputting to
docs/upp-guide/ - Copies the built demos into
docs/upp-guide/demos/
Because the upp-docs container bind-mounts the whole docs/ tree from the host, the updated content is served immediately after the build; reload the page in the browser.
403 Forbidden on how.* with a full docs/upp-guide/ on the host usually means the upp-docs container does not see those files (e.g. an empty bind on Docker Desktop). Verify the mount:
docker exec upp-docs test -f /usr/share/nginx/html/docs/upp-guide/index.html && echo OK
If that fails, recreate documentation from docker/nx: docker compose up -d --force-recreate documentation.
Source Structure
apps/upp-guide/
├── docs/ # Markdown pages (the actual content)
│ ├── intro.md # Home page (slug: /)
│ ├── getting-started/
│ ├── core-concepts/
│ ├── libraries/
│ ├── guides/
│ ├── about-this-guide.md
│ └── resources.md
├── src/
│ ├── components/ # Custom React components usable from Markdown
│ └── css/custom.css # Theme overrides
├── sidebars.ts # Sidebar tree definition
├── docusaurus.config.ts # Site configuration, navbar, footer, custom fields
└── project.json # Nx targets (serve, build)
Adding a New Page
- Create the Markdown file in the appropriate
docs/subfolder. Use the standard frontmatter:
---
sidebar_position: 3
---
# Page Title
Content goes here.
sidebar_position controls the order within its category.
- Register it in the sidebar. Open
sidebars.tsand add the document id (path relative todocs/, without.md) in the right place:
{
type: 'category',
label: 'Guides',
items: [
'guides/adding-a-feature',
'guides/adding-a-widget',
'guides/my-new-page', // ← new page
],
},
- Build and refresh. Run
npx nx run upp-guide:buildinsidenx-devand reload the guide in the browser.
Adding a New Category
To create a new top-level section (like "Getting Started" or "Guides"), add a category object in sidebars.ts:
{
type: 'category',
label: 'New Section',
items: [
'new-section/first-page',
'new-section/second-page',
],
},
Then create the corresponding folder and files under docs/.
Using Custom Components
Docusaurus supports MDX, so you can import and use React components directly in Markdown. Custom components live in src/components/.
Example (from resources.md):
import ResourceLinks from '@site/src/components/ResourceLinks';
<ResourceLinks />
To create a new component, add a .tsx file in src/components/ and import it from any .md page using the @site/ alias.
Custom Fields
docusaurus.config.ts exposes runtime variables through customFields, populated from docker/nx/.env:
| Field | Value | Purpose |
|---|---|---|
domain | nx.{domain} (e.g. nx.unpispas4.mywire.org) | NX subdomain |
swaggerUrl | https://doc.nx.{domain}/swagger/ | Swagger UI |
doxygenUrl | https://doc.nx.{domain}/codedoc/ | PHP code documentation |
coverageUrl | https://doc.nx.{domain}/coverage/ | Test coverage reports |
appUrl | https://app.nx.{domain}/ | Main POS application |
apiUrl | https://api.nx.{domain}/ | Backend REST API |
Components can read these via useDocusaurusContext().siteConfig.customFields.
Documentation Services Overview
The project maintains several documentation services, all served through Docker and Traefik:
| Service | URL | Source | How to update |
|---|---|---|---|
| Developer Guide | https://how.nx.{domain}/ | apps/upp-guide/docs/ | Edit Markdown, rebuild with nx run upp-guide:build |
| Swagger (API) | https://doc.nx.{domain}/swagger/ | server/openapi/openapi.yaml + server/openapi/publish.mjs | Edit the spec (and the publish script if the UI shell changes); run npm run swagger:to-docs or npx nx run upp-guide:build to refresh docs/swagger/ for the doc host |
| Doxygen (PHP) | https://doc.nx.{domain}/codedoc/ | PHP source docblocks | Regenerate with Doxygen |
| Coverage | https://doc.nx.{domain}/coverage/ | Test output | Run tests with --coverage, then npm run coverage:to-docs |
All four are accessible from the navbar links and from the Resources page.
Writing Guidelines
- Write in English (consistent with the codebase comment rules).
- Use standard Markdown with Docusaurus extensions (admonitions, tabs, MDX).
- Keep pages focused on one topic. Prefer multiple short pages over one long page.
- Include code examples wherever possible — runnable snippets are better than descriptions.
- For widget documentation, include an interactive demo from
upp-testwhen available. - Use relative links between pages (e.g.
[Data Model](/core-concepts/data-model)).