Skip to content

feat: add user count API endpoint and refactor stats fetching logic:#241

Merged
yashdev9274 merged 1 commit into
mainfrom
supercode-cli
Jul 27, 2026
Merged

feat: add user count API endpoint and refactor stats fetching logic:#241
yashdev9274 merged 1 commit into
mainfrom
supercode-cli

Conversation

@yashdev9274

@yashdev9274 yashdev9274 commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Description

  • Introduced a new API endpoint to count users in the database.
  • Updated the stats fetching logic to use the new endpoint, improving error handling with a fallback mechanism.
  • Enhanced the overall structure of the getStats function for better readability and maintainability.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactor (no functional changes)

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

  • bun test passes
  • bun run typecheck passes
  • bun run lint passes (if applicable)

Checklist:

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works

Summary by CodeRabbit

  • New Features

    • Added an API endpoint that provides the total user count.
    • Added resilient statistics retrieval for user counts, downloads, and GitHub stars.
  • Bug Fixes

    • Statistics now gracefully fall back when external data requests fail.
    • Added periodic caching to improve statistics loading reliability and performance.

- Introduced a new API endpoint to count users in the database.
- Updated the stats fetching logic to use the new endpoint, improving error handling with a fallback mechanism.
- Enhanced the overall structure of the getStats function for better readability and maintainability.
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
supercli Ready Ready Preview, Comment Jul 27, 2026 6:45am
supercli-client Ready Ready Preview, Comment Jul 27, 2026 6:45am
supercli-docs Ready Ready Preview, Comment Jul 27, 2026 6:45am

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

A terminal server endpoint now exposes the database user count. The web stats action retrieves users, npm downloads, and GitHub stars through cached HTTP requests with fallback values.

Changes

Stats API migration

Layer / File(s) Summary
User count endpoint
apps/supercode-cli/server/src/index.ts
Adds GET /api/data/users/count, returning the Prisma user count or a 500 JSON error.
Stats fetch integration
apps/web/modules/stats/actions/index.ts
Adds safeFetchJson with hourly revalidation and updates getStats to retrieve all statistics through HTTP requests with nullish-coalescing fallbacks.

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

Sequence Diagram(s)

sequenceDiagram
  participant getStats
  participant TerminalServer
  participant Prisma
  participant NpmAPI
  participant GitHubAPI
  getStats->>TerminalServer: Fetch user count
  TerminalServer->>Prisma: Count users
  Prisma-->>TerminalServer: Return count
  TerminalServer-->>getStats: Return user count JSON
  getStats->>NpmAPI: Fetch downloads
  NpmAPI-->>getStats: Return downloads
  getStats->>GitHubAPI: Fetch stargazers
  GitHubAPI-->>getStats: Return stars
Loading

Possibly related PRs

Poem

I’m a bunny counting stars,
Fetching numbers near and far.
Prisma hops behind the door,
Safe fallbacks guard the floor.
Fresh stats bloom for all to see!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: a user count API endpoint and a stats fetching refactor.
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.
✨ 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 supercode-cli

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@yashdev9274
yashdev9274 merged commit be7b016 into main Jul 27, 2026
4 of 8 checks passed

@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: 4

🤖 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 `@apps/supercode-cli/server/src/index.ts`:
- Around line 125-133: Update the user-count route around the app.get handler to
use the existing Bun.serve() routing structure instead of Express, preserving
its Prisma user count response and 500-error behavior. Integrate the route into
the server’s existing routes while retaining WebSocket support, or use the
repository’s established exemption mechanism if migration is not possible.

In `@apps/web/modules/stats/actions/index.ts`:
- Around line 13-15: Update getStats to resolve the terminal-server URL using
the same resolver and fallback chain as the analytics route, rather than
defaulting directly to http://localhost:3004. Reuse the existing shared
URL-resolution logic so the fallback aligns with the terminal server’s
PORT=10000 default and requests the correct users endpoint.
- Around line 3-10: Update safeFetchJson to return fallback when the fetch
response is not OK before parsing JSON. In getStats, validate each fetched
payload with the existing or appropriate Zod schemas before performing fallback
arithmetic, so invalid fields such as string counts use the documented fallback
instead of being coerced.
- Around line 3-5: Update the stats page’s caching configuration so
safeFetchJson’s revalidate: 3600 can establish hourly caching: remove the
force-dynamic export from the stats page when static generation is supported, or
move the cached getStats/safeFetchJson fetch behind a route or Server Component
boundary that is not forced dynamic.
🪄 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 Plus

