Skip to content

feat: show install counts on system apps - #943

Merged
tavdog merged 2 commits into
tronbyt:mainfrom
ayushsoni1001:feat/install-counts
Oct 4, 2026
Merged

tavdog merged 2 commits into
tronbyt:mainfrom
ayushsoni1001:feat/install-counts

Conversation

@ayushsoni1001

@ayushsoni1001 ayushsoni1001 commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Each system app card on Add App now shows how many people have installed it: a download icon and the full number (1,234), with a dash for apps with no installs. Sort by gets a Most installs option. The Category and Sort by selects also get a padded chevron that follows the theme, replacing the native arrow that sat against the border.

Where the numbers come from

Counts come from Niblet Cloud's public, unauthenticated GET /v1/catalog/install-counts endpoint (app-install-counts.v1). It returns only {app_id: count} for community apps, which are the same Tidbyt community apps Tronbyt ships, so IDs match one-to-one. A count is the number of unique Niblet accounts that have ever installed the app. Tronbyt usage isn't counted, and nothing about this server or its users is sent.

  • Fetched at most once per 24 hours and cached in the settings table, so restarts reuse the last good counts.
  • Page views never make network requests.
  • Failures keep the last good counts and retry no sooner than hourly. The backoff is persisted, so restarts can't hammer the endpoint.
  • HTTPS only (plain HTTP is allowed only on loopback, for development), no redirects, 10 s timeout, 1 MiB response limit, and payload validation.
  • Disable it with NIBLET_CLOUD_URL="". It's documented in the README.

Changes

  • internal/server/install_counts.go: fetch, validate, cache and schedule.
  • handlers_app.go: attaches cached counts to system apps. Custom apps never get a count.
  • app_list_grid.html / addapp-simple.css: the count row (Heroicons arrow-down-tray, MIT), the data-installs attribute, and the select chevron.
  • manager.js: the installs sort (descending, ties by name, uncounted apps last).
  • funcmap.go: thousands and installCount template helpers.
  • i18n: Number of installs and Most installs (en, de).

Testing

  • go test ./... passes, including new tests for daily caching, restart reuse, hourly backoff, cache replacement, rejecting remote plain HTTP, number formatting, and no count on custom apps.
  • golangci-lint run (v2) reports 0 issues. djlint@1.40.8 web/templates --profile=golang --check is clean.
  • Checked by hand against production Niblet Cloud: 1,061 system apps show counts, 513 of them as a dash. Dark theme.

Screenshots

Most installs sort with full counts:

Most installs sort

Apps with no installs show a dash:

Zero installs shown as a dash

Summary by CodeRabbit

  • New Features
    • App cards can show install counts, and the app list can be sorted by popularity (most installs).
    • Install counts refresh daily. Set NIBLET_CLOUD_URL to choose the data source, or leave it empty to disable syncing; the default source is https://cloud.heyniblet.com.
  • Style
    • Updated the category and sort dropdowns with theme-colored styling.
    • Improved the display of install counts with aligned numerals and accessible labels.

Show how many people have installed each system app, with a download
icon and full numbers (a dash for zero), and add a "Most installs" sort.
Also give the Category and Sort by selects a padded, theme-aware chevron.

Counts come from Niblet Cloud's public app-install-counts.v1 endpoint,
fetched at most once a day and cached in settings so page views never
make network requests. Failures keep the last good counts and retry
hourly. NIBLET_CLOUD_URL='' disables the feature.
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: cfa03301-bde8-49db-9b67-f2551dac101b
📥 Commits

Reviewing files that changed from the base of the PR and between a0465f7 and ead38f2.

📒 Files selected for processing (3)
  • web/i18n/de.json
  • web/i18n/en.json
  • web/templates/partials/app_list_grid.html
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/i18n/de.json
  • web/i18n/en.json
  • web/templates/partials/app_list_grid.html

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.


📝 Walkthrough

Walkthrough

The server can retrieve and cache install counts from Niblet Cloud. The add-app page displays available counts and supports sorting apps by install count.

Changes

Niblet app install counts

Layer / File(s) Summary
Configure the count source
internal/config/config.go, internal/apps/apps.go, internal/config/config_test.go, README.md
Settings add NIBLET_CLOUD_URL, preserve an explicitly empty value, and document the option. AppMetadata gains an optional install count.
Retrieve, validate, and cache counts
internal/server/install_counts.go, internal/server/server.go, internal/server/handlers_app.go, internal/server/install_counts_test.go
The server retrieves and validates counts, persists successful results and sync times, and assigns cached counts to system apps. Tests cover caching, refresh, failure backoff, and URL validation.
Display and sort app counts
internal/server/funcmap.go, web/templates/partials/app_list_grid.html, web/static/js/manager.js, web/static/css/addapp-simple.css, web/i18n/*.json, internal/server/install_counts_test.go
App cards display formatted, localized counts when available. The sort menu adds install-count sorting. Styling and translations support the count display and dropdowns.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Server
  participant NibletCloud
  participant PersistentStore
  participant AddAppHandler
  participant AppTemplate
  Server->>NibletCloud: Request install counts
  NibletCloud-->>Server: Return count response
  Server->>PersistentStore: Save validated counts and next sync time
  AddAppHandler->>Server: Read cached counts
  Server-->>AddAppHandler: Return counts for system apps
  AddAppHandler->>AppTemplate: Render app metadata
  AppTemplate-->>AddAppHandler: Display count when available
Loading

Merge Risk: ⚪ Minimal · up to ead38

The selected changes add localized install-count labels and template display support. No concrete merge-blocking risk was identified.

Security Architecture Review

Security architecture risk: 🔵 Low · up to a0465

The integration is isolated from page requests, validates incoming counts, and preserves existing app-listing access controls. Its demonstrated influence is limited to displayed counts and sorting. Deployment control of the destination and coordination across servers sharing a database remain unconfirmed.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — A compromised configured provider can influence cached counts and system-app ordering across users of that Server. The traced consumer path does not use provider strings as executable content, create apps, or select installation targets. Requests contain no user or device payload and no application Authorization header.

Trust Boundaries and Controls

  • observed — The fetcher rejects URL credentials, queries, fragments, and missing hosts. It requires HTTPS except for HTTP to named loopback hosts, disables redirects, applies a 10-second timeout, and limits response reads to 1 MiB plus an overflow-detection byte. Payload validation precedes persistence of newly fetched counts.

Resilience and Maintainability Implications

  • inferred — Persisting retry state before the request limits repeated egress after ordinary failures and restarts. Mutex protection prevents concurrent cache-map publication and handler reads. It does not coordinate refresh ownership across processes: competing owners could both observe a due deadline and overwrite counts or schedules. Shared-database deployment applicability remains unknown.

Hardening Proposals

  • proposed — Keep destination configuration under deployment-operator control. If multiple servers share the settings database, establish one refresh owner or an atomic database-backed claim to preserve the intended request-rate boundary.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 9 files. (3 skipped: … 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 clearly describes the main change: showing install counts on system app cards.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 9 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@tavdog
tavdog merged commit d71c57a into tronbyt:main Oct 4, 2026
7 checks passed
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