Skip to content

Add API key console#553

Closed
antoncoding wants to merge 3 commits into
masterfrom
codex/api-key-console
Closed

Add API key console#553
antoncoding wants to merge 3 commits into
masterfrom
codex/api-key-console

Conversation

@antoncoding
Copy link
Copy Markdown
Owner

@antoncoding antoncoding commented May 29, 2026

Summary

  • Add a wallet-signed API key console at /api-keys.
  • Harden API key creation with object-body validation, bounded gateway calls, contract-wallet signature verification, and signed nonce metadata.
  • Remove the misleading key test route/UI and repair the Ultracite/Biome setup so lint config parses cleanly.

Verification

  • npx ultracite fix
  • npx ultracite check
  • pnpm lint:check
  • pnpm typecheck
  • pnpm build
  • git diff --check
  • npm run typecheck in monarch-data-gateway-worker

Summary by CodeRabbit

  • New Features

    • API Key Console: Generate API keys by signing requests with your connected wallet. Access the new console from the navigation menu (desktop and mobile).
  • Improvements

    • Enhanced security attributes on external links.
  • Documentation

    • Updated technical overview with API Key Console documentation, including signed wallet verification and freshness validation details.

Review Change Stack

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 29, 2026

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

Project Deployment Actions Updated (UTC)
monarch Ready Ready Preview, Comment May 29, 2026 12:41pm

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an API Key Console feature to Monarch, allowing users to generate and test API keys by signing a wallet proof. It adds the /api-keys page, the backend routes POST /api/api-keys and POST /api/api-keys/test, and updates documentation and dependencies. Feedback on these changes highlights several opportunities to improve robustness, including handling null JSON bodies to prevent runtime crashes, wrapping external fetch calls in try/catch blocks for graceful error handling, providing a fallback for crypto.randomUUID in non-secure contexts, and relaxing signature validation to support smart contract wallets.

Comment thread app/api/api-keys/route.ts
Comment on lines +88 to +93
let body: CreateApiKeyRequestBody;
try {
body = (await request.json()) as CreateApiKeyRequestBody;
} catch {
return { error: 'Invalid JSON body.' };
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If the request body is null (which is valid JSON), request.json() will return null. Accessing properties on body afterwards will throw a TypeError and crash the route handler. It is safer to verify that body is a non-null object before proceeding.

Suggested change
let body: CreateApiKeyRequestBody;
try {
body = (await request.json()) as CreateApiKeyRequestBody;
} catch {
return { error: 'Invalid JSON body.' };
}
let body: CreateApiKeyRequestBody;
try {
body = (await request.json()) as CreateApiKeyRequestBody;
if (!body || typeof body !== 'object') {
return { error: 'Invalid JSON body.' };
}
} catch {
return { error: 'Invalid JSON body.' };
}

Comment thread app/api/api-keys/test/route.ts Outdated
Comment on lines +53 to +58
let body: TestApiKeyRequestBody;
try {
body = (await request.json()) as TestApiKeyRequestBody;
} catch {
return { error: 'Invalid JSON body.' };
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If the request body is null, request.json() will return null. Accessing body.apiKey will then throw a TypeError. We should ensure body is a valid object before accessing its properties.

  let body: TestApiKeyRequestBody;
  try {
    body = (await request.json()) as TestApiKeyRequestBody;
    if (!body || typeof body !== 'object') {
      return { error: 'Invalid JSON body.' };
    }
  } catch {
    return { error: 'Invalid JSON body.' };
  }

Comment thread app/api/api-keys/route.ts Outdated
Comment on lines +130 to +149
const response = await fetch(adminEndpoint, {
method: 'POST',
headers: {
Authorization: `Bearer ${adminToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name,
environment: 'live',
scopes: ['data.read', 'indexer.query'],
tier: 'free',
rateLimitTier: 'free',
metadata: {
ownerAddress: address,
origin,
signedAt: issuedAt,
createdBy: 'monarch-api-key-console',
},
}),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The fetch call to the external admin endpoint is not wrapped in a try/catch block. If the gateway is temporarily down or a network error occurs, fetch will throw an unhandled exception, causing a 500 error. Wrapping it in a try/catch allows returning a clean 502 Bad Gateway response.

  let response: Response;
  try {
    response = await fetch(adminEndpoint, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${adminToken}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        name,
        environment: 'live',
        scopes: ['data.read', 'indexer.query'],
        tier: 'free',
        rateLimitTier: 'free',
        metadata: {
          ownerAddress: address,
          origin,
          signedAt: issuedAt,
          createdBy: 'monarch-api-key-console',
        },
      }),
    });
  } catch (error) {
    return NextResponse.json({ error: 'Failed to connect to the API gateway.' }, { status: 502 });
  }

Comment thread app/api/api-keys/test/route.ts Outdated
Comment on lines +22 to +29
const response = await fetch(TEST_ENDPOINT, {
method: 'GET',
headers: {
Accept: 'application/json',
'X-API-Key': apiKey.value,
},
cache: 'no-store',
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The fetch call to the test endpoint is not wrapped in a try/catch block. If the endpoint is down or a network error occurs, it will throw an unhandled exception. Wrapping it in a try/catch ensures a graceful 502 response.

Suggested change
const response = await fetch(TEST_ENDPOINT, {
method: 'GET',
headers: {
Accept: 'application/json',
'X-API-Key': apiKey.value,
},
cache: 'no-store',
});
let response: Response;
try {
response = await fetch(TEST_ENDPOINT, {
method: 'GET',
headers: {
Accept: 'application/json',
'X-API-Key': apiKey.value,
},
cache: 'no-store',
});
} catch (error) {
return NextResponse.json(
{
ok: false,
status: 502,
error: 'Failed to connect to the test endpoint.',
},
{ status: 502 }
);
}

wallet: normalizedAddress,
origin: window.location.origin,
issuedAt: new Date().toISOString(),
nonce: crypto.randomUUID(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

crypto.randomUUID is only available in secure contexts (HTTPS or localhost). If the app is accessed over HTTP in non-localhost environments (e.g., local network testing or staging), this will throw a TypeError and crash the key generation flow. Providing a simple fallback prevents this.

Suggested change
nonce: crypto.randomUUID(),
nonce: typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2),

Comment thread app/api/api-keys/route.ts Outdated
Comment on lines +104 to +106
if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) {
return { error: 'Invalid signature format.' };
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The strict regex check /^0x[0-9a-fA-F]{130}$/ limits signatures to exactly 65 bytes, which blocks smart contract wallets (like Safe) that use ERC-1271 signatures of arbitrary lengths. Relaxing this regex to allow any valid hex string and passing a publicClient to verifyMessage will enable support for smart contract wallets, which is highly recommended for a DeFi dashboard.

Suggested change
if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) {
return { error: 'Invalid signature format.' };
}
if (!/^0x[0-9a-fA-F]+$/.test(signature)) {
return { error: 'Invalid signature format.' };
}

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

📝 Walkthrough

Walkthrough

Adds a complete API key management system allowing users to generate keys by signing a wallet message, with server-side validation and EIP-191 signature verification. Includes a console UI, navigation integration, hook rename for clarity, and widespread formatting improvements.

Changes

API Key Management Feature

Layer / File(s) Summary
API key request message type and utilities
src/utils/apiKeyRequest.ts, .env.local.example
ApiKeyRequestMessage type includes chainId, wallet, origin, issuedAt, and nonce. Utilities build and parse this fixed-format message. Environment variables added for admin token and optional gateway URL.
API key creation route with signature verification
app/api/api-keys/route.ts
POST handler parses signed requests, validates wallet signature via EIP-191, checks timestamp freshness and origin match, verifies supported chainId, then forwards to Monarch admin gateway; returns 201 with key or mapped error codes for validation/signature/timeout failures.
API key console component
src/features/api-keys/api-key-console-view.tsx
React component manages wallet connection, builds and signs request message, submits to POST /api/api-keys, displays one-time created key with copy button, shows errors, and provides loading/disabled states for signing and creation.
API Keys page and navigation
app/api-keys/page.tsx, src/components/layout/header/Navbar.tsx, src/components/layout/header/NavbarMobile.tsx
Creates /api-keys page with metadata and integrates "API Keys" menu item in both desktop and mobile navigation.

Hook Rename and Security

Layer / File(s) Summary
Rename use4626VaultAPR to useErc4626VaultAPR
src/hooks/use4626VaultAPR.ts, src/modals/leverage/components/add-collateral-and-leverage.tsx, docs/TECHNICAL_OVERVIEW.md
Exported hook renamed for clarity. All call sites and documentation updated.
Add rel=noopener noreferrer to external links
src/features/markets/components/market-details-block.tsx, src/features/markets/components/table/market-table-utils.tsx
External links now include security attributes alongside target="_blank".

Configuration, Dependencies, and Documentation

Layer / File(s) Summary
Biome linter and formatter configuration
biome.jsonc
Adjusts formatter.lineWidth comment, disables additional linter rules (noNegationElse, useConsistentTypeDefinitions, useCollapsedIf, noNoninteractiveTabindex), removes old nursery disables, adds formatter override for app/global.css.
Dependency version updates
package.json
Bumps @biomejs/biome to 2.3.10 and ultracite to 6.5.1.
Documentation updates
docs/TECHNICAL_OVERVIEW.md, docs/VALIDATIONS.md
Clarifies "no app-owned backend database", updates GraphQL endpoint to indexer.monarchlend.xyz, documents API key console flow with signature verification, updates hook name in migration checklist, adds validation instructions.

Code Formatting and Style Consolidation

Layer / File(s) Summary
Widespread formatting improvements
~40 files across src/, app/
Collapses multi-line expressions to single-line where readable, expands conditionals for clarity, normalizes imports/exports, updates indentation and array formatting without changing behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • antoncoding/monarch#202: Modifies NavbarMobile.tsx dropdown menu items with similar UI pattern as the API Keys navigation added here.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.51% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add API key console' clearly and concisely summarizes the main addition: a new API key management feature at /api-keys, which is the primary change in this changeset.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/api-key-console

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.

@coderabbitai coderabbitai Bot added feature request Specific feature ready to be implemented ui User interface labels May 29, 2026
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: 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 `@app/api/api-keys/route.ts`:
- Around line 54-74: parsedMessage.nonce is only validated but not consumed,
allowing replay of the same signed payload to create multiple keys; before
calling createGatewayApiKey, check a server-side nonce store (or DB table) keyed
by {address, nonce} and atomically mark the nonce as used (or insert a unique
{address, nonce} row) so subsequent requests with the same nonce are rejected,
or alternatively modify createGatewayApiKey to be idempotent on {address, nonce}
by returning the existing key if the pair already exists; ensure this
check/insert is performed after verifyMessage succeeds and before
createGatewayApiKey is invoked to prevent race conditions.
- Around line 129-151: The admin fetch to adminEndpoint (creating the POST with
Authorization, body and casting to AdminCreateApiKeyResponse) can hang or throw;
wrap the fetch in an AbortController-based timeout and put the whole network
call and response.json() inside a try/catch so any fetch/network/timeout error
is caught and you return a JSON failure payload instead of letting the route
throw; specifically, create an AbortController, set a timeout to call
controller.abort(), pass controller.signal to fetch, await response and
response.json() inside the try block, and in the catch return a consistent error
object (or set a failure body) so callers always receive JSON even on network
errors or timeouts.

In `@app/api/api-keys/test/route.ts`:
- Around line 22-39: The route's fetch to TEST_ENDPOINT (in
app/api/api-keys/test/route.ts) is unbounded and not wrapped in a try/catch, so
network/timeouts will throw and produce unhandled 500s; update the handler that
calls fetch(TEST_ENDPOINT, ...) to perform the request inside a try/catch and
use an AbortController timeout (or equivalent) to bound the call, then on any
fetch/network/timeout error return a stable JSON response via NextResponse.json
with ok: false, a suitable status (e.g., 502/504), and an explanatory error
message instead of letting the exception bubble; keep references to
TEST_ENDPOINT, MarketMetricsResponse, and the existing NextResponse.json
response shape so consumer parsing remains consistent.

In `@src/features/markets/components/table/market-table-utils.tsx`:
- Line 55: Update the external anchor(s) that currently set rel="noopener" (in
src/features/markets/components/table/market-table-utils.tsx) to include
noreferrer as well so they read rel="noopener noreferrer"; search for
occurrences of rel="noopener" or anchors with target="_blank" (e.g., the link
rendering in the market table util helper) and add "noreferrer" to the rel
attribute to prevent referrer leakage and ensure noopener protection.
🪄 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: CHILL

Plan: Pro

Run ID: c8c31df5-d77a-4824-bb35-d44bbb2a9c03

📥 Commits

Reviewing files that changed from the base of the PR and between 9b45d28 and 216acc6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (50)
  • .env.local.example
  • app/api-keys/page.tsx
  • app/api/api-keys/route.ts
  • app/api/api-keys/test/route.ts
  • app/fonts.ts
  • biome.jsonc
  • docs/TECHNICAL_OVERVIEW.md
  • docs/VALIDATIONS.md
  • package.json
  • src/components/layout/header/Navbar.tsx
  • src/components/layout/header/NavbarMobile.tsx
  • src/components/shared/account-actions-popover.tsx
  • src/components/shared/account-identity.tsx
  • src/components/shared/transaction-identity.tsx
  • src/contexts/VaultRegistryContext.tsx
  • src/data-sources/monarch-api/markets.ts
  • src/data-sources/monarch-api/vaults.ts
  • src/data-sources/morpho-api/vault-share-price-history.ts
  • src/data-sources/morpho-api/vaults.ts
  • src/features/api-keys/api-key-console-view.tsx
  • src/features/autovault/components/vault-detail/vault-market-allocations.tsx
  • src/features/autovault/components/vault-identity.tsx
  • src/features/markets/components/market-details-block.tsx
  • src/features/markets/components/markets-table-same-loan.tsx
  • src/features/markets/components/table/market-table-body.tsx
  • src/features/markets/components/table/market-table-utils.tsx
  • src/features/markets/components/table/markets-table.tsx
  • src/features/position-detail/components/report-range-picker.tsx
  • src/features/position-detail/hooks/usePositionDetailData.ts
  • src/features/positions/components/allocation-cell.tsx
  • src/features/positions/components/markets-filter-compact.tsx
  • src/features/positions/components/portfolio-analytics-banner.tsx
  • src/features/positions/components/user-vaults-table.tsx
  • src/features/vault/components/vault-market-allocations-table.tsx
  • src/features/vault/components/vault-share-price-chart.tsx
  • src/features/vault/vault-view.tsx
  • src/hooks/queries/useMarketV2SupplyingVaultsQuery.ts
  • src/hooks/use4626VaultAPR.ts
  • src/hooks/useEffectiveTrustedVaults.ts
  • src/hooks/useFilteredMarkets.ts
  • src/hooks/useTrustedVaultMetadata.ts
  • src/hooks/useUserPositions.ts
  • src/hooks/useVaultAllocations.ts
  • src/hooks/useVaultSharePriceHistory.ts
  • src/modals/leverage/components/add-collateral-and-leverage.tsx
  • src/modals/settings/monarch-settings/details/TrustedVaultsDetail.tsx
  • src/modals/vault/vault-withdraw-modal.tsx
  • src/stores/usePositionsFilters.ts
  • src/utils/apiKeyRequest.ts
  • src/utils/portfolio.ts

Comment thread app/api/api-keys/route.ts
Comment on lines +54 to +74
if (!/^[A-Za-z0-9-]{16,80}$/.test(parsedMessage.nonce)) {
return NextResponse.json({ error: 'Invalid signature nonce.' }, { status: 400 });
}

const signatureValid = await verifyMessage({
address,
message: body.message,
signature: body.signature as `0x${string}`,
});

if (!signatureValid) {
return NextResponse.json({ error: 'Invalid wallet signature.' }, { status: 401 });
}

const adminResponse = await createGatewayApiKey({
adminToken,
address,
name: body.name,
origin: parsedMessage.origin,
issuedAt: parsedMessage.issuedAt,
});
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 | 🏗️ Heavy lift

Consume the nonce after first use.

parsedMessage.nonce is only shape-checked here. The same signed payload can be replayed for the full TTL and create multiple keys, so one captured signature becomes a reusable create token. Reject reused nonces server-side, or make the admin write idempotent on {address, nonce} before calling upstream.

🤖 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 `@app/api/api-keys/route.ts` around lines 54 - 74, parsedMessage.nonce is only
validated but not consumed, allowing replay of the same signed payload to create
multiple keys; before calling createGatewayApiKey, check a server-side nonce
store (or DB table) keyed by {address, nonce} and atomically mark the nonce as
used (or insert a unique {address, nonce} row) so subsequent requests with the
same nonce are rejected, or alternatively modify createGatewayApiKey to be
idempotent on {address, nonce} by returning the existing key if the pair already
exists; ensure this check/insert is performed after verifyMessage succeeds and
before createGatewayApiKey is invoked to prevent race conditions.

Comment thread app/api/api-keys/route.ts Outdated
Comment thread app/api/api-keys/test/route.ts Outdated
Comment on lines +22 to +39
const response = await fetch(TEST_ENDPOINT, {
method: 'GET',
headers: {
Accept: 'application/json',
'X-API-Key': apiKey.value,
},
cache: 'no-store',
});

const body = (await response.json().catch(() => ({}))) as MarketMetricsResponse;
if (!response.ok) {
return NextResponse.json(
{
ok: false,
status: response.status,
error: typeof body.error === 'string' ? body.error : 'API key test failed.',
},
{ status: response.status },
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 | ⚡ Quick win

Bound and catch the test fetch.

This upstream call has no timeout or network error handling, so transient gateway failures will bubble out as unhandled 500s. Wrap it and return a stable JSON error from this route instead.

🤖 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 `@app/api/api-keys/test/route.ts` around lines 22 - 39, The route's fetch to
TEST_ENDPOINT (in app/api/api-keys/test/route.ts) is unbounded and not wrapped
in a try/catch, so network/timeouts will throw and produce unhandled 500s;
update the handler that calls fetch(TEST_ENDPOINT, ...) to perform the request
inside a try/catch and use an AbortController timeout (or equivalent) to bound
the call, then on any fetch/network/timeout error return a stable JSON response
via NextResponse.json with ok: false, a suitable status (e.g., 502/504), and an
explanatory error message instead of letting the exception bubble; keep
references to TEST_ENDPOINT, MarketMetricsResponse, and the existing
NextResponse.json response shape so consumer parsing remains consistent.

Comment thread src/features/markets/components/table/market-table-utils.tsx Outdated
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.

Caution

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

⚠️ Outside diff range comments (2)
src/features/api-keys/api-key-console-view.tsx (1)

100-105: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle clipboard failures.

navigator.clipboard.writeText can reject when clipboard access is blocked or unavailable. Right now the copy button can throw an unhandled client error.

Proposed fix
   const handleCopy = async () => {
     if (!createdKey) return;
 
-    await navigator.clipboard.writeText(createdKey.apiKey);
-    setCopied(true);
+    try {
+      await navigator.clipboard.writeText(createdKey.apiKey);
+      setCopied(true);
+    } catch {
+      setCopied(false);
+      setCreationError('Copy failed. Please copy the key manually.');
+    }
   };
🤖 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/features/api-keys/api-key-console-view.tsx` around lines 100 - 105, The
handleCopy function can throw if navigator.clipboard.writeText rejects; wrap the
await call in a try/catch around
navigator.clipboard.writeText(createdKey.apiKey) inside handleCopy, handle the
error by logging it (console.error or processLogger), setCopied(false) or set an
error state to show the user a failure message, and optionally fall back to a
document.execCommand('copy') or selecting a temporary textarea to copy; ensure
you still return early if !createdKey and reference the handleCopy function,
createdKey, and setCopied when implementing the fix.
app/api/api-keys/route.ts (1)

225-231: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mark the API key response as non-cacheable.

This payload contains the one-time secret, but the success response does not send Cache-Control: no-store. Add it here so browsers and intermediaries do not retain the key.

Proposed fix
   return NextResponse.json(
     {
       apiKey: body.apiKey,
       key: body.key,
     },
-    { status: 201 },
+    {
+      status: 201,
+      headers: { 'Cache-Control': 'no-store' },
+    },
   );
🤖 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 `@app/api/api-keys/route.ts` around lines 225 - 231, The response that returns
the one-time secret (using NextResponse.json with payload containing body.apiKey
and body.key) must be marked non-cacheable; update the NextResponse.json call to
include headers: { "Cache-Control": "no-store" } so browsers and intermediaries
do not retain the key (i.e., NextResponse.json({...}, { status: 201, headers: {
"Cache-Control": "no-store" } })). Ensure you modify the response creation in
route.ts where NextResponse.json is used to return the API key.
🤖 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.

Outside diff comments:
In `@app/api/api-keys/route.ts`:
- Around line 225-231: The response that returns the one-time secret (using
NextResponse.json with payload containing body.apiKey and body.key) must be
marked non-cacheable; update the NextResponse.json call to include headers: {
"Cache-Control": "no-store" } so browsers and intermediaries do not retain the
key (i.e., NextResponse.json({...}, { status: 201, headers: { "Cache-Control":
"no-store" } })). Ensure you modify the response creation in route.ts where
NextResponse.json is used to return the API key.

In `@src/features/api-keys/api-key-console-view.tsx`:
- Around line 100-105: The handleCopy function can throw if
navigator.clipboard.writeText rejects; wrap the await call in a try/catch around
navigator.clipboard.writeText(createdKey.apiKey) inside handleCopy, handle the
error by logging it (console.error or processLogger), setCopied(false) or set an
error state to show the user a failure message, and optionally fall back to a
document.execCommand('copy') or selecting a temporary textarea to copy; ensure
you still return early if !createdKey and reference the handleCopy function,
createdKey, and setCopied when implementing the fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 25df52c6-6b71-436c-975e-9969922013ff

📥 Commits

Reviewing files that changed from the base of the PR and between 216acc6 and fbb39d6.

📒 Files selected for processing (6)
  • app/api/api-keys/route.ts
  • docs/TECHNICAL_OVERVIEW.md
  • src/features/api-keys/api-key-console-view.tsx
  • src/features/markets/components/market-details-block.tsx
  • src/features/markets/components/table/market-table-utils.tsx
  • src/utils/apiKeyRequest.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/features/markets/components/table/market-table-utils.tsx
  • src/features/markets/components/market-details-block.tsx

@antoncoding
Copy link
Copy Markdown
Owner Author

Closing this messy branch in favor of the clean API-console-only PR: #554

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request Specific feature ready to be implemented ui User interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant