191 lines
9.6 KiB
Markdown
191 lines
9.6 KiB
Markdown
# Public Enelix Documentation Publisher
|
|
|
|
## Context and decision
|
|
|
|
The license portal already serves public static files and exposes its effective
|
|
catalog at `GET /api/catalog`. Documentation is therefore built as static HTML;
|
|
the existing portal process, authentication, database and payment code are not
|
|
modified. Generic Utils runtime code is not coupled to the documentation service.
|
|
|
|
Source ownership:
|
|
|
|
- `docs/public/*.md` in Enelix-EMS: installation, operator guides, FAQ and pricing explanation.
|
|
- `docs/public/images/*.png`: explicitly reviewed real Symcon screenshots, allowlisted in `images.mjs`.
|
|
- Explicit README/interface allowlists in `config.mjs`: original EMS and Utils references.
|
|
- Backend catalog: every monetary value, product name, SKU, term and effective VAT rate.
|
|
- This directory: rendering, navigation, publication and tests.
|
|
|
|
The default source branch is `develop`, visibly identified as Testing. The
|
|
publisher reads separate shallow bare mirrors; it never changes existing
|
|
developer checkouts. All sources are pinned to the commits captured at the start
|
|
of a successful run. Neither unpublished worktrees nor internal handoffs, open
|
|
issues, credentials, operational notes or arbitrary new files are published.
|
|
|
|
Alternative considered: adding server-side documentation routes. Not selected:
|
|
the current static mount enables this rollout without a portal restart or a new
|
|
public service. A separately maintained copy of prices was rejected because it
|
|
would drift from checkout. The browser refreshes the same catalog endpoint every
|
|
30 seconds while visible, on reappearance and on manual refresh.
|
|
|
|
## Public routes
|
|
|
|
- `/docs/index.html`
|
|
- `/docs/erste-schritte.html`
|
|
- `/docs/installation.html`
|
|
- `/docs/manager.html`
|
|
- `/docs/verbraucher.html`
|
|
- `/docs/utils.html`
|
|
- `/docs/faq.html`
|
|
- `/docs/preise.html`
|
|
- `/docs/ems/*.html` and `/docs/utils/*.html`
|
|
- `/docs/manifest.json`: published source commits and file mappings.
|
|
- `/docs-status.json`: last attempted/successful synchronization, no internal errors.
|
|
|
|
The portal's anonymous login screen links to documentation, FAQ and prices.
|
|
Authenticated sidebar navigation additionally links to documentation and prices.
|
|
|
|
Within the EMS references, Manager remains a direct link; consumer modules and
|
|
technical interfaces are nested in native, keyboard-accessible disclosure groups.
|
|
The current page's group opens on the server, also without JavaScript. Search
|
|
expands matching groups and restores their previous state when cleared. All
|
|
page URLs remain unchanged; new references without a group stay accessible.
|
|
|
|
`assets/portal-links.css` contains only the added navigation styling.
|
|
`portal-navigation.patch` records the exact additive portal HTML change; check
|
|
it against the current portal before applying it and copy the stylesheet to
|
|
`public/docs-portal.css`. Never replace the full portal HTML from another checkout.
|
|
|
|
## Install and publish
|
|
|
|
Deploy the reviewed publisher files to `/home/agent/services/public-docs` as
|
|
the unprivileged `agent` user. Do not copy `node_modules`, state, preview, QA or
|
|
backups into a repository or the public directory. Install locked dependencies:
|
|
|
|
```sh
|
|
npm ci --ignore-scripts
|
|
npm test
|
|
```
|
|
|
|
For rendering/rollback checks without changing public pages:
|
|
|
|
```sh
|
|
node preview.mjs
|
|
node verify.mjs
|
|
node browser-test.mjs
|
|
```
|
|
|
|
Publication uses the existing public mount at
|
|
`/home/agent/services/license/public`. The portal container's UID must be able to
|
|
read generated directories and files. No credentials are included in the
|
|
publisher: Git uses the server's preconfigured access. Never put credentials in
|
|
repository URLs or public output.
|
|
|
|
Install the two supplied units through `systemctl --user link`, reload the user
|
|
manager, start `enelix-public-docs.service` once and enable
|
|
`enelix-public-docs.timer`. Only these user units are affected. The timer checks
|
|
Git five minutes after completion, with at most fifteen seconds of jitter.
|
|
The successful first run can take longer while fetching both repositories.
|
|
|
|
`DOCS_BRANCH` accepts `develop`, `beta` or `main`; reading a source branch does
|
|
not merge or modify it. `DOCS_PUBLIC_DIR` and `DOCS_STATE_DIR` support isolated
|
|
tests. Keep the chosen channel explicit in the service configuration.
|
|
|
|
## Git-only operation and historical bootstrap
|
|
|
|
The production service does not enable `DOCS_ALLOW_DRAFTS`. All approved guides
|
|
and screenshots must exist in the selected Git branch. A missing source aborts
|
|
publication and preserves the last good release; deployment copies cannot
|
|
silently replace deleted Git content.
|
|
|
|
The initial 2026-10-06 deployment used explicitly labelled drafts while Git
|
|
publication awaited authorization. Daniel approved commit and push on the same
|
|
day. The bootstrap fallback remains available only through the explicit
|
|
`DOCS_ALLOW_DRAFTS=true` setting, including isolated previews. It reads missing
|
|
sources from the deployment-only `guides/` directory and labels them as drafts.
|
|
Do not treat these bootstrap copies as a second long-term editing location.
|
|
|
|
When completing a bootstrap deployment, push the approved sources first, verify
|
|
that publication uses their Git commits, then install the production unit without
|
|
the draft setting and reload the user service manager.
|
|
|
|
## Security and resilience
|
|
|
|
Markdown is rendered with pinned Marked and sanitized with sanitize-html.
|
|
Raw HTML and remote images are disabled; link schemes and local mappings are
|
|
restricted. Relative links to unpublished files are plain text. The existing
|
|
portal CSP remains active; there is no inline JavaScript or browser-side Git
|
|
credential. Every page is readable without JavaScript except the live price
|
|
table, which shows an explicit notice and a catalog link in that case.
|
|
|
|
Only the exact local PNG paths in `images.mjs` render as screenshots. The
|
|
publisher reads binary files at the same EMS commit as the guides, checks regular
|
|
file type, PNG signature and dimensions, and publishes content-hashed filenames.
|
|
Image hashes, dimensions and source revisions are recorded in the manifest.
|
|
Missing or invalid images fail publication and preserve the last good release.
|
|
Draft bootstrap images follow the same explicit opt-in as the draft guides.
|
|
|
|
Screenshot refresh is intentionally reviewed, not a live camera into Symcon.
|
|
Capture through the local-only web console in a separate browser session, without
|
|
saving configuration or invoking regulation. Select useful crops, inspect them
|
|
for license codes, installation IDs, accounts and private plant details, then
|
|
replace the approved PNGs in Git and update the dated captions. Never publish
|
|
unreviewed captures automatically. Current screenshots show the demo object tree
|
|
and the manager basic settings in Symcon 8.0 on 2026-10-06. Test values are not
|
|
recommended installation settings; reference READMEs may evolve faster than the
|
|
dated screenshots.
|
|
|
|
Publication is a complete immutable release followed by an atomic `docs`
|
|
symlink switch. Failed fetches or rendering leave the previous release intact.
|
|
Public status reports failed or overdue checks. Stale prices are removed when
|
|
the catalog cannot be obtained or validated; no hardcoded fallback is used.
|
|
|
|
The sync lock prevents overlapping manual and timer runs. If the process is
|
|
forcibly terminated, inspect process state before removing its stale lock from
|
|
the publisher state directory. Generated releases are retained to permit
|
|
rollback; disk usage should be monitored and old releases pruned only after
|
|
checking that they are not current or needed for rollback.
|
|
|
|
## Verification and rollback
|
|
|
|
`npm test` covers relative and cross-repository links, HTML sanitization,
|
|
allowlist isolation, headings/tables, price validation, VAT and periods.
|
|
`node verify.mjs` validates all published links and deliberately exercises a
|
|
source failure against an isolated mirror, proving last-good preservation.
|
|
`node browser-test.mjs` checks desktop, 390px and 320px layouts, search, automatic
|
|
catalog refresh, newly returned products, VAT toggle, API failure/recovery and
|
|
JavaScript-free documentation. Mocks affect only a fresh browser context, never
|
|
the production catalog. Screenshots remain private in `qa/`.
|
|
|
|
`node verify.mjs live` and `node browser-test.mjs --live` perform anonymous checks
|
|
on the actual domain. The tests must not log in or mutate live application data.
|
|
|
|
For rollback, first stop and disable only the documentation timer. Restore the
|
|
previous documentation symlink atomically. For full removal, revert only the
|
|
added portal navigation and stylesheet link after checking for concurrent edits;
|
|
do not overwrite the portal with an older full backup if other edits occurred.
|
|
No database, Nginx or IP-Symcon rollback is required because those were unchanged.
|
|
|
|
## Initial deployment evidence
|
|
|
|
2026-10-06: 29 pages, 1,116 valid local links, 12 unit tests passed, isolated
|
|
failure recovery passed, desktop/mobile browser tests passed both in preview and
|
|
against the public domain. The initial sources were EMS
|
|
`58f91e431936fedca3e59178037b939f420ce3e3` and Utils
|
|
`82860c0eb938173841088f935a306729b6ce9a1c`. These are dated evidence, not permanent
|
|
source pins. The price API initially returned 15 products and was accessed
|
|
without login.
|
|
|
|
Portal navigation backup:
|
|
`/home/agent/services/public-docs/backups/2026-10-06T12-58-13-558Z/index.html`.
|
|
The backup is private and must never be copied into `public/`.
|
|
|
|
## Pull request summary
|
|
|
|
Add public documentation and FAQ from reviewed Git-backed sources, plus a live
|
|
backend-driven price table. Preserve existing portal authentication and data;
|
|
publish static releases atomically with a five-minute user timer. No schema or
|
|
module migration and no beta/main changes. Include the end-user introduction,
|
|
guided first steps, reviewed Symcon screenshots and grouped module navigation.
|
|
Production publication requires versioned guides and screenshots, without draft
|
|
fallback.
|