Skip to content

Feat/improved review process#353

Open
Producdevity wants to merge 14 commits into
stagingfrom
feat/improved-review-process
Open

Feat/improved review process#353
Producdevity wants to merge 14 commits into
stagingfrom
feat/improved-review-process

Conversation

@Producdevity
Copy link
Copy Markdown
Owner

@Producdevity Producdevity commented May 14, 2026

Description

Fixes # (issue)

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactor
  • Other (please describe):

How Has This Been Tested?

  • Local build
  • Lint
  • Typecheck
  • Unit tests
  • Manual testing

Screenshots (if applicable)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have made corresponding changes to the documentation
  • I have checked that all checks (lint, typecheck, test) pass

Notes for reviewers

Summary by CodeRabbit

  • New Features

    • "Risk only" filter for pending approvals; interactive review-risk indicators and consolidated review warning banners.
  • Improvements

    • Submission-risk profiling for emulator-version heuristics.
    • Admin UI: shared error state with retry and a compact risk filter toggle.
    • Dev/local: allow images from 127.0.0.1 and use different static asset caching in non-production.
  • Tests

    • Added coverage for review- and submission-risk services and UI components.

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 14, 2026

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

Project Deployment Actions Updated (UTC)
emuready Ready Ready Preview, Comment May 15, 2026 2:23pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds review-risk computation and wiring: submission/author/review risk services, repository pending-query support, router branching for riskFilter, admin UI/filter components, tests for services and routers, Toggle/Admin UI primitives, and small config/CORS/dev-script tweaks.

Changes

Review risk feature and infra

Layer / File(s) Summary
Next config & local dev origins
next.config.ts, package.json, src/app/testing/page.tsx, src/lib/cors.ts
Add 127.0.0.1 to developer origins, make _next static cache headers conditional on production, change dev script layout (dev / dev:turbo), include loopback in testing allowlist, and centralize local CI origins handling.
UI primitives & barrels
src/components/ui/ToggleButton.tsx, src/components/admin/AdminErrorState.tsx, src/components/ui/index.ts, src/components/admin/index.ts, src/components/ui/reviewRiskDisplay.ts
Add ToggleButton and AdminErrorState components, export new UI components via barrels, and introduce reviewRiskDisplay helpers (grouping/severity/count).
Review risk UI components & tests
src/components/ui/ReviewRiskIndicator.tsx, src/components/ui/ReviewRiskWarningBanner.tsx, src/components/ui/ReviewRiskIndicator.test.tsx, src/components/ui/ReviewRiskWarningBanner.test.tsx
Add ReviewRiskIndicator and ReviewRiskWarningBanner, rendering grouped signals, highest-severity styling and tooltip/interactions; include tests for rendering, accessibility labels, and interaction behavior.
Admin filter hook & button
src/app/admin/hooks/useReviewRiskFilter.ts, src/components/admin/ReviewRiskFilterButton.tsx, src/app/admin/hooks/index.ts
New useReviewRiskFilter hook managing isRiskOnly/riskFilter and callbacks to clear selection/reset page; add ReviewRiskFilterButton and re-export the hook.
Admin pages and modals wiring
src/app/admin/approvals/..., src/app/admin/pc-listing-approvals/...
Wire risk filter into pending listings queries, replace AuthorRisk components with ReviewRisk variants in tables/modals, clear selection & reset paging when toggling filter, and standardize pending-query error rendering via AdminErrorState.
Schemas: submission risk & riskFilter
src/schemas/submissionRisk.ts, src/schemas/listing.ts, src/schemas/pcListing.ts
Add submission risk schemas/types and ReviewRiskFilter; extend GetPending listings schemas to accept a riskFilter (default ALL).
Submission-risk service & tests
src/server/services/submission-risk.service.ts, src/server/services/submission-risk.service.test.ts
New submission-risk service detecting placeholder-like emulator versions, batching author credibility queries, computing severity, and providing computeSubmissionRiskProfiles/computeSubmissionRiskProfile; tests cover detection and severity rules.
Author-risk adjustments
src/server/services/author-risk.service.ts
Adjust computeAuthorRiskProfiles ban types, add createExistingAuthorBansMap helper, simplify one signal description, and add TODO notes.
Review-risk service & tests
src/server/services/review-risk.service.ts, src/server/services/review-risk.service.test.ts
New review-risk service to compute/attach author+submission profiles, detect risky candidate IDs, and unit tests for getRiskyReviewItemIds and attachReviewRiskProfiles.
Repository support for pending & risk candidates
src/server/repositories/listings.repository.ts, src/server/repositories/pc-listings.repository.ts
Add pending-specific query helpers, risk-candidate selects, getPendingListingsByIds, verified emulator lookups, pending where/order builders, and active-ban predicates to support the risky-candidates flow.
Router changes: listings & pcListings pending
src/server/api/routers/listings/admin.ts, src/server/api/routers/pcListings.ts
Refactor pending procedures to accept riskFilter, branch to candidate-based risky pagination (compute profiles → slice IDs → fetch by IDs → attach profiles) or to direct pending fetch; update imports to review-risk helpers.
Server tests: admin & pcListings pending
src/server/api/routers/listings/admin.test.ts, src/server/api/routers/pcListings.test.ts
Expand tests to cover risky/all/no-risk scenarios, add extensive repository/service mocks, and assert call sequences, parameters, returned ordering, attached severities, and pagination totals.
Misc utils & tests
src/server/utils/query-builders.ts, src/lib/cors.test.ts
Default showNsfw to false in buildNsfwFilter; add getAllowedOrigins tests and env unstub cleanup.
GlobalError typing
src/app/global-error.tsx
Type GlobalError props via a Props interface and use props.error in Sentry capture.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through code and schema rows,
Bounded risks where the candidate goes.
New buttons, banners, tests that sing,
Repos, routers — a reviewing spring.
Nibble a carrot, CI — let hope grow!

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning PR description is mostly incomplete: no issue referenced, no testing performed (all checkboxes unchecked), no code review checklist items completed, no notes for reviewers, and no meaningful context about the changes. Complete the description by referencing an issue, documenting which tests were run, checking the code style and self-review items, and providing context about the review-risk and submission-risk features being added.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive Title 'Feat/improved review process' is vague and does not clearly convey the specific changes made in this comprehensive refactor. Use a more specific title that summarizes the main change, such as 'Add review-risk filtering and submission-risk profiling to listing approvals' or similar.
✅ Passed checks (2 passed)
Check name Status Explanation
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 feat/improved-review-process

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/repositories/pc-listings.repository.ts (1)

855-882: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Inconsistent shadow-ban filtering between pending listings repositories.

The buildPendingListingsWhere method in this file applies shadow-ban filtering to pending PC listings when canSeeBannedUsers is false, but the equivalent method in listings.repository.ts does not apply any shadow-ban filtering.

This creates inconsistent behavior:

  • Pending PC listings from shadow-banned users are hidden by default
  • Pending handheld listings from shadow-banned users are shown

Both repositories should apply the same shadow-ban filtering logic, or neither should. Without alignment, moderators reviewing different listing types will have different visibility, which could lead to confusion and inconsistent workflows.

🤖 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 `@src/server/repositories/pc-listings.repository.ts` around lines 855 - 882,
The PC listings method buildPendingListingsWhere applies shadow-ban filtering
when canSeeBannedUsers is false but the handheld equivalent does not; align them
by applying the same logic: compute const shadowBanFilter = canSeeBannedUsers ?
undefined : buildShadowBanFilter(null) and merge it into the returned where
clause as ...(shadowBanFilter && { author: shadowBanFilter }) (or conversely
remove that merge from buildPendingListingsWhere if you intend no shadow-ban
filtering), ensuring both repositories use the same approach for
canSeeBannedUsers and buildShadowBanFilter so moderator visibility is
consistent.
🧹 Nitpick comments (6)
src/server/api/routers/listings/admin.test.ts (2)

21-43: 💤 Low value

Consider extracting the inline mock implementation to a shared test helper.

The inline implementation of createExistingAuthorBansMap duplicates business logic within the mock. If the real implementation changes, this could drift out of sync. Consider either importing the real helper or creating a shared test utility that both test files can use.

🤖 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 `@src/server/api/routers/listings/admin.test.ts` around lines 21 - 43, The
inline mock function createExistingAuthorBansMap duplicates production logic and
should be extracted: move the implementation into a shared test helper (e.g.,
tests/helpers or tests/utils) or import the real helper if appropriate, then
replace the inline definition in admin.test.ts with an import of
createExistingAuthorBansMap and update any other tests to reuse that single
exported function so logic remains centralized and in-sync with production code.

131-217: ⚡ Quick win

Add test coverage for the non-risky filter path.

The test suite only covers riskFilter: 'risky'. Consider adding test cases for:

  • riskFilter: 'all' to verify the standard pending listings path
  • Edge cases like no risky listings found, or pagination edge cases

This would ensure both code paths are exercised and prevent regressions.

🤖 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 `@src/server/api/routers/listings/admin.test.ts` around lines 131 - 217, Add
tests covering the non-risky and no-risky paths: in
src/server/api/routers/listings/admin.test.ts add a test calling
caller.getPending({ riskFilter: 'all', page: 1, limit: 20 }) that stubs
mockGetPendingListings to return expected rows and asserts
mockGetPendingListings was called (and mockGetPendingListingRiskCandidates was
not), verifies returned listings and pagination; also add a test for riskFilter:
'risky' when mockGetPendingListingRiskCandidates returns an empty array to
assert mockGetPendingListingsByIds is not called and result.listings.length and
pagination.total are 0. Ensure you reference and use the existing helpers/mocks
(createCaller, mockGetPendingListingRiskCandidates, mockGetPendingListingsByIds,
mockGetPendingListings) and mirror the style/assertions used in the existing
'filters risk-only listings' test.
src/server/api/routers/pcListings.test.ts (2)

81-103: ⚡ Quick win

Extract the inline mock implementation to a shared test utility.

This duplicates the same inline implementation pattern from admin.test.ts. Both test files would benefit from a shared test helper for createExistingAuthorBansMap to avoid duplication and ensure consistency.

🤖 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 `@src/server/api/routers/pcListings.test.ts` around lines 81 - 103, Extract the
inline createExistingAuthorBansMap implementation into a shared test utility
function and have both tests import and use it instead of duplicating the logic;
specifically, create and export a helper named createExistingAuthorBansMap that
accepts the same listings shape and returns a Map<string, {reason:string}[]>,
then replace the inline implementations in pcListings.test.ts and admin.test.ts
with imports of that helper so both tests call the single shared function and
retain identical behavior.

477-561: ⚡ Quick win

Add test coverage for the non-risky filter path.

Similar to admin.test.ts, this test suite only covers riskFilter: 'risky'. Add test cases for:

  • riskFilter: 'all' or undefined to verify the standard pending listings retrieval
  • Edge cases such as empty results or different user roles

This ensures both code paths are tested and prevents regressions.

🤖 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 `@src/server/api/routers/pcListings.test.ts` around lines 477 - 561, The suite
only tests the 'risky' riskFilter path; add tests that exercise the
non-risky/default path by calling caller.pending with riskFilter: 'all' and with
riskFilter omitted/undefined, verifying that mockRepositoryGetPendingListings is
used (not getPendingListingRiskCandidates/getPendingListingsByIds), that
returned pcListings match the mocked pending listings and pagination.total
reflects the count, and add an edge-case test for empty results and a test using
a non-moderator role (e.g., Role.USER) to validate permission-based branches;
reference mocks and helpers like createCaller, caller.pending,
mockRepositoryGetPendingListings, mockRepositoryGetPendingListingRiskCandidates,
mockRepositoryGetPendingListingsByIds, mockComputeAuthorRiskProfiles and
mockComputeSubmissionRiskProfiles to seed expected responses and assertions.
src/components/ui/ReviewRiskIndicator.test.tsx (1)

