docs: publish public guides, screenshots and Git-synced portal documentation
Tests / test (push) Failing after 50s
Tests / test (push) Failing after 50s
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user