Add the forge-panel sidecar + forgejo-issues read path (#31) #33

Merged
pedromcaraujo merged 1 commit from panel-forgejo-issues into main 2026-08-26 11:49:29 +01:00

Step 2 of #31, stacked on #32 (base branch panel-drawer). Fills the drawer with its first plugin: read-only Forgejo issues.

What's here

  • web/panel.py — new loopback sidecar. Reads a module-generated panels.json (channel → plugin config, secrets by path only) and exposes:
    • GET /panel/<channel>/issues — open issues (number, title, state, labels). PRs excluded via type=issues.
    • GET /panel/<channel>/issues/<n> — detail, with the body rendered to sanitized HTML by Forgejo's own /markdown API.
      It holds the repo + token and never hands them to the browser; the token is read from its file per request so rotation reaches a running service. Upstream error bodies are never forwarded (they can echo the token) — only the status.
  • modules/launcher.nix — generate panels.json, add the forge-panel systemd service (runs as the session owner so it can read the deploy's tokenFiles; TLS pinned via pkgs.cacert), and an nginx /panel/ route. Both gated on at least one channel declaring a panel.
  • web/index.html — the forgejo-issues renderer: an accordion of open issues that lazily fetches detail on expand (single-expand), shows label chips, the rendered body, and an Open in Forgejo link, plus a drawer refresh button. A small PANEL_RENDERERS registry keyed by panel type keeps this pluggable. Only Forgejo-sanitized body_html is ever set via innerHTML; everything else uses textContent.

Verification

  • JS parse check + py_compile pass; full nixosSystem build produces panels.json ({"app":{…}}, plain channel dropped) and the /panel/ route.
  • End-to-end against the live Forgejo (this repo): /issues returns the 10 open issues (PR #32 correctly excluded), /issues/31 renders ~7.7 KB of sanitized HTML with author/labels/link, and an unknown channel 404s.

Deferred

Click-to-inject (step 3), quick-add (step 4), and README/docs + mobile polish (step 5).

Refs #31.

Step 2 of #31, stacked on #32 (base branch `panel-drawer`). Fills the drawer with its first plugin: read-only Forgejo issues. ## What's here - **`web/panel.py`** — new loopback sidecar. Reads a module-generated `panels.json` (channel → plugin config, secrets by *path* only) and exposes: - `GET /panel/<channel>/issues` — open issues (number, title, state, labels). PRs excluded via `type=issues`. - `GET /panel/<channel>/issues/<n>` — detail, with the body rendered to **sanitized HTML by Forgejo's own `/markdown` API**. It holds the repo + token and never hands them to the browser; the token is read from its file per request so rotation reaches a running service. Upstream error bodies are never forwarded (they can echo the token) — only the status. - **`modules/launcher.nix`** — generate `panels.json`, add the `forge-panel` systemd service (runs as the session owner so it can read the deploy's tokenFiles; TLS pinned via `pkgs.cacert`), and an nginx `/panel/` route. Both gated on at least one channel declaring a panel. - **`web/index.html`** — the `forgejo-issues` renderer: an accordion of open issues that lazily fetches detail on expand (single-expand), shows label chips, the rendered body, and an **Open in Forgejo** link, plus a drawer refresh button. A small `PANEL_RENDERERS` registry keyed by panel type keeps this pluggable. Only Forgejo-sanitized `body_html` is ever set via `innerHTML`; everything else uses `textContent`. ## Verification - JS parse check + `py_compile` pass; full `nixosSystem` build produces `panels.json` (`{"app":{…}}`, plain channel dropped) and the `/panel/` route. - **End-to-end against the live Forgejo** (this repo): `/issues` returns the 10 open issues (PR #32 correctly excluded), `/issues/31` renders ~7.7 KB of sanitized HTML with author/labels/link, and an unknown channel 404s. ## Deferred Click-to-inject (step 3), quick-add (step 4), and README/docs + mobile polish (step 5). Refs #31.
Add the forge-panel sidecar and forgejo-issues read path (#31)
All checks were successful
CI / check (pull_request) Successful in 6s
c8022b0cd0
Fills the drawer from step 1 with its first plugin.

- web/panel.py: new loopback sidecar. Reads a module-generated panels.json
  (channel -> plugin config; secrets by path only) and, per channel, lists
  open Forgejo issues and fetches one issue's detail — body rendered to
  sanitized HTML via Forgejo's own /markdown API. It holds the repo/token and
  never hands them to the browser; the token is read from its file per request
  so rotation reaches a running service. PRs are excluded (type=issues).
- launcher.nix: generate panels.json, add the forge-panel systemd service
  (runs as the session owner, TLS pinned via pkgs.cacert) and an nginx
  /panel/ route — both gated on at least one channel declaring a panel.
- index.html: the forgejo-issues renderer — an accordion of open issues that
  lazily fetches detail on expand (single-expand), shows label chips, the
  rendered body, and an "Open in Forgejo" link, plus a drawer refresh button.
  A small plugin registry keyed by panel type keeps this pluggable.

Click-to-inject (step 3), quick-add (step 4), and docs (step 5) follow.
Refs #31.
pedromcaraujo changed target branch from panel-drawer to main 2026-08-26 11:47:00 +01:00
pedromcaraujo force-pushed panel-forgejo-issues from c8022b0cd0
All checks were successful
CI / check (pull_request) Successful in 6s
to a036fbf18c
All checks were successful
CI / check (pull_request) Successful in 8s
2026-08-26 11:48:51 +01:00
Compare
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pedromcaraujo/forge!33
No description provided.