33-68: ⚡ Quick win

Add test coverage for the investigate interaction path.

The suite does not currently verify the clickable onInvestigate behavior (mouse + keyboard), which is core logic in the component.

🧪 Suggested test addition
-import { render, screen } from '@testing-library/react'
-import { describe, expect, it } from 'vitest'
+import { fireEvent, render, screen } from '@testing-library/react'
+import { describe, expect, it, vi } from 'vitest'
@@
   it('renders for submission risk without author risk', () => {
@@
   })
+
+  it('invokes investigate callback for clickable indicator', () => {
+    const onInvestigate = vi.fn()
+    render(
+      <ReviewRiskIndicator
+        authorRiskProfile={authorRiskProfile}
+        submissionRiskProfile={submissionRiskProfile}
+        onInvestigate={onInvestigate}
+      />,
+    )
+
+    const button = screen.getByRole('button')
+    fireEvent.click(button)
+    fireEvent.keyDown(button, { key: 'Enter' })
+    fireEvent.keyDown(button, { key: ' ' })
+
+    expect(onInvestigate).toHaveBeenCalledWith('author-1')
+    expect(onInvestigate).toHaveBeenCalledTimes(3)
+  })
 })
🤖 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 `@src/components/ui/ReviewRiskIndicator.test.tsx` around lines 33 - 68, Add
tests for the investigate interaction by rendering ReviewRiskIndicator with a
jest.fn() onInvestigate prop and asserting it is called on mouse click and
keyboard activation; locate the interactable element via
screen.getByRole('status') (or the same role used in the component), use
userEvent.click(status) to simulate mouse activation and
userEvent.keyboard('{Enter}') and userEvent.keyboard(' ') after status.focus()
to simulate Enter/Space keyboard activation, and assert onInvestigate was
invoked for each interaction; ensure the test covers both the case with
author/submission risk and the case with only submission risk so the click
target exists in both scenarios.
src/server/repositories/pc-listings.repository.ts (1)

890-894: 💤 Low value

Consider extracting active ban criteria to a helper method.

The inline active ban filtering (lines 891-893) duplicates the same logic used in forList include at lines 86-87. While correct, extracting this to a getActiveBanWhere() helper method (similar to listings.repository.ts line 572-577) would improve maintainability and ensure consistency across query builders.

♻️ Suggested refactor

Add a private helper method:

+  private getActiveBanWhere(): Prisma.UserBanWhereInput {
+    return {
+      isActive: true,
+      OR: [{ expiresAt: null }, { expiresAt: { gt: new Date() } }],
+    }
+  }
+
   private getPendingListingRiskCandidateSelect() {
     return {
       id: true,
       authorId: true,
       author: {
         select: {
           userBans: {
-            where: {
-              isActive: true,
-              OR: [{ expiresAt: null }, { expiresAt: { gt: new Date() } }],
-            },
+            where: this.getActiveBanWhere(),
             select: { reason: true },
           },
         },
       },

Then reuse in the forList include at line 85:

       author: {
         include: {
           userBans: {
-            where: {
-              isActive: true,
-              OR: [{ expiresAt: null }, { expiresAt: { gt: new Date() } }],
-            },
+            where: this.getActiveBanWhere(),
             select: { id: true, reason: true, bannedAt: true, expiresAt: true },
           },
         },
       },
🤖 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 `@src/server/repositories/pc-listings.repository.ts` around lines 890 - 894,
Extract the duplicated active-ban predicate into a private helper (e.g., private
getActiveBanWhere()) that returns the object { isActive: true, OR: [{ expiresAt:
null }, { expiresAt: { gt: new Date() } }] }, add it to the PCListingsRepository
class, and replace the inline userBans.where in the forList include and the
other occurrence (the block using userBans: { where: { ... } }) to call
getActiveBanWhere() so both queries reuse the same logic for consistency.
🤖 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 `@src/components/admin/ReviewRiskFilterButton.tsx`:
- Around line 12-19: This toggle button (Button in ReviewRiskFilterButton) needs
an accessible pressed state: add aria-pressed={props.isActive} to the Button
element so screen readers announce the on/off state; ensure props.isActive is a
boolean in the component's props type and keep onToggle as the click handler.

In `@src/server/api/routers/listings/admin.ts`:
- Around line 89-109: The final by-ID fetch (repository.getPendingListingsByIds)
can return listings that no longer meet the original scope (emulatorIds/pending)
between the two queries; update the fetch to re-apply the same scope checks used
to build riskCandidates: either modify repository.getPendingListingsByIds to
accept the same scope parameters (emulatorIds and pending status/search
constraints) and use them in the DB query, or after fetching pageListings filter
them against the original riskCandidates set (or a map keyed by id) so only
listings that still match the emulatorIds/pending scope are kept before calling
attachReviewRiskProfiles; reference getPendingListingRiskCandidates,
getRiskyReviewItemIds, getPendingListingsByIds, computeReviewRiskProfiles, and
attachReviewRiskProfiles.

In `@src/server/api/routers/pcListings.ts`:
- Around line 467-489: The refetch of listings via
repository.getPendingListingsByIds creates a TOCTOU gap because the original
candidate query used emulatorIds and other auth constraints; fix by keeping the
same authorization scope when loading the final page: either change
getPendingListingsByIds to accept the same filter arguments (emulatorIds,
canSeeBannedUsers, search/sort if needed) and call it with the same parameters
used for getPendingListingRiskCandidates, or avoid the re-query entirely by
selecting full listing fields in getPendingListingRiskCandidates and using those
results (so computeReviewRiskProfiles and attachReviewRiskProfiles still work).
Update calls around getPendingListingRiskCandidates, getPendingListingsByIds,
computeReviewRiskProfiles, and attachReviewRiskProfiles to ensure emulatorIds
and canSeeBannedUsers are preserved when producing paginatedPcListings.

---

Outside diff comments:
In `@src/server/repositories/pc-listings.repository.ts`:
- Around line 855-882: The PC listings method buildPendingListingsWhere applies
shadow-ban filtering when canSeeBannedUsers is false but the handheld equivalent
does not; align them by applying the same logic: compute const shadowBanFilter =
canSeeBannedUsers ? undefined : buildShadowBanFilter(null) and merge it into the
returned where clause as ...(shadowBanFilter && { author: shadowBanFilter }) (or
conversely remove that merge from buildPendingListingsWhere if you intend no
shadow-ban filtering), ensuring both repositories use the same approach for
canSeeBannedUsers and buildShadowBanFilter so moderator visibility is
consistent.

---

Nitpick comments:
In `@src/components/ui/ReviewRiskIndicator.test.tsx`:
- Around line 33-68: Add tests for the investigate interaction by rendering
ReviewRiskIndicator with a jest.fn() onInvestigate prop and asserting it is
called on mouse click and keyboard activation; locate the interactable element
via screen.getByRole('status') (or the same role used in the component), use
userEvent.click(status) to simulate mouse activation and
userEvent.keyboard('{Enter}') and userEvent.keyboard(' ') after status.focus()
to simulate Enter/Space keyboard activation, and assert onInvestigate was
invoked for each interaction; ensure the test covers both the case with
author/submission risk and the case with only submission risk so the click
target exists in both scenarios.

In `@src/server/api/routers/listings/admin.test.ts`:
- Around line 21-43: The inline mock function createExistingAuthorBansMap
duplicates production logic and should be extracted: move the implementation
into a shared test helper (e.g., tests/helpers or tests/utils) or import the
real helper if appropriate, then replace the inline definition in admin.test.ts
with an import of createExistingAuthorBansMap and update any other tests to
reuse that single exported function so logic remains centralized and in-sync
with production code.
- Around line 131-217: Add tests covering the non-risky and no-risky paths: in
src/server/api/routers/listings/admin.test.ts add a test calling
caller.getPending({ riskFilter: 'all', page: 1, limit: 20 }) that stubs
mockGetPendingListings to return expected rows and asserts
mockGetPendingListings was called (and mockGetPendingListingRiskCandidates was
not), verifies returned listings and pagination; also add a test for riskFilter:
'risky' when mockGetPendingListingRiskCandidates returns an empty array to
assert mockGetPendingListingsByIds is not called and result.listings.length and
pagination.total are 0. Ensure you reference and use the existing helpers/mocks
(createCaller, mockGetPendingListingRiskCandidates, mockGetPendingListingsByIds,
mockGetPendingListings) and mirror the style/assertions used in the existing
'filters risk-only listings' test.

In `@src/server/api/routers/pcListings.test.ts`:
- Around line 81-103: Extract the inline createExistingAuthorBansMap
implementation into a shared test utility function and have both tests import
and use it instead of duplicating the logic; specifically, create and export a
helper named createExistingAuthorBansMap that accepts the same listings shape
and returns a Map<string, {reason:string}[]>, then replace the inline
implementations in pcListings.test.ts and admin.test.ts with imports of that
helper so both tests call the single shared function and retain identical
behavior.
- Around line 477-561: The suite only tests the 'risky' riskFilter path; add
tests that exercise the non-risky/default path by calling caller.pending with
riskFilter: 'all' and with riskFilter omitted/undefined, verifying that
mockRepositoryGetPendingListings is used (not
getPendingListingRiskCandidates/getPendingListingsByIds), that returned
pcListings match the mocked pending listings and pagination.total reflects the
count, and add an edge-case test for empty results and a test using a
non-moderator role (e.g., Role.USER) to validate permission-based branches;
reference mocks and helpers like createCaller, caller.pending,
mockRepositoryGetPendingListings, mockRepositoryGetPendingListingRiskCandidates,
mockRepositoryGetPendingListingsByIds, mockComputeAuthorRiskProfiles and
mockComputeSubmissionRiskProfiles to seed expected responses and assertions.

In `@src/server/repositories/pc-listings.repository.ts`:
- Around line 890-894: Extract the duplicated active-ban predicate into a
private helper (e.g., private getActiveBanWhere()) that returns the object {
isActive: true, OR: [{ expiresAt: null }, { expiresAt: { gt: new Date() } }] },
add it to the PCListingsRepository class, and replace the inline userBans.where
in the forList include and the other occurrence (the block using userBans: {
where: { ... } }) to call getActiveBanWhere() so both queries reuse the same
logic for consistency.
🪄 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: e401bb9c-b655-46b0-ae0a-d291c6aea83a

📥 Commits

Reviewing files that changed from the base of the PR and between 5d32935 and 7eebded.

📒 Files selected for processing (32)
  • next.config.ts
  • package.json
  • src/app/admin/approvals/components/ApprovalModal.tsx
  • src/app/admin/approvals/page.tsx
  • src/app/admin/hooks/index.ts
  • src/app/admin/hooks/useReviewRiskFilter.ts
  • src/app/admin/pc-listing-approvals/components/ApprovalModal.tsx
  • src/app/admin/pc-listing-approvals/page.tsx
  • src/app/global-error.tsx
  • src/app/testing/page.tsx
  • src/components/admin/ReviewRiskFilterButton.tsx
  • src/components/admin/index.ts
  • src/components/ui/ReviewRiskIndicator.test.tsx
  • src/components/ui/ReviewRiskIndicator.tsx
  • src/components/ui/ReviewRiskWarningBanner.test.tsx
  • src/components/ui/ReviewRiskWarningBanner.tsx
  • src/components/ui/index.ts
  • src/schemas/listing.ts
  • src/schemas/pcListing.ts
  • src/schemas/submissionRisk.ts
  • src/server/api/routers/listings/admin.test.ts
  • src/server/api/routers/listings/admin.ts
  • src/server/api/routers/pcListings.test.ts
  • src/server/api/routers/pcListings.ts
  • src/server/repositories/listings.repository.ts
  • src/server/repositories/pc-listings.repository.ts
  • src/server/services/author-risk.service.ts
  • src/server/services/review-risk.service.test.ts
  • src/server/services/review-risk.service.ts
  • src/server/services/submission-risk.service.test.ts
  • src/server/services/submission-risk.service.ts
  • src/server/utils/query-builders.ts

Comment thread src/components/admin/ReviewRiskFilterButton.tsx Outdated
Comment thread src/server/api/routers/listings/admin.ts
Comment thread src/server/api/routers/pcListings.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/lib/cors.ts (1)

30-34: 💤 Low value

Optional: use a Set to dedupe instead of repeated includes scans.

addMissingOrigins does an O(n) includes per candidate, and getAllowedOrigins calls it 3–4 times. The list is small so this isn't a real performance issue, but a Set-based accumulator would be both cleaner and avoid the in-place mutation pattern.

♻️ Sketch
-function addMissingOrigins(origins: string[], additionalOrigins: string[]) {
-  for (const origin of additionalOrigins) {
-    if (!origins.includes(origin)) origins.push(origin)
-  }
-}
+function addMissingOrigins(origins: Set<string>, additionalOrigins: readonly string[]) {
+  for (const origin of additionalOrigins) origins.add(origin)
+}

Then build origins as a Set<string> in getAllowedOrigins and return [...origins] at the end.

🤖 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 `@src/lib/cors.ts` around lines 30 - 34, The current addMissingOrigins uses
repeated origins.includes scans and mutates an array; change the logic to use a
Set for deduplication: in getAllowedOrigins create a Set<string> (e.g., allowed
= new Set()), add the base origins and then add additional origins with Set.add
(or refactor addMissingOrigins to accept and add into a Set rather than an
array), and finally return Array.from(allowed) (or [...allowed]); update
references to addMissingOrigins/getAllowedOrigins accordingly to remove O(n)
includes and in-place array mutation.
🤖 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 `@src/lib/cors.test.ts`:
- Around line 21-27: The test titled "allows localhost during CI production-mode
E2E runs" does not stub NODE_ENV so it does not actually exercise
production-mode logic in getAllowedOrigins(); update the test to stub
NODE_ENV='production' (alongside CI='true', NEXT_PUBLIC_ALLOWED_ORIGINS='' and
ALLOWED_ORIGINS='') so getAllowedOrigins() hits the intended production branch
that adds LOCAL_TEST_ORIGINS, or alternatively rename the test to remove the
"production-mode" claim if you prefer not to change environment stubs.

In `@src/lib/cors.ts`:
- Around line 56-58: The current CI-only guard that calls
addMissingOrigins(origins, LOCAL_TEST_ORIGINS) should also verify NODE_ENV to
mirror defensive checks elsewhere; update the condition around addMissingOrigins
(the block that uses process.env.CI) to require process.env.NODE_ENV !==
'production' in addition to process.env.CI === 'true' so it only runs in
non-production CI/test environments and remains consistent with
threeDsGameRefreshService.ts and switchGameRefreshService.ts.

---

Nitpick comments:
In `@src/lib/cors.ts`:
- Around line 30-34: The current addMissingOrigins uses repeated
origins.includes scans and mutates an array; change the logic to use a Set for
deduplication: in getAllowedOrigins create a Set<string> (e.g., allowed = new
Set()), add the base origins and then add additional origins with Set.add (or
refactor addMissingOrigins to accept and add into a Set rather than an array),
and finally return Array.from(allowed) (or [...allowed]); update references to
addMissingOrigins/getAllowedOrigins accordingly to remove O(n) includes and
in-place array mutation.
🪄 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: 07cb3a01-0789-4a1e-a6d8-ad69b493edc9

📥 Commits

Reviewing files that changed from the base of the PR and between a02bd24 and df63733.

📒 Files selected for processing (2)
  • src/lib/cors.test.ts
  • src/lib/cors.ts

Comment thread src/lib/cors.test.ts Outdated
Comment thread src/lib/cors.ts Outdated
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