Skip to content

fix(webapp): server logs pagination and twitch not-configured ux - #613

Merged
LucasSantana-Dev merged 3 commits into
mainfrom
fix/webapp-logs-twitch
Apr 14, 2026
Merged

LucasSantana-Dev merged 3 commits into
mainfrom
fix/webapp-logs-twitch

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented Apr 14, 2026 •

Copy link
Copy Markdown
Owner

Server Logs Pagination Fix

  • Problem: Pagination was broken - the frontend calculated total logs based on the current page slice length, not the actual total count
  • Solution: Added countRecentLogs and countLogsByType methods to ServerLogService; API now returns the actual total count
  • Changes:
    • Backend: Modified /api/guilds/:guildId/logs endpoint to return { logs, total } instead of just { logs }
    • Frontend: Updated ServerLogs.tsx to use res.data.total from the API response
    • Tests: Updated integration tests to mock the new count methods

Twitch Notifications Not-Configured UX

  • Problem: When Twitch API credentials aren't configured, users got a generic 503 error
  • Solution: Added a status endpoint and pre-check in the UI to show a clear banner
  • Changes:
    • Backend: Added GET /api/twitch/status endpoint that returns { configured: boolean }
    • Frontend: Added status check on component mount; shows clear banner and disables Add button when not configured
    • API: Added twitch.getStatus() method to the frontend API service

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Twitch notifications now validate whether the Twitch API is properly configured and display a "Not Configured" message with the Add button disabled when setup is incomplete
  • Improvements

    • Server logs pagination now uses accurate total counts retrieved from the server for all filtered and unfiltered log views, improving page range display accuracy

- Add total count to logs API response to fix pagination bug
- Add countRecentLogs and countLogsByType methods to ServerLogService
- Update frontend to use API-returned total instead of page length
- Add GET /api/twitch/status endpoint to check configuration
- Show clear banner when Twitch is not configured
- Disable Add button when Twitch API credentials are missing
- Update tests to expect total field in logs response
@coderabbitai

coderabbitai Bot commented Apr 14, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR adds server-side total count calculation for server logs endpoints and introduces a new Twitch configuration status check. Backend routes now compute and return log totals; the frontend pagination uses these provided counts. A new Twitch status endpoint checks API configuration availability.

Changes

Cohort / File(s) Summary
Log Counting Backend
packages/shared/src/services/ServerLogService.ts, packages/backend/src/routes/management.ts
Added countRecentLogs and countLogsByType methods to service. Route handlers now call these methods and return { logs, total } response instead of just logs.
Log Counting Tests
packages/backend/tests/integration/routes/management.test.ts
Extended mock service with new counting methods and updated test assertions to verify total field in responses for both filtered and unfiltered log endpoints.
Log API Types
packages/frontend/src/services/logsApi.ts
Updated response type signatures for getRecent and getByType from { logs: ServerLog[] } to { logs: ServerLog[]; total: number }.
Log Pagination Frontend
packages/frontend/src/pages/ServerLogs.tsx
Updated pagination total calculation to use server-provided res.data.total instead of deriving from allLogs.length.
Twitch Configuration Status
packages/backend/src/routes/twitch.ts, packages/frontend/src/services/api.ts
Added new GET /api/twitch/status route returning { configured: boolean } based on TWITCH_CLIENT_ID env var. Added corresponding twitch.status() API client method.
Twitch Configuration Tests
packages/backend/tests/integration/routes/twitch.test.ts
Added integration test suite validating the status endpoint with and without the env var set.
Twitch Configuration UI
packages/frontend/src/pages/TwitchNotifications.tsx, packages/frontend/src/pages/TwitchNotifications.test.tsx
Added twitchConfigured state initialized via useEffect calling status endpoint. Renders "Not Configured" UI when false; disables "Add" button when not configured. Added test case verifying disabled state and message when unconfigured.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the two main changes: server logs pagination fix and Twitch not-configured UX improvement.

✏️ 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 fix/webapp-logs-twitch

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.

@vercel

vercel Bot commented Apr 14, 2026 •

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
lucky Ready Ready Preview, Comment Apr 14, 2026 5:35pm

@github-actions

github-actions Bot commented Apr 14, 2026 •

Copy link
Copy Markdown

Size Change: +162 B (+0.05%)

Total Size: 326 kB

📦 View Changed
Filename Size Change
packages/frontend/dist/assets/api-C9C_BoPi.js 0 B -2.91 kB (removed) 🏆
packages/frontend/dist/assets/api-Ct-G-V9x.js 2.92 kB +2.92 kB (new file) 🆕
packages/frontend/dist/assets/authStore-B-XgI7Qf.js 0 B -560 B (removed) 🏆
packages/frontend/dist/assets/authStore-Bv7ddPph.js 559 B +559 B (new file) 🆕
packages/frontend/dist/assets/AutoMessages-COIPz34j.js 0 B -2.69 kB (removed) 🏆
packages/frontend/dist/assets/AutoMessages-D4H6-gy2.js 2.69 kB +2.69 kB (new file) 🆕
packages/frontend/dist/assets/AutoMod-_J4xnRuw.js 4.09 kB +4.09 kB (new file) 🆕
packages/frontend/dist/assets/AutoMod-BjdrXXl1.js 0 B -4.09 kB (removed) 🏆
packages/frontend/dist/assets/CommandsConfig-BQdOTJOd.js 1.49 kB +1.49 kB (new file) 🆕
packages/frontend/dist/assets/CommandsConfig-DJriytRY.js 0 B -1.49 kB (removed) 🏆
packages/frontend/dist/assets/Config-Ly7WAJ1V.js 1.74 kB +1.74 kB (new file) 🆕
packages/frontend/dist/assets/Config-UsnxKSX8.js 0 B -1.73 kB (removed) 🏆
packages/frontend/dist/assets/CustomCommands-DGy0CT66.js 2.16 kB +2.16 kB (new file) 🆕
packages/frontend/dist/assets/CustomCommands-DhT_cm6n.js 0 B -2.16 kB (removed) 🏆
packages/frontend/dist/assets/DashboardOverview-BYts12pJ.js 3.42 kB +3.42 kB (new file) 🆕
packages/frontend/dist/assets/DashboardOverview-CZZny723.js 0 B -3.42 kB (removed) 🏆
packages/frontend/dist/assets/EmbedBuilder-CH5OgzK4.js 3.36 kB +3.36 kB (new file) 🆕
packages/frontend/dist/assets/EmbedBuilder-DmZZ45A8.js 0 B -3.36 kB (removed) 🏆
packages/frontend/dist/assets/Features-BI9IXosn.js 2.85 kB +2.85 kB (new file) 🆕
packages/frontend/dist/assets/Features-F7FIPxrz.js 0 B -2.85 kB (removed) 🏆
packages/frontend/dist/assets/GuildAutomation-BxuBuwK4.js 0 B -2.94 kB (removed) 🏆
packages/frontend/dist/assets/GuildAutomation-D_OSoqvi.js 2.94 kB +2.94 kB (new file) 🆕
packages/frontend/dist/assets/guildStore-CCTErGxJ.js 792 B +792 B (new file) 🆕
packages/frontend/dist/assets/guildStore-CddfDJuw.js 0 B -791 B (removed) 🏆
packages/frontend/dist/assets/index-2n4D62Ff.js 11.8 kB +11.8 kB (new file) 🆕
packages/frontend/dist/assets/index-8zYVlqnD.css 14.4 kB +14.4 kB (new file) 🆕
packages/frontend/dist/assets/index-C2e-WFQf.js 0 B -11.8 kB (removed) 🏆
packages/frontend/dist/assets/index-DoLe_VYd.css 0 B -14.4 kB (removed) 🏆
packages/frontend/dist/assets/LastFm-Bid2qyVt.js 0 B -1.74 kB (removed) 🏆
packages/frontend/dist/assets/LastFm-WH6EVB_N.js 1.74 kB +1.74 kB (new file) 🆕
packages/frontend/dist/assets/Levels-CCYbG06S.js 0 B -2.65 kB (removed) 🏆
packages/frontend/dist/assets/Levels-osW8WkBp.js 2.65 kB +2.65 kB (new file) 🆕
packages/frontend/dist/assets/Login-BfPCIyNH.js 2.54 kB +2.54 kB (new file) 🆕
packages/frontend/dist/assets/Login-COmPipU-.js 0 B -2.54 kB (removed) 🏆
packages/frontend/dist/assets/Lyrics-6SdUY8KZ.js 0 B -1.34 kB (removed) 🏆
packages/frontend/dist/assets/Lyrics-BRDXFcH3.js 1.34 kB +1.34 kB (new file) 🆕
packages/frontend/dist/assets/Moderation-B71W0LI7.js 0 B -3.87 kB (removed) 🏆
packages/frontend/dist/assets/Moderation-C0No7n5t.js 3.86 kB +3.86 kB (new file) 🆕
packages/frontend/dist/assets/Music-CSyTXQxi.js 0 B -6.16 kB (removed) 🏆
packages/frontend/dist/assets/Music-ZvGEx9On.js 6.16 kB +6.16 kB (new file) 🆕
packages/frontend/dist/assets/MusicConfig-CG5oFEW-.js 1.62 kB +1.62 kB (new file) 🆕
packages/frontend/dist/assets/MusicConfig-DuDvDoPw.js 0 B -1.62 kB (removed) 🏆
packages/frontend/dist/assets/ReactionRoles-C6RicQAG.js 0 B -1.9 kB (removed) 🏆
packages/frontend/dist/assets/ReactionRoles-DfmF6V3d.js 1.9 kB +1.9 kB (new file) 🆕
packages/frontend/dist/assets/ServerLogs-C-FHNVXc.js 2.9 kB +2.9 kB (new file) 🆕
packages/frontend/dist/assets/ServerLogs-D-7CLfoM.js 0 B -2.9 kB (removed) 🏆
packages/frontend/dist/assets/ServerSettings-Bn-PAwK-.js 0 B -4.22 kB (removed) 🏆
packages/frontend/dist/assets/ServerSettings-C58L9eEX.js 4.22 kB +4.22 kB (new file) 🆕
packages/frontend/dist/assets/ServersPage-BMxvmSM4.js 0 B -2.95 kB (removed) 🏆
packages/frontend/dist/assets/ServersPage-O2OL1mIE.js 2.95 kB +2.95 kB (new file) 🆕
packages/frontend/dist/assets/Spotify-BvG85-oI.js 0 B -1.75 kB (removed) 🏆
packages/frontend/dist/assets/Spotify-C517gk64.js 1.75 kB +1.75 kB (new file) 🆕
packages/frontend/dist/assets/Starboard-CIlrtGzw.js 2.1 kB +2.1 kB (new file) 🆕
packages/frontend/dist/assets/Starboard-CJkVfcaD.js 0 B -2.1 kB (removed) 🏆
packages/frontend/dist/assets/TrackHistory-CRDtIZqN.js 0 B -1.91 kB (removed) 🏆
packages/frontend/dist/assets/TrackHistory-ICsjZAYG.js 1.91 kB +1.91 kB (new file) 🆕
packages/frontend/dist/assets/TwitchNotifications-BQqtKmfp.js 0 B -2.29 kB (removed) 🏆
packages/frontend/dist/assets/TwitchNotifications-trrtSoGO.js 2.44 kB +2.44 kB (new file) 🆕
packages/frontend/dist/assets/useGuildSelection-CkUsxL5H.js 0 B -489 B (removed) 🏆
packages/frontend/dist/assets/useGuildSelection-Dr1T8xtB.js 488 B +488 B (new file) 🆕
ℹ️ View Unchanged
Filename Size
packages/frontend/dist/assets/ActionPanel-C1_aFFa9.js 398 B
packages/frontend/dist/assets/avatar-BQnx8ml3.js 1.79 kB
packages/frontend/dist/assets/badge-1w20SMT3.js 499 B
packages/frontend/dist/assets/Button-CK6bsnJt.js 761 B
packages/frontend/dist/assets/Card-DnMZziVL.js 453 B
packages/frontend/dist/assets/chunk-QFMPRPBF-B0_TipZB.js 14.8 kB
packages/frontend/dist/assets/dialog-Bebu5T9M.js 947 B
packages/frontend/dist/assets/dist-BYvsGl5s.js 414 B
packages/frontend/dist/assets/EmptyState-lhGRi9gh.js 428 B
packages/frontend/dist/assets/input-CmtLKKUO.js 461 B
packages/frontend/dist/assets/label-BHlkUPCY.js 483 B
packages/frontend/dist/assets/PrivacyPolicy-BTjDqB3o.js 1.38 kB
packages/frontend/dist/assets/rbac-BN1DzQHh.js 152 B
packages/frontend/dist/assets/rolldown-runtime-COnpUsM8.js 467 B
packages/frontend/dist/assets/scroll-area-DE87jUCx.js 517 B
packages/frontend/dist/assets/SectionHeader-Ct2qWDhq.js 382 B
packages/frontend/dist/assets/select-sC_ROyTR.js 1.22 kB
packages/frontend/dist/assets/Skeleton-DMHFbvVW.js 234 B
packages/frontend/dist/assets/StatTile-BkmmFyEu.js 604 B
packages/frontend/dist/assets/switch-RfNgpq5v.js 539 B
packages/frontend/dist/assets/TermsOfService-D7Oh05tV.js 1.37 kB
packages/frontend/dist/assets/usePageMetadata-B513X5D1.js 328 B
packages/frontend/dist/assets/utils-D5piDr1U.js 148 B
packages/frontend/dist/assets/vendor-forms-B7m4p2yU.js 25.4 kB
packages/frontend/dist/assets/vendor-radix-CqxcePto.js 33.5 kB
packages/frontend/dist/assets/vendor-react-DRFstzzV.js 55.6 kB
packages/frontend/dist/assets/vendor-state-C5KtdxzA.js 22.2 kB
packages/frontend/dist/assets/vendor-ui-BIJpYk2I.js 64.2 kB

compressed-size-action

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

🧹 Nitpick comments (4)
packages/backend/src/routes/management.ts (1)

221-237: Parallelize log fetch + count to reduce response time

Both calls are independent in each branch; running them sequentially adds avoidable latency.

Suggested refactor
             if (type) {
-                const logs = await serverLogService.getLogsByType(
-                    guildId,
-                    type as LogType,
-                    limit,
-                )
-                const total = await serverLogService.countLogsByType(
-                    guildId,
-                    type as LogType,
-                )
+                const [logs, total] = await Promise.all([
+                    serverLogService.getLogsByType(
+                        guildId,
+                        type as LogType,
+                        limit,
+                    ),
+                    serverLogService.countLogsByType(
+                        guildId,
+                        type as LogType,
+                    ),
+                ])
                 res.json({ logs, total })
                 return
             }

-            const logs = await serverLogService.getRecentLogs(guildId, limit)
-            const total = await serverLogService.countRecentLogs(guildId)
+            const [logs, total] = await Promise.all([
+                serverLogService.getRecentLogs(guildId, limit),
+                serverLogService.countRecentLogs(guildId),
+            ])
             res.json({ logs, total })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/backend/src/routes/management.ts` around lines 221 - 237, The two
independent DB calls in management route are done sequentially and should be
parallelized to reduce latency: for the type branch, call
serverLogService.getLogsByType(guildId, type as LogType, limit) and
serverLogService.countLogsByType(guildId, type as LogType) in parallel with
Promise.all and then res.json({ logs, total }); likewise for the fallback branch
use Promise.all with serverLogService.getRecentLogs(guildId, limit) and
serverLogService.countRecentLogs(guildId) so both values are fetched
concurrently before sending the response.
packages/backend/tests/integration/routes/twitch.test.ts (1)

31-52: Restore TWITCH_CLIENT_ID between tests to avoid env leakage

These tests mutate global process env but don’t restore the prior value, which can make neighboring tests flaky.

Suggested test hardening
 describe('GET /api/twitch/status', () => {
     let app: express.Express
+    let previousClientId: string | undefined

     beforeEach(() => {
+        previousClientId = process.env.TWITCH_CLIENT_ID
         app = express()
         app.use(express.json())
         setupTwitchRoutes(app)
         app.use(errorHandler)
     })
+
+    afterEach(() => {
+        if (previousClientId === undefined) {
+            delete process.env.TWITCH_CLIENT_ID
+        } else {
+            process.env.TWITCH_CLIENT_ID = previousClientId
+        }
+    })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/backend/tests/integration/routes/twitch.test.ts` around lines 31 -
52, The tests mutate process.env.TWITCH_CLIENT_ID without restoring it; capture
the original value before each test and restore it after each test (or use
afterEach) so neighboring tests don't leak state — for example, save the current
process.env.TWITCH_CLIENT_ID in beforeEach or at test start, run the test cases
that set/delete process.env.TWITCH_CLIENT_ID (the tests named 'returns
configured=true when TWITCH_CLIENT_ID is set' and 'returns configured=false when
TWITCH_CLIENT_ID is not set'), then restore the saved value in afterEach to
ensure process.env.TWITCH_CLIENT_ID is returned to its prior state for other
tests.
packages/frontend/src/pages/TwitchNotifications.test.tsx (1)

378-394: Nice UX test—consider one more assertion for short-circuit behavior

To fully enforce the pre-check contract, assert that notifications fetch is skipped when configured is false.

Optional assertion
         expect(
             screen.getByText(/Twitch API is not configured/),
         ).toBeInTheDocument()
         expect(screen.queryByText('Add')).not.toBeInTheDocument()
+        expect(api.twitch.list).not.toHaveBeenCalled()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/frontend/src/pages/TwitchNotifications.test.tsx` around lines 378 -
394, The test should also assert that the notifications fetch is skipped when
Twitch is unconfigured: after mocking api.twitch.status to return { configured:
false } (as already done) and calling renderPage()/waitFor, add an assertion
that vi.mocked(api.twitch.notifications) (or the actual notifications fetch
function on the api.twitch object used in the component) was not called (e.g.,
expect(vi.mocked(api.twitch.notifications)).not.toHaveBeenCalled()); this
enforces the short-circuit pre-check behavior alongside mockGuildSelection and
existing DOM assertions.
packages/backend/tests/integration/routes/management.test.ts (1)

725-763: Consider asserting count method calls in logs route tests

You already assert the response includes total; adding call assertions would better lock route behavior to the new contract.

Example assertions to add
             expect(mockServerLogService.getRecentLogs).toHaveBeenCalledWith(
                 '111111111111111111',
                 50,
             )
+            expect(mockServerLogService.countRecentLogs).toHaveBeenCalledWith(
+                '111111111111111111',
+            )
             expect(mockServerLogService.getLogsByType).toHaveBeenCalledWith(
                 '111111111111111111',
                 'error',
                 50,
             )
+            expect(mockServerLogService.countLogsByType).toHaveBeenCalledWith(
+                '111111111111111111',
+                'error',
+            )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/backend/tests/integration/routes/management.test.ts` around lines
725 - 763, Tests for the logs route assert the response.total but don't assert
that the corresponding count methods were called; add expectations that
mockServerLogService.countRecentLogs was called with '111111111111111111' in the
first test (after getRecentLogs assertion) and that
mockServerLogService.countLogsByType was called with '111111111111111111' and
'error' in the second test (after getLogsByType assertion) to lock the route to
the new contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/backend/src/routes/twitch.ts`:
- Around line 113-114: The /api/twitch/status endpoint currently sets configured
= !!process.env.TWITCH_CLIENT_ID which can be a false positive; update the check
so configured is true only if TWITCH_CLIENT_ID is present AND at least one of
TWITCH_ACCESS_TOKEN or TWITCH_CLIENT_SECRET is present (i.e. configured =
!!TWITCH_CLIENT_ID && (!!TWITCH_ACCESS_TOKEN || !!TWITCH_CLIENT_SECRET)), then
return that value in res.json({ configured }); locate this in
packages/backend/src/routes/twitch.ts where configured is defined and used.

In `@packages/frontend/src/pages/TwitchNotifications.tsx`:
- Around line 55-62: The current checkTwitchStatus async handler maps any
exception from api.twitch.status() to setTwitchConfigured(false), which treats
network/timeouts/5xx as “not configured”; update checkTwitchStatus to
distinguish a real 404/unsuccessful configuration response from transient errors
by inspecting the error/response: call api.twitch.status() and on success
setTwitchConfigured(res.data.configured); on error, if the error indicates a
definitive “not configured” (e.g., response.status === 404 or error payload
explicitly says not configured) setTwitchConfigured(false), otherwise do not
change the existing twitchConfigured state and optionally set or log a
transientError flag; reference the checkTwitchStatus function, useEffect hook,
setTwitchConfigured, and api.twitch.status to locate where to implement this
logic.

In `@packages/frontend/src/services/logsApi.ts`:
- Around line 6-19: The current getRecent and getByType calls drop limit when it
is 0 because they use truthy checks; change the param handling to test for
undefined explicitly (e.g., limit !== undefined) so a numeric 0 is passed
through; update the params objects in getRecent and getByType (the apiClient.get
calls) to include limit when limit !== undefined while keeping other params
(like type) intact.

---

Nitpick comments:
In `@packages/backend/src/routes/management.ts`:
- Around line 221-237: The two independent DB calls in management route are done
sequentially and should be parallelized to reduce latency: for the type branch,
call serverLogService.getLogsByType(guildId, type as LogType, limit) and
serverLogService.countLogsByType(guildId, type as LogType) in parallel with
Promise.all and then res.json({ logs, total }); likewise for the fallback branch
use Promise.all with serverLogService.getRecentLogs(guildId, limit) and
serverLogService.countRecentLogs(guildId) so both values are fetched
concurrently before sending the response.

In `@packages/backend/tests/integration/routes/management.test.ts`:
- Around line 725-763: Tests for the logs route assert the response.total but
don't assert that the corresponding count methods were called; add expectations
that mockServerLogService.countRecentLogs was called with '111111111111111111'
in the first test (after getRecentLogs assertion) and that
mockServerLogService.countLogsByType was called with '111111111111111111' and
'error' in the second test (after getLogsByType assertion) to lock the route to
the new contract.

In `@packages/backend/tests/integration/routes/twitch.test.ts`:
- Around line 31-52: The tests mutate process.env.TWITCH_CLIENT_ID without
restoring it; capture the original value before each test and restore it after
each test (or use afterEach) so neighboring tests don't leak state — for
example, save the current process.env.TWITCH_CLIENT_ID in beforeEach or at test
start, run the test cases that set/delete process.env.TWITCH_CLIENT_ID (the
tests named 'returns configured=true when TWITCH_CLIENT_ID is set' and 'returns
configured=false when TWITCH_CLIENT_ID is not set'), then restore the saved
value in afterEach to ensure process.env.TWITCH_CLIENT_ID is returned to its
prior state for other tests.

In `@packages/frontend/src/pages/TwitchNotifications.test.tsx`:
- Around line 378-394: The test should also assert that the notifications fetch
is skipped when Twitch is unconfigured: after mocking api.twitch.status to
return { configured: false } (as already done) and calling renderPage()/waitFor,
add an assertion that vi.mocked(api.twitch.notifications) (or the actual
notifications fetch function on the api.twitch object used in the component) was
not called (e.g.,
expect(vi.mocked(api.twitch.notifications)).not.toHaveBeenCalled()); this
enforces the short-circuit pre-check behavior alongside mockGuildSelection and
existing DOM assertions.
🪄 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: b650077b-31e6-4e37-8bde-24830df0c399

📥 Commits

Reviewing files that changed from the base of the PR and between 41c8e1e and 5eafcad.

📒 Files selected for processing (10)
  • packages/backend/src/routes/management.ts
  • packages/backend/src/routes/twitch.ts
  • packages/backend/tests/integration/routes/management.test.ts
  • packages/backend/tests/integration/routes/twitch.test.ts
  • packages/frontend/src/pages/ServerLogs.tsx
  • packages/frontend/src/pages/TwitchNotifications.test.tsx
  • packages/frontend/src/pages/TwitchNotifications.tsx
  • packages/frontend/src/services/api.ts
  • packages/frontend/src/services/logsApi.ts
  • packages/shared/src/services/ServerLogService.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: SonarCloud Scan
  • GitHub Check: compressed-size
  • GitHub Check: Quality Gates
🔇 Additional comments (7)
packages/backend/tests/integration/routes/management.test.ts (1)

342-344: No concerns on this hunk (format-only split call)

packages/shared/src/services/ServerLogService.ts (1)

84-92: Count helpers look correct and consistent

These methods match the same where filters as the corresponding list queries, so backend pagination totals should stay accurate.

packages/frontend/src/services/api.ts (1)

346-346: Status API client addition looks good

This method cleanly matches the new backend status endpoint and keeps the typed response shape consistent.

packages/frontend/src/pages/ServerLogs.tsx (1)

175-181: Pagination total source fix is correct

Using res.data.total on Line 181 properly decouples pagination metadata from the current page’s sliced list.

packages/frontend/src/pages/TwitchNotifications.test.tsx (1)

97-99: Defaulting api.twitch.status to configured in setup is solid

Good baseline to keep existing tests focused unless a test explicitly overrides status.

packages/frontend/src/pages/TwitchNotifications.tsx (2)

290-308: Clear not-configured UX branch.

The dedicated “Not Configured” state is straightforward and removes ambiguity for users.


324-325: Good defensive disable on Add action.

Disabling the Add button when configuration isn’t confirmed prevents invalid attempts.

Comment on lines +113 to +114
const configured = !!process.env.TWITCH_CLIENT_ID
res.json({ configured })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

/api/twitch/status can report a false positive configured state

Line 113 only checks TWITCH_CLIENT_ID, but lookup logic in this same file also needs either TWITCH_ACCESS_TOKEN or TWITCH_CLIENT_SECRET. This can re-enable the broken UX you’re fixing (button enabled, then lookup fails).

Suggested fix
-            const configured = !!process.env.TWITCH_CLIENT_ID
+            const clientId = process.env.TWITCH_CLIENT_ID
+            const hasAccessToken = !!process.env.TWITCH_ACCESS_TOKEN
+            const hasClientSecret = !!process.env.TWITCH_CLIENT_SECRET
+            const configured = Boolean(clientId && (hasAccessToken || hasClientSecret))
             res.json({ configured })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/backend/src/routes/twitch.ts` around lines 113 - 114, The
/api/twitch/status endpoint currently sets configured =
!!process.env.TWITCH_CLIENT_ID which can be a false positive; update the check
so configured is true only if TWITCH_CLIENT_ID is present AND at least one of
TWITCH_ACCESS_TOKEN or TWITCH_CLIENT_SECRET is present (i.e. configured =
!!TWITCH_CLIENT_ID && (!!TWITCH_ACCESS_TOKEN || !!TWITCH_CLIENT_SECRET)), then
return that value in res.json({ configured }); locate this in
packages/backend/src/routes/twitch.ts where configured is defined and used.

Comment on lines +55 to +62
useEffect(() => {
const checkTwitchStatus = async () => {
try {
const res = await api.twitch.status()
setTwitchConfigured(res.data.configured)
} catch {
setTwitchConfigured(false)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t map all status-check failures to “not configured.”

Line 61 currently sets twitchConfigured to false for any exception (network failure, timeout, 5xx), which can show an incorrect “Not Configured” banner and block actions for a temporary outage.

Suggested fix
+    const [twitchStatusError, setTwitchStatusError] = useState<string | null>(null)

     useEffect(() => {
         const checkTwitchStatus = async () => {
             try {
                 const res = await api.twitch.status()
                 setTwitchConfigured(res.data.configured)
+                setTwitchStatusError(null)
             } catch {
-                setTwitchConfigured(false)
+                setTwitchConfigured(null)
+                setTwitchStatusError('Unable to verify Twitch configuration status')
             }
         }
         checkTwitchStatus()
     }, [])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/frontend/src/pages/TwitchNotifications.tsx` around lines 55 - 62,
The current checkTwitchStatus async handler maps any exception from
api.twitch.status() to setTwitchConfigured(false), which treats
network/timeouts/5xx as “not configured”; update checkTwitchStatus to
distinguish a real 404/unsuccessful configuration response from transient errors
by inspecting the error/response: call api.twitch.status() and on success
setTwitchConfigured(res.data.configured); on error, if the error indicates a
definitive “not configured” (e.g., response.status === 404 or error payload
explicitly says not configured) setTwitchConfigured(false), otherwise do not
change the existing twitchConfigured state and optionally set or log a
transientError flag; reference the checkTwitchStatus function, useEffect hook,
setTwitchConfigured, and api.twitch.status to locate where to implement this
logic.

Comment on lines 6 to +19
getRecent: (guildId: string, limit?: number) =>
apiClient.get<{ logs: ServerLog[] }>(`/guilds/${guildId}/logs`, {
params: limit ? { limit } : {},
}),
apiClient.get<{ logs: ServerLog[]; total: number }>(
`/guilds/${guildId}/logs`,
{
params: limit ? { limit } : {},
},
),
getByType: (guildId: string, type: string, limit?: number) =>
apiClient.get<{ logs: ServerLog[] }>(`/guilds/${guildId}/logs`, {
params: { type, ...(limit ? { limit } : {}) },
}),
apiClient.get<{ logs: ServerLog[]; total: number }>(
`/guilds/${guildId}/logs`,
{
params: { type, ...(limit ? { limit } : {}) },
},
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use explicit undefined checks for limit query params.

Current truthy checks omit limit when it is 0 (Line 10 and Line 17), which can silently change request behavior.

Suggested fix
         getRecent: (guildId: string, limit?: number) =>
             apiClient.get<{ logs: ServerLog[]; total: number }>(
                 `/guilds/${guildId}/logs`,
                 {
-                    params: limit ? { limit } : {},
+                    params: limit !== undefined ? { limit } : {},
                 },
             ),
         getByType: (guildId: string, type: string, limit?: number) =>
             apiClient.get<{ logs: ServerLog[]; total: number }>(
                 `/guilds/${guildId}/logs`,
                 {
-                    params: { type, ...(limit ? { limit } : {}) },
+                    params: { type, ...(limit !== undefined ? { limit } : {}) },
                 },
             ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/frontend/src/services/logsApi.ts` around lines 6 - 19, The current
getRecent and getByType calls drop limit when it is 0 because they use truthy
checks; change the param handling to test for undefined explicitly (e.g., limit
!== undefined) so a numeric 0 is passed through; update the params objects in
getRecent and getByType (the apiClient.get calls) to include limit when limit
!== undefined while keeping other params (like type) intact.

@sonarqubecloud

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit 09e4ed5 into main Apr 14, 2026
13 checks passed
LucasSantana-Dev added a commit that referenced this pull request May 13, 2026
…ts (#613)

* fix(webapp): server logs pagination and twitch not-configured ux

- Add total count to logs API response to fix pagination bug
- Add countRecentLogs and countLogsByType methods to ServerLogService
- Update frontend to use API-returned total instead of page length
- Add GET /api/twitch/status endpoint to check configuration
- Show clear banner when Twitch is not configured
- Disable Add button when Twitch API credentials are missing
- Update tests to expect total field in logs response

* test(twitch): mock twitch status and add not-configured test

* test(twitch): add status endpoint coverage tests
@LucasSantana-Dev
LucasSantana-Dev deleted the fix/webapp-logs-twitch branch May 23, 2026 02:21

This branch was successfully deployed

1 active deployment
Preview — 5eafcadd Deployed Apr 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant