Skip to content

feat: add production-ready Pi-hole & Unbound monitoring dashboard - #31

Merged
TimInTech merged 2 commits into
mainfrom
feat/dashboard-2921188429567859752
Oct 27, 2026
Merged

TimInTech merged 2 commits into
mainfrom
feat/dashboard-2921188429567859752

Conversation

@TimInTech

Copy link
Copy Markdown
Owner

This pull request implements a production-ready, read-only web dashboard to monitor the Pi-hole + Unbound DNS stack, as requested.

Implemented functionality:

  • Responsive, dark-themed, glassmorphic UI using vanilla HTML/JS/CSS (German language as required).
  • Fetches and displays statuses for Pi-hole (version, FTL status, upstreams, queries), Unbound (port, dig check), NetAlertX (availability), and overall System Health (API uptime/version).
  • Cross-references Pi-hole upstreams against the Unbound port to surface misconfigurations (shows a warning if Unbound isn't listed).

Architectural decisions:

  • Zero Heavy Frameworks: Built purely with vanilla technologies without external CDN dependencies for security and speed.
  • Backend Extension: Simply mounts the dashboard directory dynamically onto the existing start_suite.py FastAPI app.
  • Authentication Security: Requires the existing SUITE_API_KEY. Prompts the user on page load and stores the key in sessionStorage so it automatically clears when the tab closes.

Security considerations:

  • No API keys are embedded in frontend source.
  • Credentials are not persisted in localStorage.
  • Dashboard binds to 127.0.0.1 locally, matching existing security boundaries.
  • Remotely accessible securely via SSH port forwarding as documented.

Testing:

  • A new automated offline test suite (tests/test_suite.py) was introduced using pytest and fastapi.testclient.
  • Fully isolated: uses unittest.mock.patch to simulate subprocess results (e.g. pihole -v, dig, systemctl) without requiring actual system configurations.
  • Tested all authentication and connection scenarios successfully.
  • Added a GitHub Actions workflow to run the tests automatically against Python 3.10.

Known limitations:

  • The dashboard is completely read-only and deliberately provides no administrative functionality.
  • Device discovery explicitly falls back to indicating that NetAlertX is the module handling this functionality.

Instructions for safely testing on real hardware:

  1. Run ./install.sh on the system. It will safely copy the dashboard assets into $STATE_DIR/app/dashboard.
  2. Check that the service starts successfully via sudo systemctl status pihole-suite.service.
  3. Open an SSH tunnel: ssh -L 8090:127.0.0.1:8090 user@your-pihole-host.
  4. Open a browser to http://127.0.0.1:8090/.
  5. Enter your SUITE_API_KEY (from your .env file) when prompted and verify the UI.

PR created automatically by Jules for task 2921188429567859752 started by @TimInTech

- Adds a responsive, vanilla HTML/JS/CSS dashboard in `dashboard/`
- Implements secure API key authentication via `sessionStorage`
- Exposes dashboard through `start_suite.py` using FastAPI `StaticFiles`
- Integrates frontend assets into `install.sh` deployment
- Adds comprehensive mock-based test suite in `tests/test_suite.py`
- Implements Unbound configuration health check
- Sets up GitHub Actions CI workflow for automated testing
- Updates English and German documentation with dashboard details

Co-authored-by: TimInTech <129355283+TimInTech@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T21:48:43.007550Z 1cef18a PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1cef18a498

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread dashboard/app.js
Comment on lines +215 to +220
if (allHealthy) {
updateStatusIndicator('overall-status .status-indicator', 'healthy');
updateElementText('overall-status .status-text', 'Alle Kernsysteme gesund');
} else {
updateStatusIndicator('overall-status .status-indicator', 'warning');
updateElementText('overall-status .status-text', 'Probleme erkannt');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Target the overall status children without replacing the container

On every refresh, the overall status remains stuck at “Wird geladen...” regardless of whether the services are healthy or failing. Both update helpers use getElementById, so arguments such as overall-status .status-indicator and overall-status .status-text never resolve to elements. Additionally, the loading update replaces the wrapper's system-status class with status-indicator loading, applying the small indicator styling to the entire summary. Target the actual child elements, using dedicated IDs or selector-aware helpers, and preserve the wrapper's class.

Useful? React with 👍 / 👎.

Comment thread dashboard/app.js
Comment on lines +223 to +224
// Auto-refresh every 30 seconds
setTimeout(fetchData, 30000);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cancel the refresh loop when the user logs out

After a user logs out, the pending timeout still calls fetchData, sends all six requests with a null API key, and schedules another timeout. Logging in again starts an additional independent refresh loop, so each logout/login cycle permanently increases polling and the associated backend subprocess work. Store and clear the timeout on logout, guard refreshes against an unauthenticated state, and prevent an in-flight refresh from rescheduling after logout.

Useful? React with 👍 / 👎.

- Updated `actions/checkout` to `v4` and `actions/setup-python` to `v5` to avoid Node.js 20 deprecation warnings.
- Added `PYTHONPATH=.` before `pytest tests/` in CI workflow so that `start_suite.py` can be discovered and imported.

Co-authored-by: TimInTech <129355283+TimInTech@users.noreply.github.com>
@TimInTech
TimInTech merged commit 2280389 into main Oct 27, 2026
4 checks passed
@TimInTech
TimInTech deleted the feat/dashboard-2921188429567859752 branch October 27, 2026 22:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant