Skip to content

Restore admin Event Log; drop warning-level to silence Browserless noise#469

Merged
bd73-com merged 9 commits into
mainfrom
claude/fix-admin-panel-menu-STG1d
Apr 24, 2026
Merged

Restore admin Event Log; drop warning-level to silence Browserless noise#469
bd73-com merged 9 commits into
mainfrom
claude/fix-admin-panel-menu-STG1d

Conversation

@bd73-com
Copy link
Copy Markdown
Owner

@bd73-com bd73-com commented Apr 24, 2026

Summary

Reverts #461 to bring back the admin Event Log feature (the "admin panel menu entry" that disappeared), then removes ErrorLogger.warning() entirely so the Browserless infra-noise that motivated #461 never reaches the Event Log in the first place. Former warning call sites now emit via console.warn (stdout only), while error- and info-level entries continue to flow through ErrorLogger into the admin UI.

The revert replays #461's CodeRabbit URL-redaction hardening on top, consolidates safeHostname into server/utils/urlUtils.ts, and picks up additional hardening identified during the review pipeline (stdout throttling, admin badge/filter parity, console-bypass redaction).

Changes

Event Log restored (revert of #461b36aecf)

  • /admin/errors page, /api/admin/error-logs* endpoints (count / list / batch-delete / restore / finalize)
  • DashboardNav badge for Power tier showing unresolved error+info count
  • ErrorLogger service, error_logs table, ensureErrorLogColumns bootstrap, partial unique dedup index
  • "Admin event log" bullet restored in Pricing.tsx and UpgradeDialog.tsx

Warning-level removed (3541155, c7bb327)

  • ErrorLogger.warning() method deleted; LogLevel = "error" | "info"
  • Every prior call site converted to console.warn([source] message, context) — stdout only, no DB write
  • Regression guards in logger.test.ts assert ErrorLogger.warning is undefined and info routes to console.log
  • 12 dead warning: vi.fn() mocks removed from route test files

Security hardening (9491536, efcd314)

  • url: monitor.urlhostname: safeHostname(monitor.url) at every log site on this branch (scraper / scheduler / email). Monitor URLs can carry https://user:pw@host or ?api_key=… secrets that the DSN/Bearer/provider-key regex list does not catch.
  • Dropped rawBrowserlessMsg from the site-specific Browserless console.warn (upstream errors can echo back the requested URL)
  • Narrowed API level allowlist + batch-delete Zod enum to ["error", "info"]; removed the "Warnings" dropdown option in AdminErrors.tsx
  • resendTracker direct level: "warning" inserts converted to ErrorLogger.info (preserves admin visibility + dedup upsert)
  • safeHostname consolidated into server/utils/urlUtils.ts; scraper.ts, scheduler.ts, email.ts, routes.ts, routes/extension.ts, routes/v1.ts all import from the shared helper

Skeptic-phase fixes (e48eb4a, 2b757b6)

  • browserlessTracker.ts:111 direct insert → ErrorLogger.info (would otherwise throw duplicate key on partial index after 6h cooldown, silently swallowing threshold alerts)
  • Transient-save console.warn no longer includes extractedValue / previousValue (user-scraped page content — can carry API tokens, PII, internal URLs). Non-transient ErrorLogger.error path keeps them because it runs sanitizeContext.
  • Outer-catch transient console.warn no longer interpolates raw error.message (undici/fetch errors echo full URL with credentials)
  • Count endpoint (/api/admin/error-logs/count) narrowed to inArray(level, ["error","info"]) so the badge matches what the filter dropdown can show
  • Module-level 60s throttle around Browserless-unavailable and circuit-breaker-open console.warn sites to prevent stdout flood during infra outages
  • safeHostname parse-failure fallback changed from literal "unknown""<invalid-url>" so triage can distinguish a malformed URL from a real host named unknown

How to test

  1. Admin Event Log visibility: Sign in as a Power-tier user. Confirm the nav shows Campaigns + Event Log (with unresolved-count badge). Navigate to /admin/errors — the filter dropdown should offer All levels / Errors / Info only (no "Warnings" option).
  2. Warning silencing: Trigger a Browserless infrastructure outage (mock: set BROWSERLESS_TOKEN=bad and run a monitor check). Observe stdout: expect [scraper] Browserless service unavailable … lines but at most one per 60s per replica. Confirm no new rows land in error_logs under source='scraper', level='warning'.
  3. URL redaction: Create a monitor with url = "https://example.com?api_key=SECRET123". Force a scheduled-check failure. Confirm error_logs.context shows hostname: "example.com" (not the raw URL) and that SECRET123 does not appear in Replit logs or in the Event Log UI.
  4. Resend threshold alerts: Mock Resend usage at 95%. Confirm a single level='info', source='resend', message='resend_alert_95' row lands in error_logs (not level='warning'); subsequent checks within 6h don't duplicate it.
  5. Admin batch-delete undo: Batch-delete a handful of error rows, click Undo within 5s. Confirm the rows are restored. Repeat without clicking Undo and confirm they're finalized after 5s.
  6. Regression tests: npm run check && npm run test — expect 2475 passing across 100 files.

Deploy runbook

Legacy level='warning' rows from pre-deprecation writes (especially source='resend', message='resend_alert_*') remain in the DB and render under the "All levels" filter with a yellow icon. They coexist with new level='info' rows for the same alert key (different partial-index dedup buckets). Optional one-shot cleanup to avoid apparent duplicate rows in the admin UI:

UPDATE error_logs SET resolved = true, resolved_at = NOW(), resolved_by = 'deploy-cleanup'
WHERE level = 'warning' AND resolved = false AND source IN ('resend', 'browserless');

Out-of-scope follow-ups

Filed during Phase 6 bug triage — not required for this PR:

Pipeline

/magicwand executed clean: 1 critical + 3 high + 3 medium + 2 low findings fixed in-branch (15 resolved issues across phases 2/3/4/5); 6 pre-existing issues filed as GitHub Issues. All 2475 tests pass; npm run check clean.


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added Admin Error Logs page for power-tier users to view, filter, and manage application errors with batch actions and undo functionality.
    • Added Event Log navigation button displaying unresolved error count badge for power users.
    • Implemented comprehensive error logging system capturing and tracking application errors across the platform.
  • Updates

    • Added "Admin event log" capability to the Power plan in pricing.

claude added 8 commits April 24, 2026 07:45
User intent on #461 was to silence the Browserless warning-noise flood
only; reverting #461 brought the Event Log back but also re-enabled
warning-level writes that produced the original noise.

This drops ErrorLogger.warning() entirely and rewrites every call site
to console.warn with the same `[source] message` prefix, so:
- Transient/noisy warnings still surface in server logs for ops triage
- The admin Event Log only shows error- and info-level events
- Dedup/aggregation still applies to the remaining error+info rows

Test mocks updated to spy on console.warn instead of ErrorLogger.warning.
Adds two assertions to logger.test.ts:
- ErrorLogger.warning is undefined (prevents accidental reintroduction)
- ErrorLogger.info routes only to console.log, never console.warn/.error
…API filter

Phase 2 security findings from /magicwand review:

H1/H2 (high): Replace `url: monitor.url` with `hostname: safeHostname(monitor.url)`
at every log site on the branch. Monitor URLs are user-supplied and can carry
basic-auth credentials (`https://user:pw@host`) or query-string secrets
(`?api_key=…`) that the ErrorLogger's sanitizeString regex list does not catch.
This re-applies PR #461's CodeRabbit hardening on top of the Event Log revert.

Sites patched:
- server/services/scraper.ts (5 call sites — error, console.warn, circuit-breaker)
- server/services/scheduler.ts (1 — scheduled-check failed)
- server/services/email.ts (1 — notification email failed)

Also drops `rawBrowserlessMsg` from the site-specific console.warn at
scraper.ts:1283 — upstream errors can echo back the requested URL with
credentials. classifiedReason already captures enough triage detail.
Updates the stale comment claiming logger sanitization applies.

M1 (medium): API `level` filter in GET /api/admin/error-logs still accepted
`"warning"` after commit 3541155 removed the warning-level write path. Narrows
the allowlist (and batch-delete Zod enum) to ["error", "info"] and removes the
"Warnings" dropdown option in AdminErrors.tsx.

safeHostname helper inlined in email.ts to avoid pulling the full
monitorValidation→storage→schema chain into email.test.ts's drizzle-orm mock.

All 2475 tests pass; npm run check clean.
Phase 3 architecture findings:

resendTracker: two direct `db.insert(errorLogs).values({level:"warning",...})`
sites bypassed ErrorLogger entirely — producing new warning rows despite the
branch's stated "no more warning writes" invariant. Threshold alerts (80/90/95%
Resend usage) are legitimately triage-worthy, so route them through
ErrorLogger.info instead (preserves admin visibility, sanitization, and
dedup-upsert). The in-DB 6-hour cooldown SELECT is retained for rate-limiting.

safeHostname consolidation: extracted the hostname helper to dependency-free
server/utils/urlUtils.ts. scraper.ts and email.ts dropped their local copies;
scheduler.ts and monitorValidation.ts updated to import from the new location
(monitorValidation re-exports for back-compat with any external callers).

Doc and test cleanup:
- shared/schema.ts:93 level comment now flags 'warning' as historical-only
- Removed twelve dead `warning: vi.fn()` mocks from route test files —
  they masked that the ErrorLogger API no longer exposes .warning()

All 2475 tests pass; npm run check clean.
…fixture

Phase 4 code-review minor findings:
- adminErrorsUtils.test.ts: swap stale `levelFilter: "warning"` fixture for
  "info" so the test mirrors real UI traffic (the dropdown no longer offers
  "warning" and the server Zod schema would 400 it).
- monitorValidation.ts: drop the `safeHostname` re-export; routes.ts,
  routes/extension.ts, routes/v1.ts now import directly from
  ../utils/urlUtils. extension.test.ts mock moved accordingly. One import
  path per symbol.

All 2475 tests pass; npm run check clean.
…atch

Phase 5 skeptic discoveries addressed:

browserlessTracker (H1/H2): Same direct-insert pattern resendTracker had —
raw db.insert(errorLogs).values({level:"info",...}) bypassed the dedup
upsert. After the 6h cooldown elapsed on an unresolved alert row, the
next INSERT would hit the partial unique index and throw, silently
swallowing the alert (no email sent during the threshold event).
Converted to ErrorLogger.info, matching the resendTracker fix.

scraper.ts transient console.warn paths (M1/M2): Two paths on this
branch use console.warn for transient failures. console.warn bypasses
ErrorLogger.sanitizeString/sanitizeContext entirely:
- Line ~1460 (save-failure retry): was logging extractedValue/previousValue
  which contain user-scraped page content — could include API tokens,
  PII, or internal URLs. Moved those fields to the non-transient
  ErrorLogger.error branch only; transient stdout log now contains
  only triage metadata. Test updated to assert the redaction.
- Line ~1590 (outer-catch transient): was interpolating raw error.message
  into the stdout line. undici/fetch errors echo the full requested URL
  (userinfo creds, ?api_key=... query). Now uses the classified
  logContext only; the ErrorLogger.error branch keeps the full message
  since it runs through the sanitizer.

Count endpoint / UI dropdown mismatch (M4): The nav badge's count
endpoint (GET /api/admin/error-logs/count) counted all unresolved rows
regardless of level, while the UI filter dropdown only offers
"error"/"info" after the warning-level narrowing. Legacy warning rows
inflated the badge but were unreachable from the filter — admin would
see "7 unresolved" then find 4 after clicking through. Narrowed count
query with inArray(level, ["error","info"]) so badge and filter agree.

Browserless-outage stdout flooding (M5): The
"Browserless service unavailable" console.warn used to dedup at DB
level via ErrorLogger.warning's partial-index upsert. After the
warning-level removal, every monitor check during an outage emits a
fresh stdout line — N monitors x M replicas. Added a module-level
per-process throttle (60s cooldown per message key) for the two
infra-wide warnings ("service unavailable" and "circuit breaker open").
Exported _resetInfraWarnThrottleForTests hook; scraper.test.ts
beforeEach resets it so tests observe the first emit deterministically.

Verified #462 fixes survived the revert: SCHEMA_NOT_READY gate,
activeCheckWaiters, claimed_at / claimDeliveryForRetry /
recoverStalledDeliveries all present.

Deferred to Phase 6 bug report (pre-existing / not regressions):
- safeHostname "unknown" fallback ambiguity
- errorLogs.level has no DB CHECK constraint
- Dual warning/info rows during deploy (operator migration note for
  PR body: legacy resend_alert_* warning rows should be resolved or
  deleted at deploy to avoid apparent duplicate alerts)

All 2475 tests pass; npm run check clean.
… failure

Phase 6 bug-reporter reclassified the Phase-5 "unknown" fallback as in-scope
(urlUtils.ts is new code on this branch). A monitor URL of http://unknown/path
would collide with the fallback, making "unknown" ambiguous in triage.

Swap to `<invalid-url>` — angle brackets are not valid in a real hostname, so
the sentinel is guaranteed distinguishable from parsed hostnames.
@bd73-com bd73-com added the fix label Apr 24, 2026 — with Claude
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

Warning

Rate limit exceeded

@bd73-com has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 45 minutes and 56 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 45 minutes and 56 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c634cb11-79fe-4699-a7b5-207113d248db

📥 Commits

Reviewing files that changed from the base of the PR and between 2b757b6 and 5a30034.

📒 Files selected for processing (1)
  • server/utils/urlUtils.test.ts
📝 Walkthrough

Walkthrough

This PR introduces a centralized error logging system with database persistence and a corresponding admin UI. It adds an ErrorLogger service for structured error/info logging with automatic deduplication, creates new error-log API endpoints restricted to power-tier users, and integrates logging across email, scraper, scheduler, and other services. The admin errors page enables power users to view, filter, and manage unresolved logs.

Changes

Cohort / File(s) Summary
Client Admin Errors Feature
client/src/pages/AdminErrors.tsx, client/src/pages/AdminErrors.test.ts, client/src/pages/adminErrorsUtils.ts, client/src/pages/adminErrorsUtils.test.ts
New admin page for displaying, filtering, and managing error logs with expandable details, soft-delete with undo, and batch operations; utility functions handle batch-delete payload construction with filter/exclusion logic.
Client Routing & Navigation
client/src/App.tsx, client/src/components/DashboardNav.tsx
Add authenticated /admin/errors route and "Event Log" nav button (power users only) with unresolved error count badge refreshing every 60 seconds; update "Campaigns" button label visibility.
Client Pricing & Upgrade UI
client/src/components/UpgradeDialog.tsx, client/src/pages/Pricing.tsx
Add "Admin event log" capability to power-tier feature lists in upgrade dialog and pricing page.
Server Error Logger Service
server/services/logger.ts, server/services/logger.test.ts
New ErrorLogger class providing structured error/info logging to database with atomic upsert deduplication, sensitive-data redaction, and graceful DB-failure handling; includes stack trace sanitization and error-type derivation.
Server Admin Error Log Routes
server/routes.ts, server/routes.deleteErrorLog.test.ts, server/routes.errorLogCount.test.ts
New admin API endpoints: GET /api/admin/error-logs/count, GET /api/admin/error-logs, DELETE /api/admin/error-logs/:id, POST /api/admin/error-logs/batch-delete, restore, and finalize; all restricted to power-tier authenticated users with ownership-based authorization; includes background cleanup of soft-deleted rows older than 5 minutes.
Server Database Schema & Migration
shared/schema.ts, server/services/ensureTables.ts, server/services/ensureTables.test.ts
New error_logs table with deduplication fields (occurrence_count, first_occurrence) and resolution lifecycle; adds transactional ensureErrorLogColumns() that creates/validates a unique partial index on (level, source, message) for unresolved entries; replaces legacy drop logic.
Server Route-Specific Logger Mocks
server/routes.campaignDashboard.test.ts, server/routes.campaignRecover.test.ts, server/routes.campaignSendTest.test.ts, server/routes.conditions.test.ts, server/routes.deleteFailedCampaign.test.ts, server/routes.migration.test.ts, server/routes.notificationChannels.test.ts, server/routes.notificationPreferences.test.ts, server/routes.tags.test.ts, server/routes/extension.test.ts, server/routes/v1.integration.test.ts, server/routes/zapier.test.ts, server/webhookHandlers.test.ts
Test files add mocks for ErrorLogger (and related DB/logger dependencies) to prevent real logging during route/integration test execution.
Server Service Logger Integration
server/services/automatedCampaigns.ts, server/services/automatedCampaigns.test.ts, server/services/browserlessTracker.ts, server/services/campaignEmail.ts, server/services/campaignEmail.test.ts, server/services/email.ts, server/services/email.test.ts, server/services/notification.ts, server/services/notification.test.ts, server/services/resendTracker.ts, server/services/resendWebhook.ts, server/services/resendWebhook.test.ts, server/services/scheduler.ts, server/services/scheduler.test.ts, server/services/scraper.ts, server/services/scraper.test.ts
Integrate ErrorLogger across services: replace console.error/log with structured logging; add transient vs non-transient error classification in scheduler; switch threshold alerts (Browserless, Resend) from in-memory cooldown to error_logs table queries; add sensitive-data redaction and context fields; extend tests to verify logging behavior and error handling.
Server Error Handling & Startup
server/index.ts, server/webhookHandlers.ts
Update global error middleware and webhook handlers to use ErrorLogger.error instead of console.error; add structured logging with route/client IP context for signature failures; add graceful shutdown via stopRouteTimers(); make welcome-campaign bootstrap errors non-fatal with logging.
Server Import Refactoring
server/utils/urlUtils.ts, server/services/monitorValidation.ts, server/routes/extension.ts, server/routes/v1.ts, server/services/scheduler.ts
Extract safeHostname utility to new server/utils/urlUtils.ts and remove from monitorValidation.ts; update all import paths in extension, v1, and scheduler routes.
Shared Schema & Validation
shared/routes.ts, shared/routes.test.ts
Add ERROR_LOG_SOURCES enum and errorLogSourceSchema Zod validator for allowed error-log source values (e.g., "browserless", "resend").
Database Invariants & Coverage
server/partial-index-invariants.test.ts
Extend invariant tests to validate alignment between error_logs_unresolved_dedup_idx partial index definition in schema and ErrorLogger's upsert conflict target; add regex extractors and byte-identical WHERE clause matching.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Rationale: The PR spans 60+ files with dense logic across multiple domains (client UI, server routes, database schema, services, and tests). While much of the integration work follows repetitive patterns (mocking logger in tests, adding ErrorLogger calls to existing services), the core components demand careful scrutiny: the AdminErrors page logic (filtering, selection, batch operations with undo), the ErrorLogger deduplication and redaction mechanisms, the admin API authorization and soft-delete semantics, and the transactional ensureErrorLogColumns() migration. The sensitive-data redaction paths require verification, and the interplay between soft-delete/restore/finalize operations and the background cleanup task needs attention to ensure data consistency.

Possibly related PRs

Suggested labels

fix

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.49% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: restoring the admin Event Log feature and silencing Browserless noise via warning-level removal. It is specific and directly summarizes the two primary objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-admin-panel-menu-STG1d

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.

New-code coverage gap noted during review: server/utils/urlUtils.ts was
added on this branch but had no direct test. Asserts:
- well-formed URL → hostname
- userinfo credentials stripped from output
- query-string secrets not reflected in hostname
- parse failures return the `<invalid-url>` sentinel (guard against
  regression of the previous "unknown" fallback)
- real host named `unknown` round-trips cleanly (collision guard)
@bd73-com bd73-com merged commit 90dd3fd into main Apr 24, 2026
1 check passed
@github-actions github-actions Bot deleted the claude/fix-admin-panel-menu-STG1d branch April 24, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants