Skip to content

feat(web): record service ping history and add usage report download#1348

Merged
brendan-kellam merged 5 commits into
mainfrom
brendan/service-ping-history
Jun 18, 2026
Merged

feat(web): record service ping history and add usage report download#1348
brendan-kellam merged 5 commits into
mainfrom
brendan/service-ping-history

Conversation

@brendan-kellam

@brendan-kellam brendan-kellam commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Overview

Offline / air-gapped deployments can't report usage to Lighthouse automatically. This change records each service ping locally so those deployments can download their usage history and send it to us out-of-band.

Changes

  • DB: New ServicePingEvent table (id, payload JSON, createdAt) + migration.
  • Recording: syncWithLighthouse now persists each ping (via recordServicePingInDB) before sending it to Lighthouse, so the history is captured even when the outbound ping fails (as it always will on air-gapped instances). The activation code is stripped before persisting, and recording is best-effort (wrapped in try/catch + logging) so a DB error never blocks the real ping.
  • Export UI: A "Download usage report" button on the offline license settings card (Settings → License), backed by an OWNER-gated server action that returns the recorded events. Downloads as <date>-usage-history.json.
  • Copy: The offline license card explains that the instance doesn't report usage automatically and that usage must be manually sent to ar@sourcebot.dev.
image

Notes

  • The export button only renders for offline licenses, but recording runs on all deployments (one tiny row/day, negligible).
  • No retention/pruning yet.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Offline license settings page now includes a "Download usage report" button to export usage data as JSON files.
    • Service ping history is now recorded locally for offline deployments to support usage tracking.
  • Refactor

    • Improved license validation helper functions for better code organization.

Record each service ping in a new ServicePingEvent table (activation code
stripped) so offline deployments, which can't report usage to Lighthouse
automatically, can download their usage history and email it to us.

Adds a "Download usage report" button to the offline license settings card
that exports the recorded pings as a JSON file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a687f59b-8ce6-4388-9572-17e7790ee58f

📥 Commits

Reviewing files that changed from the base of the PR and between 5c5271e and e8bbbaa.

📒 Files selected for processing (7)
  • packages/db/prisma/migrations/20260618210032_add_service_ping_event_table/migration.sql
  • packages/db/prisma/schema.prisma
  • packages/shared/src/entitlements.ts
  • packages/shared/src/index.server.ts
  • packages/web/src/app/(app)/settings/license/actions.ts
  • packages/web/src/app/(app)/settings/license/downloadServicePingHistoryButton.tsx
  • packages/web/src/features/billing/servicePing.ts

Walkthrough

Adds a ServicePingEvent database table to persist sanitized service ping payloads per organization. syncWithLighthouse now records each ping to the DB before sending it to Lighthouse. A new server action getServicePingHistory retrieves the history, and a DownloadServicePingHistoryButton in the offline license card lets owners export it as JSON.

Changes

Service Ping Event Recording and Download

Layer / File(s) Summary
ServicePingEvent DB schema and migration
packages/db/prisma/migrations/.../migration.sql, packages/db/prisma/schema.prisma
Migration creates the ServicePingEvent table with id, payload JSONB, createdAt, and a cascading FK to Org. Prisma schema adds the model and back-reference field on Org.
Best-effort ping recording in syncWithLighthouse
packages/shared/src/entitlements.ts, packages/shared/src/index.server.ts, packages/web/src/features/billing/servicePing.ts
isValidOfflineLicenseActive and isValidOnlineLicenseActive are extracted as exported helpers. recordServicePingInDB strips activationCode and writes the sanitized payload to servicePingEvent, swallowing errors. syncWithLighthouse awaits this call before the offline-license early return.
Server action, download button, and offline card integration
packages/web/src/app/(app)/settings/license/actions.ts, packages/web/src/app/(app)/settings/license/downloadServicePingHistoryButton.tsx, packages/web/src/app/(app)/settings/license/offlineLicenseCard.tsx
getServicePingHistory server action queries servicePingEvent ordered by createdAt and maps rows to ServicePingHistoryEntry. DownloadServicePingHistoryButton calls the action, handles error/empty toasts, and triggers a JSON blob download. OfflineLicenseCard gains a new bordered footer section rendering the button alongside a manual-reporting notice.
Changelog
CHANGELOG.md
Adds an unreleased entry describing service ping history recording and the new download button.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(173, 216, 230, 0.5)
    note over syncWithLighthouse,servicePingEvent: Every ping cycle
    syncWithLighthouse->>recordServicePingInDB: await recordServicePingInDB(orgId, payload)
    recordServicePingInDB->>recordServicePingInDB: strip activationCode
    recordServicePingInDB->>servicePingEvent: prisma.servicePingEvent.create()
    servicePingEvent-->>recordServicePingInDB: saved (errors caught+logged)
    recordServicePingInDB-->>syncWithLighthouse: returns
    syncWithLighthouse->>LighthouseClient: ping(payload) or skip if offline
  end
  rect rgba(144, 238, 144, 0.5)
    note over Owner,servicePingEvent: On-demand export
    Owner->>DownloadServicePingHistoryButton: click "Download usage report"
    DownloadServicePingHistoryButton->>getServicePingHistory: getServicePingHistory()
    getServicePingHistory->>servicePingEvent: findMany(orgId, orderBy: createdAt asc)
    servicePingEvent-->>getServicePingHistory: rows
    getServicePingHistory-->>DownloadServicePingHistoryButton: ServicePingHistoryEntry[]
    DownloadServicePingHistoryButton->>Owner: JSON blob download
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • sourcebot-dev/sourcebot#1296: Modifies syncWithLighthouse in the same servicePing.ts file to extend the ping payload, directly adjacent to where this PR inserts the recordServicePingInDB call.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: recording service ping history and adding a usage report download button for offline deployments.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch brendan/service-ping-history

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.

brendan-kellam and others added 2 commits June 18, 2026 11:51
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/web/src/app/(app)/settings/license/actions.ts (1)

19-21: ⚖️ Poor tradeoff

Consider pagination or limits for large datasets.

This query fetches all service ping events without pagination. For deployments running for years with daily pings, this could return thousands of records. While offline deployments may genuinely need all history, consider whether:

  1. A take/skip pagination strategy would be useful
  2. A date range filter would help users export specific periods
  3. A reasonable upper limit (e.g., last 1000 events) should be enforced
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/app/`(app)/settings/license/actions.ts around lines 19 - 21,
The query in the servicePingEvent.findMany() call fetches all records without
any limit or pagination, which could cause performance issues with large
datasets over time. Add a take parameter to limit the number of records returned
(e.g., last 1000 events) as a reasonable upper bound, and optionally implement
pagination with take and skip parameters or add a where clause with a date range
filter to allow users to query specific time periods instead of always fetching
the complete history.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/db/prisma/migrations/20260618182127_add_service_ping_event_table/migration.sql`:
- Around line 1-8: In the migration file creating the ServicePingEvent table,
add a database index on the createdAt column to optimize the ordered queries
performed by the getServicePingHistory server action. The createdAt column is
used for sorting results in ascending order but currently lacks an index, which
will cause full table scans as the table grows. Add a CREATE INDEX statement
after the table creation to create an index on the createdAt column of the
ServicePingEvent table to ensure queries remain performant as data accumulates.

In `@packages/db/prisma/schema.prisma`:
- Around line 361-365: The ServicePingEvent model lacks an orgId field which
breaks multi-tenant isolation and allows any OWNER to access all organizations'
service ping events. Add an orgId field to the ServicePingEvent model in
schema.prisma as a String required field with a foreign key relation to the
Organization model. Then update the getServicePingHistory function in actions.ts
to filter ServicePingEvent records by the requesting organization's orgId
instead of fetching all records. Finally, update the recordServicePingInDB
function in servicePing.ts to capture and include the orgId when creating new
ServicePingEvent records.

In `@packages/web/src/app/`(app)/settings/license/actions.ts:
- Around line 19-21: The servicePingEvent.findMany() query is missing an orgId
filter, which creates a security vulnerability allowing cross-organization data
access. Add a where clause to the findMany call that filters servicePingEvent
records by orgId using role.orgId to ensure only the current organization's
service ping events are retrieved. This prevents any OWNER from accessing
service ping history from other organizations.

---

Nitpick comments:
In `@packages/web/src/app/`(app)/settings/license/actions.ts:
- Around line 19-21: The query in the servicePingEvent.findMany() call fetches
all records without any limit or pagination, which could cause performance
issues with large datasets over time. Add a take parameter to limit the number
of records returned (e.g., last 1000 events) as a reasonable upper bound, and
optionally implement pagination with take and skip parameters or add a where
clause with a date range filter to allow users to query specific time periods
instead of always fetching the complete history.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 29aa261b-7964-47ab-b600-895f44389483

📥 Commits

Reviewing files that changed from the base of the PR and between 9320065 and 5c5271e.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • packages/db/prisma/migrations/20260618182127_add_service_ping_event_table/migration.sql
  • packages/db/prisma/schema.prisma
  • packages/web/src/app/(app)/settings/license/actions.ts
  • packages/web/src/app/(app)/settings/license/downloadServicePingHistoryButton.tsx
  • packages/web/src/app/(app)/settings/license/offlineLicenseCard.tsx
  • packages/web/src/features/billing/servicePing.ts

Comment thread packages/db/prisma/schema.prisma
Comment thread packages/web/src/app/(app)/settings/license/actions.ts
Comment thread packages/web/src/features/billing/servicePing.ts
msukkari
msukkari previously approved these changes Jun 18, 2026
@brendan-kellam brendan-kellam merged commit f0bb90b into main Jun 18, 2026
8 of 9 checks passed
@brendan-kellam brendan-kellam deleted the brendan/service-ping-history branch June 18, 2026 21:28
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.

2 participants