Run ID: f9fdd136-9587-4c37-86bf-877321ff8189

📥 Commits

Reviewing files that changed from the base of the PR and between ada1f9a and 24d7501.

📒 Files selected for processing (2)
  • apps/supercode-cli/server/src/index.ts
  • apps/web/modules/stats/actions/index.ts

Comment on lines +125 to +133
app.get("/api/data/users/count", async (_req, res) => {
try {
const count = await prisma.user.count()
res.json({ count })
} catch (error) {
console.error("[users/count] Error:", error)
res.status(500).json({ error: "Failed to fetch user count" })
}
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)apps/supercode-cli/server/src/index\.ts$|(^|/)apps/supercode-cli/server/src/server\.ts$|package\.json$|bun\.lockb$' || true

echo "== index outline =="
if [ -f apps/supercode-cli/server/src/index.ts ]; then
  wc -l apps/supercode-cli/server/src/index.ts
  ast-grep outline apps/supercode-cli/server/src/index.ts || true
  echo "== relevant index =="
  sed -n '1,220p' apps/supercode-cli/server/src/index.ts
fi

echo "== route/framework references =="
rg -n "app\.get|Express|express|Bun\.serve|WebSocket|prisma\.user\.count|users/count" apps/supercode-cli/server/src/index.ts apps/supercode-cli/server 2>/dev/null | head -200 || true

Repository: yashdev9274/supercli

Length of output: 13398


Use Bun.serve() for the new route.

This handler is added via Express’s app.get, but apps/supercode-cli/server/src/index.ts matches the repository rule requiring Bun.serve() with routes and WebSocket support instead of Express. Migrate this route with the server implementation or provide an explicit exemption.

🤖 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 `@apps/supercode-cli/server/src/index.ts` around lines 125 - 133, Update the
user-count route around the app.get handler to use the existing Bun.serve()
routing structure instead of Express, preserving its Prisma user count response
and 500-error behavior. Integrate the route into the server’s existing routes
while retaining WebSocket support, or use the repository’s established exemption
mechanism if migration is not possible.

Source: Coding guidelines

Comment on lines +3 to +5
async function safeFetchJson<T>(url: string, fallback: T): Promise<T> {
try {
const res = await fetch(url, { next: { revalidate: 3600 } })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate stats page and actions =="
fd -a 'page\.tsx$|index\.ts$' apps/web/modules/apps/web 2>/dev/null | sed 's#^\./##' | rg 'apps/web/app/\(pages\)/stats/page\.tsx|apps/web/modules/stats/actions/index\.ts' || true

echo
echo "== stats page outline/content =="
if [ -f 'apps/web/app/(pages)/stats/page.tsx' ]; then
  wc -l 'apps/web/app/(pages)/stats/page.tsx'
  cat -n 'apps/web/app/(pages)/stats/page.tsx' | sed -n '1,220p'
fi

echo
echo "== stats actions content =="
if [ -f 'apps/web/modules/stats/actions/index.ts' ]; then
  wc -l 'apps/web/modules/stats/actions/index.ts'
  cat -n 'apps/web/modules/stats/actions/index.ts' | sed -n '1,180p'
fi

Repository: yashdev9274/supercli

Length of output: 1840


🌐 Web query:

Next.js App Router force-dynamic fetch next revalidate revalidate ignored route segment config

💡 Result:

In the Next.js App Router, the interaction between individual fetch revalidation settings and route segment configuration—particularly force-dynamic—has evolved through various releases. Here is how they function together. Understanding Segment vs. Fetch Settings The route segment config (e.g., export const revalidate or export const dynamic) acts as a default or constraint for the entire route, while the fetch options are granular, per-request settings [1][2]. Impact of force-dynamic When a route is marked with export const dynamic = 'force-dynamic', Next.js forces the route to be rendered dynamically on every request [1]. Historically, this configuration caused all fetch requests within that segment to opt out of caching [3][4]. Precedence and Recent Changes Recent updates (notably PR #72357) have refined this behavior to allow more explicit control: 1. Fetch Precedence: If you explicitly set a positive revalidate value on a fetch request (e.g., next: { revalidate: 60 }) inside a force-dynamic route, the revalidate setting is now intended to take precedence, allowing that specific request to be cached even if the route is otherwise dynamic [5]. 2. Opting into Cache: For this to work reliably when force-dynamic is present, you should generally ensure you are not conflicting with other cache-disabling settings and may need to explicitly use cache: 'force-cache' in your fetch call if the default behavior is being overridden by the segment config [5]. Common Pitfalls - Segment as Ceiling: When both segment-level revalidate and fetch-level revalidate are set, the effective revalidation time is the lower of the two values [1][6]. If the segment config is set to a specific number (e.g., revalidate = 60), a fetch with revalidate = 3600 will still revalidate every 60 seconds [1]. - Conflicting Options: Setting both { revalidate: 3600, cache: 'no-store' } in a single fetch call will cause the options to be ignored, and Next.js will typically log a warning in development mode [7]. - Development vs. Production: Behavior can sometimes differ in next dev due to how the development server handles cache headers and HMR [8]. Always verify caching behavior using production builds (next build && next start) [2]. If you find your fetch revalidation is being ignored, check for conflicting settings (like cache: 'no-store') and ensure your Next.js version is up to date, as issues regarding force-dynamic overriding fetch cache have been subject to frequent framework patches [9][5][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package versions =="
if [ -f package.json ]; then
  node - <<'JS'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('package.json','utf8'));
console.log('root next:', p.dependencies?.next || p.devDependencies?.next);
console.log('root react:', p.dependencies?.react || p.devDependencies?.react);
for (const f of ['apps/web/package.json', 'apps/web/package-lock.json', 'package-lock.json']) {
  if (fs.existsSync(f)) {
    console.log('\nfile:', f);
    const pkg = JSON.parse(fs.readFileSync(f,'utf8'));
    if (pkg.dependencies?.next || pkg.devDependencies?.next) {
      console.log('next:', pkg.dependencies?.next || pkg.devDependencies?.next);
    }
  }
}
JS
fi

echo
echo "== next config =="
fd -a 'next\.config\.(js|mjs|ts)$' apps/web . | sed 's#^\./##' | while read -r f; do
  echo "--- $f"
  wc -l "$f"
  cat -n "$f" | sed -n '1,220p'
done

Repository: yashdev9274/supercli

Length of output: 2817


🌐 Web query:

apps/web next.config.ts is server app page config dynamic force-dynamic Next.js 15 route segment config

💡 Result:

In Next.js 15, the dynamic configuration is a Route Segment Config option, not a setting defined in next.config.ts [1][2][3]. To force a page, layout, or route handler to be dynamically rendered at request time, you must export the dynamic constant directly from the route segment file (e.g., page.tsx, layout.tsx, or route.ts) [1][4]. Use the following syntax in your page or layout file: export const dynamic = 'force-dynamic'; When this is set, the route will be rendered for each user at request time [1][5]. This is functionally equivalent to setting every fetch request within that segment to { cache: 'no-store', next: { revalidate: 0 } } and setting the fetchCache option to 'force-no-store' [1]. Key points regarding this configuration: - Next.config.ts: This file is for global project-level configuration (e.g., experimental flags, redirects, headers) and does not control the dynamic rendering behavior of individual routes [2][3]. - Purpose: 'force-dynamic' is intended for routes that require request-time data, such as headers, cookies, or frequently changing server-side data [5]. - Alternatives: Using export const revalidate = 0 in your route segment provides an equivalent outcome, forcing the route to be dynamic [5][6]. - Next.js 15 behavior: Note that in Next.js 15, GET Route Handlers are not cached by default [2]. If you only need to ensure data freshness for specific fetches, you can use the { cache: 'no-store' } option on those individual fetch calls rather than forcing the entire route to be dynamic [1][6].

Citations:


Avoid forcing hourly stats revalidation into no-store.

apps/web/app/(pages)/stats/page.tsx exports dynamic = "force-dynamic", and its fetch-like calls all originate from that route (including getStats()), so the explicit next: { revalidate: 3600 } is unlikely to establish the intended hourly cache. Remove force-dynamic if the page can be statically generated, or move the cached fetch behind a route/Server Component boundary that is not forced dynamic.

🤖 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 `@apps/web/modules/stats/actions/index.ts` around lines 3 - 5, Update the stats
page’s caching configuration so safeFetchJson’s revalidate: 3600 can establish
hourly caching: remove the force-dynamic export from the stats page when static
generation is supported, or move the cached getStats/safeFetchJson fetch behind
a route or Server Component boundary that is not forced dynamic.

Source: MCP tools

Comment on lines +3 to +10
async function safeFetchJson<T>(url: string, fallback: T): Promise<T> {
try {
const res = await fetch(url, { next: { revalidate: 3600 } })
return await res.json()
} catch {
return fallback
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate target file and related files"
git ls-files | rg '(^|/)apps/web/modules/stats/actions/index\.ts$|package.json$|zod|tsconfig'

echo
echo "Target file outline"
ast-grep outline apps/web/modules/stats/actions/index.ts 2>/dev/null || true

echo
echo "Target file content"
cat -n apps/web/modules/stats/actions/index.ts

echo
echo "Search stats/json5/500/fetch usages"
rg -n "safeFetchJson|getStats|force-dynamic|fetch\(|json5|JSON\.parse|500|revalidate|localhost:3004" apps/web/modules/stats apps/web/features apps/web -g '*.ts' -g '*.tsx' | head -200

Repository: yashdev9274/supercli

Length of output: 15299


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "apps/web/package.json relevant deps"
node - <<'JS'
const fs = require('fs')
const p = JSON.parse(fs.readFileSync('apps/web/package.json', 'utf8'))
const deps = {...(p.dependencies||{}), ...(p.devDependencies||{})}
for (const [k,v] of Object.entries(deps)) {
  if (/^(next|zod|json5)$/.test(k)) console.log(`${k}: ${v}`)
}
JS

echo
echo "Behavioral probe: fetch response handling and numeric coercion"
node - <<'JS'
class Response {
  constructor(status, json, err) {
    this.status = status
    this._json = json
    this._err = err
    this.ok = status >= 200 && status < 300
  }
  async json() {
    if (this._err) throw this._err
    return this._json
  }
}

async function currentSafeFetchJson(url, fallback) {
  // Minimal JS equivalent of the current helper: no status check
  const res = new Response(500, { error: "Internal Server Error" }, undefined)
  try {
    return await res.json()
  } catch {
    return fallback
  }
}

for (const payload of [
  { status: 500, json: { downloads: "not a number" }, fallback: { downloads: 0 } },
  { status: 200, json: { downloads: NaN }, fallback: { downloads: 0 } },
  { status: 200, json: { downloads: null }, fallback: { downloads: 0 } },
  { status: "network error thrown", json: null, fallback: { downloads: 0 } },
]) {
  let actualPayload
  try {
    actualPayload = payload.json
  } catch (e) {
    actualPayload = `THREW:${e.message}`
  }
  const fallbackReturn = typeof actualPayload === "object" && actualPayload !== null ? actualPayload : payload.fallback
  const userStats = fallbackReturn
  const computed = (userStats.downloads ? 1 : 0) + 6000
  const arithmeticSafe = (userStats.downloads ?? 0) + 6000
  console.log(JSON.stringify({
    inputPayload: payload.json,
    inputStatus: payload.status,
    fallsthroughTo: fallbackReturn,
    arithmeticUsingNullishCoalescing: arithmeticSafe,
    arithmeticUsingTruthiness: computed,
  }))
}
JS

Repository: yashdev9274/supercli

Length of output: 971


Enforce fallbacks for non-OK responses and invalid payload fields.

safeFetchJson currently returns any parsed JSON for 5xx/redirect responses, e.g. count: "error", while getStats() coerces that to 1 + 100. Add if (!res.ok) return fallback before res.json() and validate each payload schema with Zod before the fallback arithmetic.

🤖 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 `@apps/web/modules/stats/actions/index.ts` around lines 3 - 10, Update
safeFetchJson to return fallback when the fetch response is not OK before
parsing JSON. In getStats, validate each fetched payload with the existing or
appropriate Zod schemas before performing fallback arithmetic, so invalid fields
such as string counts use the documented fallback instead of being coerced.

Source: Coding guidelines

Comment on lines +13 to +15
const serverUrl = process.env.TERMINAL_SERVER_URL || "http://localhost:3004"
const [userStats, npmResponse, githubResponse] = await Promise.all([
safeFetchJson<{ count?: number }>(`${serverUrl}/api/data/users/count`, { count: 0 }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Align the terminal-server URL fallback.

getStats defaults to http://localhost:3004, but apps/supercode-cli/server/src/index.ts defaults PORT to 10000 at Line 27. Without TERMINAL_SERVER_URL, this request misses the terminal server and the stats page silently reports fallback users. Reuse the same URL resolver and fallback chain as apps/web/app/api/data/analytics/route.ts.

🤖 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 `@apps/web/modules/stats/actions/index.ts` around lines 13 - 15, Update
getStats to resolve the terminal-server URL using the same resolver and fallback
chain as the analytics route, rather than defaulting directly to
http://localhost:3004. Reuse the existing shared URL-resolution logic so the
fallback aligns with the terminal server’s PORT=10000 default and requests the
correct users endpoint.

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