Skip to content

[BOUNTY #5905] Health Dashboard#12

Open
zhaog100 wants to merge 2 commits into
ubiquity:mainfrom
zhaog100:feat/health-dashboard
Open

[BOUNTY #5905] Health Dashboard#12
zhaog100 wants to merge 2 commits into
ubiquity:mainfrom
zhaog100:feat/health-dashboard

Conversation

@zhaog100

Copy link
Copy Markdown

Summary

Real-time health dashboard for all UBQ.FI apps and plugins, built directly into the Cloudflare Worker router.

Endpoints

Endpoint Type Description
/health-dashboard HTML Dark-themed dashboard page
/health.json JSON Machine-readable API

Features

  • Apps: Checks pay, work, dashboard, docs subdomains
  • Plugins: Auto-discovers from ubiquity-os-marketplace GitHub org
  • Status levels: 🟢 Healthy (2xx) / 🟡 Degraded (4xx) / 🔴 Unhealthy (5xx/timeout)
  • Summary cards: Total, healthy, degraded, unhealthy counts
  • Per-service: Response time (ms) + HTTP status code
  • 5s timeout per health check
  • No caching (real-time data)

Interop

Works with the Plugin Health Monitor cron job for alerting.

Closes #3 (linked from devpool-directory #5905)

Dynamically generates sitemaps for all UBQ.FI apps and plugins.

- /sitemap.xml: Standard XML sitemap for search engines
- /sitemap.json: Machine-readable JSON index for interop

Apps (static): root, www, pay, work, dashboard, docs
Plugins (dynamic): fetched from ubiquity-os-marketplace GitHub org

Both endpoints cached for 1 hour (Cache-Control).

Closes ubiquity#2 (linked from devpool-directory #5906)
Real-time status monitoring for all UBQ.FI services.

Endpoints:
- /health-dashboard — HTML dashboard (dark theme, auto-styled)
- /health.json — JSON API

Features:
- Checks all app subdomains (pay, work, dashboard, docs)
- Dynamically discovers plugins from ubiquity-os-marketplace org
- 5s timeout per check
- Status: healthy (2xx) / degraded (4xx) / unhealthy (5xx/timeout)
- Summary cards: total, healthy, degraded, unhealthy
- Response time and HTTP status per service
- Interops with Plugin Health Monitor (ubiquity-os/.github#12)

Closes ubiquity#3 (linked from devpool-directory #5905)
@coderabbitai

coderabbitai Bot commented Mar 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request introduces a health monitoring system and sitemap generation for UBQ.FI services. A new health-dashboard.ts module performs HTTP health checks on app subdomains and discovered plugin repositories, aggregating results into a dashboard object and rendering both JSON and HTML outputs. A new sitemap.ts module generates XML and JSON sitemaps covering the same apps and plugins by querying the GitHub API. The worker.ts file adds four new routes: /sitemap.xml and /sitemap.json for sitemap delivery, and /health-dashboard, /health-dashboard.html, and /health.json for health dashboard access. Both features include error handling and cache control headers.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Sitemap functionality (buildSitemap in sitemap.ts) is out-of-scope relative to the linked issue #3 which only requires health dashboard, though it supports discoverability. Remove sitemap.ts changes or open a separate PR/issue for sitemap functionality, as it extends beyond the health dashboard requirement in issue #3.
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly references the bounty and dashboard feature, directly aligning with the main change of implementing a health dashboard.
Description check ✅ Passed Description provides comprehensive overview of the health dashboard feature, endpoints, and functionality, matching the actual changes.
Linked Issues check ✅ Passed PR implements all core requirements: public health dashboard reporting status of apps/plugins, real-time data, plugin auto-discovery, status classification, and interoperability with Plugin Health Monitor.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🧹 Nitpick comments (2)
src/sitemap.ts (1)

51-51: Unused type field: pushed_at is declared but never used.

Either use it for lastmod entries or remove from type.

src/health-dashboard.ts (1)

206-211: Minor XSS consideration: plugin/app names are inserted into HTML without escaping.

If a malicious repo name contained <script>, it would execute. Low risk since names come from GitHub API, but worth sanitizing.

Add escapeHtml helper
function escapeHtml(str: string): string {
  return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

Then use: ${escapeHtml(a.name)} and ${escapeHtml(p.name)} in the template.

Also applies to: 219-224


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2a00ddec-86dc-442a-adb6-b337ee290a4d

📥 Commits

Reviewing files that changed from the base of the PR and between a2634c5 and a9c3368.

📒 Files selected for processing (3)
  • src/health-dashboard.ts
  • src/sitemap.ts
  • src/worker.ts

Comment thread src/health-dashboard.ts
* Checks availability of all apps and plugins.
*/

const APP_SUBDOMAINS = ["pay", "work", "dashboard", "docs"] as const;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

APP_SUBDOMAINS differs from sitemap.ts:6-13.

Sitemap includes root ("") and "www", health dashboard doesn't. Consider unifying or intentionally documenting the difference.

Comment thread src/health-dashboard.ts
Comment on lines +70 to +83
async function fetchPluginRepos(): Promise<string[]> {
try {
const res = await fetch(`${GITHUB_API}/orgs/ubiquity-os-marketplace/repos?per_page=100&sort=updated`, {
headers: { Accept: "application/vnd.github.v3+json", "User-Agent": "ubq-fi-health" },
});
if (!res.ok) return [];
const repos: Array<{ name: string }> = await res.json();
return repos
.filter((r) => !r.name.startsWith(".") && !["command-config"].includes(r.name))
.map((r) => r.name);
} catch {
return [];
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Exclusion list mismatch with sitemap.ts:55.

This function only excludes "command-config", while sitemap.ts excludes 11 repos (command-, daemon-). Health dashboard will show plugins that sitemap.xml filters out.

Proposed fix: Extract shared exclusion list

Create a shared constant in a utils file:

// src/utils/plugin-config.ts
export const EXCLUDED_REPOS = [
  "command-config",
  "command-wallet", 
  "command-query",
  "daemon-pricing",
  "daemon-merging",
  "daemon-disqualifier",
  "daemon-task-matcher",
  "daemon-planner",
  "daemon-xp",
  "daemon-spec-rewriter",
];

Then import and use in both files.

Comment thread src/health-dashboard.ts
</table>
</section>

<p class="generated">Generated: ${data.generated} • <a href="/health.json">JSON API</a> • <a href="/health-history">History</a></p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Dead link: /health-history endpoint doesn't exist.

Remove the link or implement the endpoint.

Quick fix: Remove unimplemented link
-  <p class="generated">Generated: ${data.generated} • <a href="/health.json">JSON API</a> • <a href="/health-history">History</a></p>
+  <p class="generated">Generated: ${data.generated} • <a href="/health.json">JSON API</a></p>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p class="generated">Generated: ${data.generated} <a href="/health.json">JSON API</a> <a href="/health-history">History</a></p>
<p class="generated">Generated: ${data.generated} <a href="/health.json">JSON API</a></p>

Comment thread src/sitemap.ts
Comment on lines +6 to +13
const APP_SUBDOMAINS = [
"",
"www",
"pay",
"work",
"dashboard",
"docs",
] as const;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

APP_SUBDOMAINS includes root ("") and "www" here, but health-dashboard.ts uses a different set without these.

This mismatch means sitemap shows 6 apps while health dashboard shows 4.

Also applies to: 89-95

Comment thread src/sitemap.ts
Comment on lines +38 to +49
async function fetchPluginRepos(): Promise<string[]> {
const res = await fetch(`${GITHUB_API}/orgs/ubiquity-os-marketplace/repos?per_page=100&sort=updated`, {
headers: {
"Accept": "application/vnd.github.v3+json",
"User-Agent": "ubq-fi-sitemap-generator",
},
});

if (!res.ok) {
console.error(`Failed to fetch plugin repos: ${res.status}`);
return [];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how many repos exist in ubiquity-os-marketplace
curl -s "https://api.github.com/orgs/ubiquity-os-marketplace" | jq '.public_repos'

Repository: ubiquity/ubq.fi-router

Length of output: 68


🏁 Script executed:

# Check the full sitemap.ts file to understand cache implementation
cat -n src/sitemap.ts

Repository: ubiquity/ubq.fi-router

Length of output: 4319


🏁 Script executed:

# Search for all GitHub API calls in the codebase
rg "github\.com|GITHUB_API" -A 3 -B 3

Repository: ubiquity/ubq.fi-router

Length of output: 1601


🏁 Script executed:

# Search for cache-related code
rg "cache|Cache" -t ts

Repository: ubiquity/ubq.fi-router

Length of output: 512


Add optional GitHub token support to avoid rate limiting issues.

Unauthenticated GitHub API calls are rate-limited to 60 requests/hour per IP. The 1-hour cache helps, but this function is also called from src/health-dashboard.ts (duplicate implementation), doubling request frequency. With multiple workers, cache bypasses, or both calls active simultaneously, 60 requests/hour becomes tight. Accept an optional GITHUB_TOKEN environment variable.

The per_page=100 without pagination is fine—the org has only 15 public repos.

Comment thread src/sitemap.ts
Comment on lines +54 to +56
return repos
.filter((r) => !r.name.startsWith(".") && !["command-config", "command-wallet", "command-query", "daemon-pricing", "daemon-merging", "daemon-disqualifier", "daemon-task-matcher", "daemon-planner", "daemon-xp", "daemon-spec-rewriter", "daemon-xp"].includes(r.name))
.map((r) => r.name);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Duplicate entry in exclusion list: "daemon-xp" appears twice.

Also, this exclusion list differs from health-dashboard.ts:78 which only excludes "command-config". This inconsistency means sitemap and health dashboard will advertise different plugin sets.

Proposed fix
-    .filter((r) => !r.name.startsWith(".") && !["command-config", "command-wallet", "command-query", "daemon-pricing", "daemon-merging", "daemon-disqualifier", "daemon-task-matcher", "daemon-planner", "daemon-xp", "daemon-spec-rewriter", "daemon-xp"].includes(r.name))
+    .filter((r) => !r.name.startsWith(".") && !["command-config", "command-wallet", "command-query", "daemon-pricing", "daemon-merging", "daemon-disqualifier", "daemon-task-matcher", "daemon-planner", "daemon-xp", "daemon-spec-rewriter"].includes(r.name))

Consider extracting this list to a shared constant used by both sitemap.ts and health-dashboard.ts.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return repos
.filter((r) => !r.name.startsWith(".") && !["command-config", "command-wallet", "command-query", "daemon-pricing", "daemon-merging", "daemon-disqualifier", "daemon-task-matcher", "daemon-planner", "daemon-xp", "daemon-spec-rewriter", "daemon-xp"].includes(r.name))
.map((r) => r.name);
return repos
.filter((r) => !r.name.startsWith(".") && !["command-config", "command-wallet", "command-query", "daemon-pricing", "daemon-merging", "daemon-disqualifier", "daemon-task-matcher", "daemon-planner", "daemon-xp", "daemon-spec-rewriter"].includes(r.name))
.map((r) => r.name);

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.

Health Dashboard

1 participant