Skip to content

feat(logs): add admin aggregated logs endpoint#10

Open
whoabuddy wants to merge 15 commits into
whoabuddy:mainfrom
aibtcdev:feat/admin-aggregated-logs
Open

feat(logs): add admin aggregated logs endpoint#10
whoabuddy wants to merge 15 commits into
whoabuddy:mainfrom
aibtcdev:feat/admin-aggregated-logs

Conversation

@whoabuddy
Copy link
Copy Markdown
Owner

Summary

  • Admin GET /logs without X-App-ID header now returns logs from all registered apps, with app_id on each entry
  • Logs are fetched from all app DOs in parallel, sorted by timestamp DESC, and truncated to the global limit
  • 11 new integration tests covering aggregation, filters (level, since/until, search, context, request_id), limit cap, auth edge cases, and single-app fallback
  • Code cleanup: simplified limit parsing, removed dead conditional, removed unused test variables

Test plan

  • All 35 integration tests pass (npm test)
  • Existing single-app GET /logs behavior unchanged
  • Admin with X-App-ID still returns single-app logs
  • Admin without X-App-ID returns aggregated logs with app_id field
  • Manual verification against production after deploy

🤖 Generated with Claude Code

whoabuddy and others added 15 commits January 12, 2026 15:58
- Add staging/production environments in wrangler.jsonc
- Add fork notice and maintenance docs to CLAUDE.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove logs.wbd.host route from top-level (only used for local dev)
- Use staging KV namespace ID for local development
- The previous ID was from whoabuddy account, not aibtcdev

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Previously DELETE /apps/:id required the app's own API key. Now admins
can also delete apps using the X-Admin-Key header, consistent with
other admin operations.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: allow admin key to delete apps

Previously DELETE /apps/:id required the app's own API key. Now admins
can also delete apps using the X-Admin-Key header, consistent with
other admin operations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(dashboard): overhaul with unified view and advanced filtering (#3)

- Add unified overview page showing all apps with error trends and health status
- Add enhanced app detail page with 7-day stats chart (Chart.js)
- Add advanced filtering: date range picker, request ID, context field filters
- Add server-side search and context filtering to Durable Object
- Modularize dashboard into separate files for maintainability
- Integrate Alpine.js for declarative client-side state management
- Add SVG sparklines and trend indicators for quick status overview

New file structure:
  src/dashboard/
    index.ts - Main router
    auth.ts - Session management
    styles.ts - Shared styles
    types.ts - Dashboard types
    pages/ - Login, overview, app-detail
    api/ - Overview aggregation endpoint
    components/ - Layout, charts utilities

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove deprecated dashboard.ts

Replaced by modular src/dashboard/ directory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…tilities (#4) (#6)

* refactor: remove dead code and extract shared utilities

- Delete unused src/services/stats.ts (legacy KV-based stats, replaced by DO SQLite)
- Extract getAppDO and countByLevel to src/utils.ts
- countByLevel now uses Map for O(n) instead of O(n²) array scanning



* refactor: consolidate duplicated code and remove unused functions

Dashboard:
- Extract getAppList, getAppName, getHealthUrls to shared helpers.ts
- Fix type safety: replace `any` with proper Context type

Result utilities:
- Add wrapError() for consistent error handling in catch blocks
- Remove unused exports: isOk, isErr, getErrorStatus, ErrorStatusMap

Registry service:
- Use wrapError() instead of inline error handling (6 occurrences)
- Remove unused validateApiKey and regenerateApiKey functions



* chore: remove unused dashboard code

- Remove unused loadingSpinner component from layout.ts
- Remove unused appOptions variable from header function
- Remove unused types: SavedFilter, FilterState, HealthSummary
- Remove unused HealthCheck import from types.ts



---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add GitHub Actions workflow for release-please
- Configure extra-files to update version in src/index.ts
- Sync package.json version to 0.4.0 to match API response
- Add x-release-please-version marker for automatic version updates

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat(dashboard): apply AIBTC brand guidelines to dashboard UI

Add AIBTC brand identity across all dashboard pages: logo, favicon,
Roc Grotesk font, brand orange (#FF4F03) accent color, dark gradient
background with pattern overlay, and card glow/hover effects matching
the x402-api and x402-sponsor-relay dashboards.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* use css var over direct def

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add safari explicit

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix(dashboard): address PR review feedback

- Use event delegation for card glow mouse tracking instead of
  per-element listeners to prevent memory leaks with dynamic content
- Scope focus ring to focus-visible with opt-out classes (log-level,
  icon-button) so semantic elements keep default focus styling
- Broaden text-blue-400 override to all elements except .log-level
  so non-anchor brand links also get orange accent
- Replace hardcoded color values in Tailwind overrides with CSS
  variables (--bg-table-header, --bg-input, --bg-input-hover, etc.)
- Add --accent-hover CSS variable, replace inline onmouseover/onmouseout
  on login button with .btn-accent CSS class
- Add z-index layering to card glow (z-index: 0 on ::after, z-index: 1
  on .brand-card > * children) so content renders above glow effect
- Replace hardcoded #111 dropdown background with var(--bg-card)
- Remove unused brand color object from styles.ts (CSS variables used)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* feat(dashboard): add BrandConfig type and getBrandConfig env reader

Phase 1 of configurable branding quest. Defines BrandConfig interface
with all brand values, AIBTC defaults, and getBrandConfig() that reads
BRAND_* environment variables with automatic accent color derivation
and CDN URL composition.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(dashboard): generate brand CSS from BrandConfig

Phase 2 of configurable branding quest. Replaces static brandCss with
buildBrandCss(config) that generates CSS from BrandConfig values.
Updates htmlDocument and header to accept brand config via LayoutOptions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(dashboard): thread BrandConfig through router and pages

Phase 3 of configurable branding quest. Resolves brand config from
env vars once per request via middleware. All page functions accept
BrandConfig and forward it to layout components.

Changes:
- Add middleware to resolve brand config from env on each request
- Update Hono generic to include Variables with brand config
- Thread brand config through all page functions (login, overview, app-detail)
- Update page functions to pass brand to layout components
- Type helper functions to accept contexts with Variables

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(dashboard): add brand env vars to wrangler and brand config tests

Add BRAND_NAME, BRAND_ACCENT, and BRAND_CDN_URL environment variables
to staging and production wrangler.jsonc environments with AIBTC
defaults. Add comprehensive tests for getBrandConfig() covering
defaults, env overrides, CDN URL derivation, accent color derivation,
and individual URL overrides.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(dashboard): address PR review feedback on brand config

- Validate hex colors with regex, fallback to default on invalid input
- Remove unused accentRgb variable
- Add BrandEnv type to eliminate unsafe cast in router middleware
- Escape brand values in HTML attributes (defense-in-depth)
- Add tests for invalid hex inputs and accent fallback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The health-urls endpoint only accepted per-app API keys, preventing
admin users from configuring health check URLs. Switch to
requireApiKeyOrAdmin middleware to match the pattern used by other
app management endpoints.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
When admin calls GET /logs without X-App-ID, iterates all registered
apps from KV, queries each app's DO in parallel, and returns combined
log entries with app_id field on each entry.

Preserves existing single-app behavior when X-App-ID header is provided
(for both API key and admin auth paths).

Per-app limit is calculated as max(10, ceil(globalLimit / numApps)) to
avoid overloading any single DO. Results are merged, sorted by timestamp
DESC, and truncated to the global limit.

Also exports AggregatedLogEntry type (LogEntry + app_id) from types.ts
for consumers that need to type-check aggregated responses.

Co-Authored-By: Claude <noreply@anthropic.com>
Tests cover:
- GET /logs with admin key and no X-App-ID returns combined entries
  from all registered apps, each with an app_id field
- Level filter (e.g., ?level=ERROR) is forwarded to each app's DO
- Global limit (e.g., ?limit=2) caps the total results returned
- GET /logs with admin key AND X-App-ID falls through to single-app
  path, returning raw DO response without app_id field

Adds beforeAll setup that creates two apps (agg-app-1, agg-app-2)
and writes distinct log levels to each so assertions can verify
cross-app aggregation.

Co-Authored-By: Claude <noreply@anthropic.com>
…ndpoint

Extend the "Admin aggregated logs" integration test suite with 7 new tests
that cover scenarios not already tested in Phase 1:

Auth edge cases:
- No auth (missing both X-Admin-Key and X-App-ID) returns 400
- Invalid admin key returns 401

Aggregated filter scenarios (new nested describe with isolated app fixtures):
- since=far-future timestamp returns empty array (filter applied per-app)
- until=far-past timestamp returns empty array
- search filter returns matching entries across multiple apps
- context.* filter returns entries with matching JSON context field
- request_id filter returns exactly the one matching entry with correct app_id

Each test verifies that app_id field is present on aggregated results and
that filters are correctly propagated to per-app DO queries.

Co-Authored-By: Claude <noreply@anthropic.com>
…sed test variables

Replace verbose has/get/parseInt pattern with Number() + nullish fallback for
the globalLimit parameter. Remove dead-code queryString conditional since
perAppParams always contains at least the limit key. Remove unused
filterApiKey1/filterApiKey2 variables from test setup -- the filter tests
authenticate exclusively via admin key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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