Skip to content

Server hardening#454

Merged
Producdevity merged 5 commits into
stagingfrom
feat/server-hardening
Jul 22, 2026
Merged

Server hardening#454
Producdevity merged 5 commits into
stagingfrom
feat/server-hardening

Conversation

@Producdevity

@Producdevity Producdevity commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Description

Hardens the web and mobile API paths and reduces unnecessary server work.

  • marks authenticated, errored, batched, non-GET, and non-allowlisted tRPC responses as private, no-store
  • emits shared-cache headers for a small allowlist of anonymous public GET lookups
  • standardizes mobile CORS headers, including API key and tRPC headers
  • rejects mobile browser requests with untrusted origin metadata while continuing to allow native clients that do not send origin headers
  • rejects invalid Authorization: ApiKey ... credentials while temporarily ignoring stale x-api-key values so existing mobile clients can fall through to Bearer or anonymous authentication
  • moves Steam batch lookup behavior into a shared service used by the mobile API and admin title ID tools
  • preserves the requested Steam App ID order and includes explicit not-found results
  • replaces background edit-permission polling with refresh-on-window-focus behavior
  • corrects the default sitemap URL to use www.emuready.com
  • updates the generated OpenAPI schema and mobile API documentation
  • adds tests for mobile authentication, proxy origin handling, CORS, tRPC cache policy, and Steam batch lookups

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactor
  • Other (please describe): Server hardening and load reduction

How Has This Been Tested?

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

Notes

The stale x-api-key fallback is temporary compatibility behavior for shipped EmuReadyApp clients. Invalid keys supplied through the explicit Authorization: ApiKey ... scheme are still rejected. The fallback should be removed once the mobile app no longer sends a stale x-api-key alongside Bearer authentication. This was a fuck up I made, i will likely wait for most users to update instead of forcing the app update (this is still in progress).

Public tRPC caching is intentionally limited to named anonymous GET queries. Requests containing cookies, authorization headers, API keys, errors, batches, mutations, or authenticated context remain private, no-store.

Summary by CodeRabbit

  • New Features
    • Added detailed batch Steam App ID lookup results, including match status, game information, performance data, devices, emulators, and listings.
    • Added API key authentication support through the x-api-key header.
  • Improvements
    • Increased device compatibility and batch lookup cache durations to 15 minutes.
    • Added clearer API documentation and examples.
    • Added request limits for search terms, names, device filters, and result collections.
    • Improved API caching and cross-origin request handling for eligible requests.

@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
emuready Ready Ready Preview, Comment Jul 19, 2026 9:05pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The changes centralize mobile API authentication, CORS, and tRPC caching; add bounded mobile schemas and a shared Steam batch lookup service; update admin lookup handling and generated API documentation; and adjust query refresh, catalog cache, sitemap, and test setup behavior.

Changes

Mobile API and Steam batch lookup

Layer / File(s) Summary
Request authentication, CORS, and origin handling
src/lib/cors.ts, src/proxy.ts, src/server/api/mobileContext.ts, src/app/api/mobile/trpc/[trpc]/route.ts, src/proxy.test.ts, src/lib/cors.test.ts, src/server/api/mobileContext.test.ts
CORS responses are centralized, mobile origin handling permits native and preflight requests, and API-key resolution distinguishes x-api-key from Authorization: ApiKey.
tRPC response caching and transport wiring
src/server/api/trpc-cache.ts, src/app/api/trpc/[trpc]/route.ts, src/app/api/mobile/trpc/[trpc]/route.ts, src/lib/api.tsx, next.config.ts, src/server/api/trpc-cache.test.ts
Eligible anonymous GET queries receive public cache headers, other responses remain private, CORS Vary values are merged, and client batches are limited to 20 items.
Steam batch lookup contract and execution
src/schemas/mobile.ts, src/server/services/steam-batch-lookup.service.ts, src/server/api/routers/mobile/games.ts, src/server/api/routers/admin/titleIdTools.ts, src/app/admin/title-id-tools/components/BatchSteamLookup.tsx, src/server/services/steam-batch-lookup.service.test.ts
A shared full/minimal batch response model now flows through cached lookup service execution, mobile and admin procedures, and normalized admin result rendering.
Mobile validation and generated API documentation
src/schemas/mobile.ts, src/scripts/api/generate-api-docs.ts, public/api-docs/mobile-openapi.json, docs/MOBILE_API.md
Mobile string, array, and numeric limits are documented in generated schemas, batch response examples use the expanded structure, authentication guidance includes x-api-key, and cache durations are updated to 15 minutes.

Application maintenance

Layer / File(s) Summary
Query refresh and application maintenance
src/app/admin/components/ApprovalCountBadge.tsx, src/app/listings/..., src/app/pc-listings/..., src/server/services/catalog.service.ts, src/app/sitemap.ts, src/app/admin/custom-field-templates/page.test.tsx
Approval and listing permission queries replace short polling with cache/focus behavior, catalog compatibility caching uses 900 seconds, sitemap fallback URLs use www, and a page test uses static import.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BatchSteamLookup
  participant titleIdToolsRouter
  participant lookupGamesBySteamAppIds
  participant steamBatchQueryCache
  participant GamesRepository
  BatchSteamLookup->>titleIdToolsRouter: submit batchSteamAppIds query
  titleIdToolsRouter->>lookupGamesBySteamAppIds: pass input and Prisma context
  lookupGamesBySteamAppIds->>steamBatchQueryCache: read batch cache
  lookupGamesBySteamAppIds->>GamesRepository: fetch matched games and listings
  GamesRepository-->>lookupGamesBySteamAppIds: return ordered results
  lookupGamesBySteamAppIds-->>titleIdToolsRouter: return batch response
  titleIdToolsRouter-->>BatchSteamLookup: render normalized rows
Loading

Suggested labels: security

Suggested reviewers: obfuscatedvoid

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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.
Title check ✅ Passed The title is concise and clearly reflects the main change: hardening server/mobile API behavior.
Description check ✅ Passed The description includes summary, change type, testing, and reviewer notes, but omits the issue link and some template sections.
✨ 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 feat/server-hardening
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/server-hardening

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.

@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 `@src/app/admin/components/ApprovalCountBadge.tsx`:
- Around line 33-49: Remove the explicit refetchOnMount and refetchOnWindowFocus
options from both listingStatsQuery and pcListingStatsQuery in
ApprovalCountBadge, preserving staleTime: CACHE_DURATIONS.SHORT and the existing
enabled conditions so React Query can refresh stale approval counts on mount and
window focus.

In `@src/app/admin/title-id-tools/components/BatchSteamLookup.tsx`:
- Around line 154-158: Update the copied-results summary in the BatchSteamLookup
result-formatting logic to replace the user-facing “listings” wording with the
appropriate report terminology, including the unavailable case. Preserve the
existing Steam app ID, title, and match-strategy formatting.

In `@src/proxy.ts`:
- Around line 81-84: Restrict the no-origin bypass in hasRequestOriginMetadata
and the mobile endpoint handling to the dedicated /api/mobile/trpc/* route. Do
not let isMobileTRPCPath match /api/trpc/mobile.* requests for this relaxation;
preserve their existing origin validation or INTERNAL_API_KEY fallback,
including mutations and mixed batches.

In `@src/scripts/api/generate-api-docs.ts`:
- Around line 40-81: Update getResponseExampleOverride for
BatchBySteamAppIdsResponseSchema to represent the default minimal: false
response shape, including steamAppId, game, matchStrategy, and nested
system/listings fields; alternatively clearly label it as the minimal: true
variant. Regenerate public/api-docs/mobile-openapi.json at lines 7655-7689 so
the endpoint success example matches the corrected default shape.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ddb9018f-f253-4cdf-a515-bf75a3f283a7

📥 Commits

Reviewing files that changed from the base of the PR and between 05b39fd and 20c5f32.

📒 Files selected for processing (31)
  • docs/MOBILE_API.md
  • next.config.ts
  • public/api-docs/mobile-openapi.json
  • src/app/admin/components/ApprovalCountBadge.test.tsx
  • src/app/admin/components/ApprovalCountBadge.tsx
  • src/app/admin/custom-field-templates/page.test.tsx
  • src/app/admin/title-id-tools/components/BatchSteamLookup.tsx
  • src/app/api/mobile/trpc/[trpc]/route.ts
  • src/app/api/trpc/[trpc]/route.ts
  • src/app/listings/[id]/components/EditListingButton.tsx
  • src/app/pc-listings/[id]/components/EditPcListingButton.tsx
  • src/app/sitemap.ts
  • src/lib/api.tsx
  • src/lib/cors.test.ts
  • src/lib/cors.ts
  • src/proxy.test.ts
  • src/proxy.ts
  • src/schemas/mobile.ts
  • src/scripts/api/generate-api-docs.ts
  • src/server/api/mobileContext.test.ts
  • src/server/api/mobileContext.ts
  • src/server/api/routers/admin/titleIdTools.ts
  • src/server/api/routers/mobile/catalog.ts
  • src/server/api/routers/mobile/games.ts
  • src/server/api/trpc-cache.test.ts
  • src/server/api/trpc-cache.ts
  • src/server/services/catalog.service.test.ts
  • src/server/services/catalog.service.ts
  • src/server/services/steam-batch-lookup.service.test.ts
  • src/server/services/steam-batch-lookup.service.ts
  • src/server/utils/cache/instances.ts
💤 Files with no reviewable changes (1)
  • next.config.ts

Comment on lines +33 to +49
staleTime: CACHE_DURATIONS.SHORT,
refetchOnMount: false,
refetchOnWindowFocus: false,
})

const listingStatsQuery = api.listings.stats.useQuery(undefined, {
enabled: canViewStats && props.href === '/admin/approvals',
refetchInterval: POLLING_INTERVALS.SHORT,
staleTime: CACHE_DURATIONS.VERY_SHORT,
refetchOnMount: true,
refetchOnWindowFocus: true,
staleTime: CACHE_DURATIONS.SHORT,
refetchOnMount: false,
refetchOnWindowFocus: false,
})

const pcListingStatsQuery = api.pcListings.stats.useQuery(undefined, {
enabled: canViewStats && props.href === '/admin/pc-listing-approvals',
refetchInterval: POLLING_INTERVALS.SHORT,
staleTime: CACHE_DURATIONS.VERY_SHORT,
refetchOnMount: true,
refetchOnWindowFocus: true,
staleTime: CACHE_DURATIONS.SHORT,
refetchOnMount: false,
refetchOnWindowFocus: 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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove refetchOnMount and refetchOnWindowFocus to allow stale data to refresh.

By setting refetchOnMount: false and refetchOnWindowFocus: false, these queries will never automatically refetch once they have initial data, even after the staleTime has expired. The staleTime: CACHE_DURATIONS.SHORT setting alone is sufficient to prevent unnecessary requests while the data is fresh.

Consider removing these explicit false flags. This allows React Query's default behavior to fetch updated approval counts when a user navigates back to the admin area or refocuses the window after the cache has become stale.

♻️ Proposed fix
   const gameStatsQuery = api.games.stats.useQuery(undefined, {
     enabled: canViewStats && props.href === '/admin/games/approvals',
     staleTime: CACHE_DURATIONS.SHORT,
-    refetchOnMount: false,
-    refetchOnWindowFocus: false,
   })
 
   const listingStatsQuery = api.listings.stats.useQuery(undefined, {
     enabled: canViewStats && props.href === '/admin/approvals',
     staleTime: CACHE_DURATIONS.SHORT,
-    refetchOnMount: false,
-    refetchOnWindowFocus: false,
   })
 
   const pcListingStatsQuery = api.pcListings.stats.useQuery(undefined, {
     enabled: canViewStats && props.href === '/admin/pc-listing-approvals',
     staleTime: CACHE_DURATIONS.SHORT,
-    refetchOnMount: false,
-    refetchOnWindowFocus: false,
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
staleTime: CACHE_DURATIONS.SHORT,
refetchOnMount: false,
refetchOnWindowFocus: false,
})
const listingStatsQuery = api.listings.stats.useQuery(undefined, {
enabled: canViewStats && props.href === '/admin/approvals',
refetchInterval: POLLING_INTERVALS.SHORT,
staleTime: CACHE_DURATIONS.VERY_SHORT,
refetchOnMount: true,
refetchOnWindowFocus: true,
staleTime: CACHE_DURATIONS.SHORT,
refetchOnMount: false,
refetchOnWindowFocus: false,
})
const pcListingStatsQuery = api.pcListings.stats.useQuery(undefined, {
enabled: canViewStats && props.href === '/admin/pc-listing-approvals',
refetchInterval: POLLING_INTERVALS.SHORT,
staleTime: CACHE_DURATIONS.VERY_SHORT,
refetchOnMount: true,
refetchOnWindowFocus: true,
staleTime: CACHE_DURATIONS.SHORT,
refetchOnMount: false,
refetchOnWindowFocus: false,
const gameStatsQuery = api.games.stats.useQuery(undefined, {
enabled: canViewStats && props.href === '/admin/games/approvals',
staleTime: CACHE_DURATIONS.SHORT,
})
const listingStatsQuery = api.listings.stats.useQuery(undefined, {
enabled: canViewStats && props.href === '/admin/approvals',
staleTime: CACHE_DURATIONS.SHORT,
})
const pcListingStatsQuery = api.pcListings.stats.useQuery(undefined, {
enabled: canViewStats && props.href === '/admin/pc-listing-approvals',
staleTime: CACHE_DURATIONS.SHORT,
})
🤖 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/app/admin/components/ApprovalCountBadge.tsx` around lines 33 - 49, Remove
the explicit refetchOnMount and refetchOnWindowFocus options from both
listingStatsQuery and pcListingStatsQuery in ApprovalCountBadge, preserving
staleTime: CACHE_DURATIONS.SHORT and the existing enabled conditions so React
Query can refresh stale approval counts on mount and window focus.

Comment on lines +154 to +158
const listingSummary =
displayResult.listingCount === null
? 'listings unavailable'
: `${displayResult.listingCount} listings`
return `${displayResult.steamAppId}: ${displayResult.title} (${displayResult.matchStrategy}, ${listingSummary})`

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 | 🟡 Minor | ⚡ Quick win

User-facing wording uses "listings". As per coding guidelines ("User-facing UI should use 'Compatibility Report', 'Handheld Report', 'PC Report', or 'PC Compatibility Report', not 'listing'"), the copied-results summary should use the report terminology rather than "listings".

Proposed wording change
-        const listingSummary =
-          displayResult.listingCount === null
-            ? 'listings unavailable'
-            : `${displayResult.listingCount} listings`
+        const listingSummary =
+          displayResult.listingCount === null
+            ? 'reports unavailable'
+            : `${displayResult.listingCount} compatibility reports`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const listingSummary =
displayResult.listingCount === null
? 'listings unavailable'
: `${displayResult.listingCount} listings`
return `${displayResult.steamAppId}: ${displayResult.title} (${displayResult.matchStrategy}, ${listingSummary})`
const listingSummary =
displayResult.listingCount === null
? 'reports unavailable'
: `${displayResult.listingCount} compatibility reports`
return `${displayResult.steamAppId}: ${displayResult.title} (${displayResult.matchStrategy}, ${listingSummary})`
🤖 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/app/admin/title-id-tools/components/BatchSteamLookup.tsx` around lines
154 - 158, Update the copied-results summary in the BatchSteamLookup
result-formatting logic to replace the user-facing “listings” wording with the
appropriate report terminology, including the unavailable case. Preserve the
existing Steam app ID, title, and match-strategy formatting.

Source: Coding guidelines

Comment thread src/proxy.ts
Comment on lines +81 to +84
function hasRequestOriginMetadata(req: NextRequest): boolean {
return Boolean(req.headers.get('origin') || req.headers.get('referer'))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check what procedures exist under the mobile router namespace, and whether any
# are mutations (which would be reachable via /api/trpc/mobile.* without origin/API-key checks
# when Origin/Referer are absent).
fd -e ts -p 'src/server/api/routers/mobile' --exec ast-grep run --pattern 'mutation($$$)' --lang typescript {} \;

Repository: Producdevity/EmuReady

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== proxy.ts relevant lines =="
nl -ba src/proxy.ts | sed -n '1,240p'

echo
echo "== locate mobile router files =="
fd -a 'mobile' src/server/api/routers || true

echo
echo "== search for mobile procedures and router definitions =="
rg -n "router\\(|mutation\\(|query\\(|mobile\\." src/server/api/routers src/server/api -g '*.ts' -g '*.tsx' || true

Repository: Producdevity/EmuReady

Length of output: 229


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== proxy.ts relevant lines =="
cat -n src/proxy.ts | sed -n '1,240p'

echo
echo "== repo files for mobile router =="
git ls-files 'src/server/api/routers/**' | sed -n '/mobile/p'

echo
echo "== mobile-related procedures =="
rg -n "mobile\\.|router\\(|mutation\\(|query\\(" src/server/api/routers src/server/api -g '*.ts' -g '*.tsx' || true

Repository: Producdevity/EmuReady

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== app router / mobile router composition =="
git ls-files 'src/server/api/*.ts' 'src/server/api/**/*.ts' | rg 'src/server/api/(root|trpc|routers/mobile)\.ts$' || true

echo
echo "== outline of root router file =="
ast-grep outline src/server/api/root.ts --view expanded || true

echo
echo "== outline of mobile router file =="
ast-grep outline src/server/api/routers/mobile.ts --view expanded || true

echo
echo "== read relevant slices =="
for f in src/server/api/root.ts src/server/api/routers/mobile.ts; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    cat -n "$f" | sed -n '1,220p'
  fi
done

Repository: Producdevity/EmuReady

Length of output: 7934


Scope the no-origin bypass to the dedicated mobile endpoint (src/proxy.ts:112-164)

isMobileTRPCPath also matches /api/trpc/mobile.*, so requests to the web tRPC namespace skip origin validation whenever Origin/Referer is missing. That widens the relaxation to the whole mobile router, including mutations and mixed batches. Keep this bypass limited to /api/mobile/trpc/*, or preserve the INTERNAL_API_KEY fallback for /api/trpc/mobile.*.

🤖 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/proxy.ts` around lines 81 - 84, Restrict the no-origin bypass in
hasRequestOriginMetadata and the mobile endpoint handling to the dedicated
/api/mobile/trpc/* route. Do not let isMobileTRPCPath match /api/trpc/mobile.*
requests for this relaxation; preserve their existing origin validation or
INTERNAL_API_KEY fallback, including mutations and mixed batches.

Comment on lines +40 to +81
function getResponseExampleOverride(outputSchemaName: string | undefined): unknown | null {
if (outputSchemaName !== 'BatchBySteamAppIdsResponseSchema') return null

return {
success: true,
results: [
{
game_id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
steam_app_id: '220',
title: 'Half-Life 2',
performance: {
id: 1,
label: 'Perfect',
rank: 1,
description: null,
},
emulator: {
id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
name: 'GameHub',
logo: null,
},
device: {
id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
modelName: 'Steam Deck',
soc: null,
},
listing: {
id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
notes: 'Runs well',
upvoteCount: 4,
downvoteCount: 1,
voteCount: 5,
successRate: 0.8,
},
},
],
totalRequested: 1,
totalFound: 1,
totalNotFound: 0,
}
}

@coderabbitai coderabbitai Bot Jul 19, 2026

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 | 🟡 Minor | ⚡ Quick win

Batch lookup success example documents the minimal result shape, but the default response is the full shape. BatchBySteamAppIdsSchema.minimal defaults to false, so default calls return full results (steamAppId, game, matchStrategy, nested system/listings), not the minimal shape shown.

  • src/scripts/api/generate-api-docs.ts#L40-L81: change getResponseExampleOverride to emit a full-shape example (matching minimal: false), or annotate it as the minimal: true variant.
  • public/api-docs/mobile-openapi.json#L7655-L7689: regenerate so the endpoint's success example reflects the corrected default shape.
📍 Affects 2 files
  • src/scripts/api/generate-api-docs.ts#L40-L81 (this comment)
  • public/api-docs/mobile-openapi.json#L7655-L7689
🤖 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/scripts/api/generate-api-docs.ts` around lines 40 - 81, Update
getResponseExampleOverride for BatchBySteamAppIdsResponseSchema to represent the
default minimal: false response shape, including steamAppId, game,
matchStrategy, and nested system/listings fields; alternatively clearly label it
as the minimal: true variant. Regenerate public/api-docs/mobile-openapi.json at
lines 7655-7689 so the endpoint success example matches the corrected default
shape.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@coderabbitai create a new issue for this

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

gh issue create \
  --repo Producdevity/EmuReady \
  --title "Document the default full Steam batch lookup response example" \
  --assignee "Producdevity" \
  --body "$(cat <<'EOF'
## Summary

The generated OpenAPI success example for `games.batchBySteamAppIds` currently shows the minimal result variant, while `BatchBySteamAppIdsSchema.minimal` defaults to `false` and the default response is the full result shape.

## Required changes

- Update `getResponseExampleOverride` in `src/scripts/api/generate-api-docs.ts` so the `BatchBySteamAppIdsResponseSchema` example represents the default full response shape.
- Include the full-result fields such as `steamAppId`, `game`, `matchStrategy`, and the nested `system` and `listings` data.
- Alternatively, explicitly label the current example as applying to `minimal: true`.
- Regenerate `public/api-docs/mobile-openapi.json` so the documented endpoint success example matches the selected behavior.

## Rationale

API consumers should be able to rely on the default request/response examples. Documenting the minimal response as the default can lead clients to model the wrong contract.

## Affected areas

- `src/scripts/api/generate-api-docs.ts`
- `public/api-docs/mobile-openapi.json`

## Acceptance criteria

- The generated example accurately reflects `minimal: false`, or is unambiguously documented as a `minimal: true` example.
- The OpenAPI JSON is regenerated from the updated generator.
- The endpoint documentation and response schema/example are consistent.

## Backlinks

- Pull request: https://github.com/Producdevity/EmuReady/pull/454
- Review comment: https://github.com/Producdevity/EmuReady/pull/454#discussion_r3611376667
- Requested by: `@Producdevity`
EOF
)"

Length of output: 1952


@Producdevity, created and assigned follow-up issue #457: Document the default full Steam batch lookup response example.

@Producdevity
Producdevity merged commit 32ef4dc into staging Jul 22, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant