Skip to content

refactor(developer-api): remove GatewayOffer concept and gateway-scoped fields#150

Merged
eliteprox merged 10 commits into
mainfrom
refactor/remove-gatewayid
Feb 26, 2026
Merged

refactor(developer-api): remove GatewayOffer concept and gateway-scoped fields#150
eliteprox merged 10 commits into
mainfrom
refactor/remove-gatewayid

Conversation

@eliteprox
Copy link
Copy Markdown
Contributor

@eliteprox eliteprox commented Feb 25, 2026

Summary

Removes GatewayOffer as a product concept end-to-end. Gateway offers were scaffolded early but never became real business objects — they had no billing scope, no routing logic, and no live data source. API keys are now cleanly scoped to a billing provider only, and the model detail UI shows model-only information.

Changes

  • Removed GatewayOffer, SLATier, CapacityLevel types from @naap/types
  • Removed gatewayCount from AIModel type and all model objects/serializers
  • Removed gatewayId, gatewayName, modelId, modelName from DeveloperApiKey type; replaced with providerDisplayName
  • Dropped DevApiGatewayOffer Prisma model and its relation from DevApiAIModel
  • Hardened DevApiKey.billingProviderId from nullable to required
  • Deleted GET /api/v1/developer/models/:id/gateways route from all backends (web-next, plugin backend, developer-svc)
  • Deleted GatewayOfferCard.tsx component
  • Simplified ModelDetailPanel — removed gateway offers section, CTA now always visible
  • Removed gatewayCount metric from ModelCard and "Gateways" column from CompareDrawer
  • Removed useModelGateways hook from useApi.ts
  • Removed all mockGatewayOffers and getAllGatewayOffers mock data
  • Replaced "Gateway" column with "Provider" in API Keys table and detail panel; removed "Model" column
  • Updated plugin DeveloperView model cards to show p50 latency instead of gateway count
  • Cleaned leaderboard-api-data-contract.md, leaderboard-api-gaps.md, database.md, and IMPROVEMENT.md

Type

  • Feature
  • Bug fix
  • Refactor
  • Documentation
  • CI / Tooling
  • Plugin (new or update)
  • Dependencies

Plugin(s) Affected

developer-api

Checklist

  • Tests pass locally
  • Lint passes (npm run lint)
  • Build succeeds (npm run build)
  • No new lint warnings introduced
  • Breaking changes documented below
  • Database migration included (if Prisma schema changed)

Breaking Changes

DevApiGatewayOffer table is dropped. Run the following after merging:

cd packages/database
npx prisma generate
npx prisma db push

Ensure all existing DevApiKey rows have a non-null billingProviderId before pushing to production — the column is now required.

AIModel no longer has gatewayCount. Any consumer reading this field from the API will receive undefined. Update callers accordingly.

Screenshots / Recordings

No new UI pages. Affected UI simplified:

  • Gateway cards removed
  • Provider column added to API Keys view
image

Summary by CodeRabbit

  • Refactor
    • Consolidated model/gateway flow into a single provider-based API key flow; removed gateway counts, Gateway Offer listings, and separate Model/Gateway columns; simplified key creation and key details to use Provider.
  • New Features
    • Provider filter for API Keys, created-key banner, and improved clipboard copy fallback; API keys now surface providerDisplayName.
  • Documentation
    • Schema docs updated to reflect exposed models: AIModel, ApiKey, UsageLog.

…terminology

- Removed all references to "gateway" in the API key creation and management flow, replacing them with "provider" terminology for consistency.
- Updated related components and hooks to reflect the removal of gateway-related logic, simplifying the API key creation process.
- Adjusted the data model to eliminate gateway-specific fields, ensuring a cleaner and more focused structure.
- Enhanced the UI to display provider information instead of gateway details, improving clarity for users.

This change streamlines the API key management process and aligns terminology across the application.
- Eliminated all references to gateways in the API key management flow, replacing them with provider terminology for consistency.
- Removed model ID handling from API key creation, simplifying the process and focusing on project and provider details.
- Updated related components and data structures to reflect these changes, enhancing clarity and maintainability.
- Adjusted UI elements to display provider information instead of gateway details, improving user experience.

This refactor streamlines the API key management process and aligns terminology across the application.
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 25, 2026

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

Project Deployment Actions Updated (UTC)
naap-platform Ready Ready Preview, Comment Feb 26, 2026 9:11pm

Request Review

@github-actions github-actions Bot added size/XL Extra large PR (500+ lines) scope/shell Shell app changes scope/packages Shared package changes scope/backend Backend service changes plugin/developer-api Developer API plugin and removed size/XL Extra large PR (500+ lines) labels Feb 25, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 25, 2026

⚠️ This PR is very large (1350 lines changed). Please split it into smaller, focused PRs if possible.

@github-actions github-actions Bot added the has-migration Includes database migration label Feb 25, 2026
@github-actions
Copy link
Copy Markdown

🗃️ Database Migration Detected

This PR includes changes to Prisma schema files. Please ensure:

  • Migration files have been generated (npx prisma migrate dev)
  • Migration is backward-compatible or a rollback plan exists
  • Data migration scripts are included if needed
  • The migration has been tested against a staging database

Requesting review from the core team: @livepeer/core

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 25, 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

Walkthrough

Removes GatewayOffer and model/gateway associations across DB, API, types, and UI; replaces them with a provider-centric model using providerDisplayName. Deletes model-gateway endpoints, strips gateway counts, and updates API key creation to accept/store only providerDisplayName.

Changes

Cohort / File(s) Summary
API key routes & services
apps/web-next/src/app/api/v1/developer/keys/route.ts, services/workflows/developer-svc/src/server.ts, plugins/developer-api/backend/src/server.ts
Removed modelId/gatewayId resolution/validation; POST now accepts { projectName, providerDisplayName }; created key records no longer include model/gateway fields and responses expose providerDisplayName.
Model gateways endpoint removed
apps/web-next/src/app/api/v1/developer/models/[id]/gateways/route.ts, services/workflows/developer-svc/src/server.ts
Deleted GET /models/:id/gateways and related gateway retrieval logic.
Prisma schema / DB model
packages/database/prisma/schema.prisma
Deleted DevApiGatewayOffer; removed gatewayOffers/apiKeys relations from DevApiAIModel; removed modelId/gatewayOfferId from DevApiKey; made billingProviderId non-nullable.
Types & serialization
packages/types/src/index.ts, apps/web-next/src/lib/api/models.ts
Removed GatewayOffer, SLATier, CapacityLevel, and gatewayCount from types and serialiseModel; DeveloperApiKey now has providerDisplayName instead of model/gateway fields.
In-memory & mock data
plugins/developer-api/backend/src/store/inMemory.ts, services/workflows/developer-svc/src/store/inMemory.ts, apps/workflows/developer-web/src/data/mockData.ts, apps/web-next/src/lib/data/developer-models.ts
Removed gatewayOffers data and GatewayOffer types; removed gatewayCount from models; API key mocks updated to use providerDisplayName; key generation updated and lastUsedAt added in stores.
Frontend hooks / API client
apps/workflows/developer-web/src/hooks/useApi.ts
Removed useModelGateways and createKey export; introduced dynamic API base via getServiceOrigin, added requestJson helper and credentials: 'include'; adapted API calls to new request/response shape.
Create key & API keys UI
apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx, ApiKeyTable.tsx, KeyDetailPanel.tsx, tabs/APIKeysTab.tsx
Removed multi-step model/gateway selection; modal now uses providerDisplayName; merged Model/Gateway into Provider column; updated onSuccess shape and clipboard handling.
Models UI / gateway offers removed
apps/workflows/developer-web/src/components/models/ModelDetailPanel.tsx, ModelCard.tsx, CompareDrawer.tsx, GatewayOfferCard.tsx
Removed GatewayOffer UI and GatewayOfferCard component; dropped gateway-related metrics/columns (gatewayCount); simplified create-key CTA to no-arg handler.
Developer view / provider filter
plugins/developer-api/frontend/src/pages/DeveloperView.tsx
Removed gatewayCount from AIModel; added provider filter and Provider column for API keys; replaced gatewayCount references with latencyP50.
Docs & config
docs/database.md, apps/workflows/developer-web/package.json, apps/workflows/developer-web/tsconfig.json, apps/workflows/developer-web/vite.config.ts, packages/utils/package.json, services/workflows/developer-svc/package.json
Updated schema docs to remove GatewayOffer responsibility; added @naap/plugin-sdk dependency and alias; tightened @types/express devDependency ranges.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client as Client (Browser)
participant Frontend as Frontend UI
participant DevSvc as Developer Service (API)
participant DB as Database / In-memory store

Client->>Frontend: submit create key { projectName, providerDisplayName }
Frontend->>DevSvc: POST /api/v1/developer/keys { projectName, providerDisplayName }
DevSvc->>DB: create DevApiKey (store providerDisplayName, billingProviderId)
DB-->>DevSvc: DevApiKey record + rawKey
DevSvc-->>Frontend: { key: rawKey, apiKey: DevApiKey }
Frontend-->>Client: display raw key and providerDisplayName

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

scope/sdk

Suggested reviewers

  • seanhanca
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% 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 'refactor(developer-api): remove GatewayOffer concept and gateway-scoped fields' directly and clearly describes the primary change across the entire changeset, which is the removal of GatewayOffer types, gateway-related fields, and associated APIs.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/remove-gatewayid

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.

…ctural improvements

- Removed `IMPROVEMENT.md`, `leaderboard-api-data-contract.md`, and `leaderboard-api-gaps.md` as they contained outdated information and were no longer relevant to the current project structure.
- This cleanup helps streamline the documentation and ensures that only current and useful resources are available for developers.
@github-actions github-actions Bot added the size/XL Extra large PR (500+ lines) label Feb 25, 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: 6

Caution

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

⚠️ Outside diff range comments (4)
apps/workflows/developer-web/src/hooks/useApi.ts (1)

88-101: ⚠️ Potential issue | 🟡 Minor

createKey is unused dead code that also has mismatched parameters.

The backend POST /api/v1/developer/keys requires billingProviderId and rawApiKey (lines 376–381 of server.ts), but this function only sends projectName. Additionally, createKey is never imported or called anywhere in the developer-web app—CreateKeyModal generates mock keys locally instead. Either remove this function or clarify its intended purpose and update the implementation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workflows/developer-web/src/hooks/useApi.ts` around lines 88 - 101, The
createKey function in useApi.ts is dead/incorrect: either remove the unused
createKey export entirely (and any tests/imports) to avoid stale code, or update
it to match the backend contract and actual usage by renaming/updating the
signature to accept the required billingProviderId and rawApiKey (and any
projectName if needed), POST { billingProviderId, rawApiKey } to
`${API_BASE_URL}/keys`, handle the JSON error payload consistently, and ensure
CreateKeyModal or callers import and use this updated createKey function; locate
the function by name (createKey) to apply the change.
apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx (1)

35-39: ⚠️ Potential issue | 🟡 Minor

navigator.clipboard.writeText has no error handling.

If clipboard access is denied or the page isn't served over HTTPS, this throws an unhandled promise rejection. The same pattern appears in APIKeysTab.tsx's handleCopyKey.

🛡️ Suggested fix
  const handleCopy = async () => {
-   await navigator.clipboard.writeText(generatedKey);
-   setCopied(true);
-   setTimeout(() => setCopied(false), 2000);
+   try {
+     await navigator.clipboard.writeText(generatedKey);
+     setCopied(true);
+     setTimeout(() => setCopied(false), 2000);
+   } catch {
+     // Fallback: could show a toast or inline error
+     console.error('Failed to copy to clipboard');
+   }
  };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`
around lines 35 - 39, Wrap the clipboard write in a try/catch inside handleCopy
(and mirror the change in APIKeysTab.tsx's handleCopyKey); attempt
navigator.clipboard.writeText(generatedKey) in the try, setCopied(true) and
start the timeout only on success, and in catch handle failures by falling back
to a programmatic copy fallback (create/select/execCommand on a hidden textarea)
or log/report the error (e.g., console.error or a toast/setError) so no
unhandled promise rejection occurs and the UI reflects only successful copies.
apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx (1)

181-189: ⚠️ Potential issue | 🟡 Minor

onSuccess silently closes the modal with no user feedback.

The created key data (including the raw key that won't be shown again) is discarded on success. The TODO comment acknowledges the gap. At minimum, a toast notification or navigation to the API Keys tab should be triggered so the user knows their key was created — otherwise they lose the raw key with no indication of what happened.

Do you want me to open a new issue to track adding navigation/toast feedback after key creation from the Models tab?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx` around lines
181 - 189, The onSuccess handler for CreateKeyModal (triggered when
showCreateKeyModal is true) currently only calls setShowCreateKeyModal(false)
and discards created key data; update the onSuccess to surface feedback—either
navigate to the API Keys tab (e.g., dispatch/navigation to the tab) or show a
toast/modal that displays or preserves the raw key returned by CreateKeyModal so
the user can copy it before it’s lost; ensure the handler accepts the created
key payload (adjust CreateKeyModal's onSuccess signature if needed), stores or
passes that raw key to the toast/modal or to the API Keys page, and still closes
the create modal via setShowCreateKeyModal(false).
apps/workflows/developer-web/src/components/tabs/APIKeysTab.tsx (1)

67-86: ⚠️ Potential issue | 🔴 Critical

Stale modelId/modelName fields left over from the gateway refactor — type mismatch with CreateKeyModal.onSuccess.

handleCreateSuccess's parameter type (line 69) still declares modelId: string, but CreateKeyModal.onSuccess now emits { projectName, providerDisplayName, rawKey } (see CreateKeyModal.tsx line 8). At runtime data.modelId will always be undefined, and mockApiKeys[0].modelName references a removed field. Both should be removed from the newly created key object.

🐛 Proposed fix
  const handleCreateSuccess = (data: {
    projectName: string;
-   modelId: string;
+   providerDisplayName: string;
    rawKey: string;
  }) => {
    // In real app, would use response from API
    const newKey: DeveloperApiKey = {
      id: `key-${Date.now()}`,
      projectName: data.projectName,
-     modelId: data.modelId,
-     modelName: mockApiKeys[0].modelName, // Would come from API
-     providerDisplayName: 'Daydream',
+     providerDisplayName: data.providerDisplayName,
      keyHash: data.rawKey.slice(0, 6) + '****************************' + data.rawKey.slice(-4),
      status: 'active',
      createdAt: new Date().toISOString(),
      lastUsedAt: null,
    };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workflows/developer-web/src/components/tabs/APIKeysTab.tsx` around lines
67 - 86, handleCreateSuccess currently expects modelId and uses
mockApiKeys[0].modelName and a hardcoded provider name, but
CreateKeyModal.onSuccess now emits { projectName, providerDisplayName, rawKey };
update handleCreateSuccess to accept ({ projectName, providerDisplayName, rawKey
}) and remove any references to modelId and modelName (including
mockApiKeys[0].modelName); use data.providerDisplayName for the
providerDisplayName field instead of the hardcoded 'Daydream', construct the new
DeveloperApiKey without modelId/modelName fields, and ensure setKeys call and
any types align with the updated payload from CreateKeyModal.onSuccess.
🧹 Nitpick comments (3)
docs/leaderboard-api-data-contract.md (1)

168-170: API key scope note still references gateway selection behavior.

Line 170 says gateway selection is informational in model detail views. Given this PR removes gateway-centric flows, this should be rewritten to provider-centric wording only (e.g., billing provider scope and provider display fields).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/leaderboard-api-data-contract.md` around lines 168 - 170, Update the
"API Key Scope Note" section to remove any mention of "gateway" and replace
gateway-centric wording with provider-centric wording: change phrases like
"gateway selection is informational in model detail views" to "provider display
is informational in model detail views" and emphasize API keys are scoped to a
single billing provider (e.g., Daydream) and that provider display fields are
informational only; ensure the section heading "API Key Scope Note" and any
sentences in that paragraph reference "billing provider" or "provider display"
instead of "gateway" everywhere.
plugins/developer-api/backend/src/server.ts (1)

306-317: modelName is still returned but removed from the DeveloperApiKey type.

Lines 311 and 353 still include modelName: k.model?.name || 'Unknown' in the API response, but the DeveloperApiKey type in @naap/types no longer has a modelName field. This extra field won't cause runtime errors, but it's inconsistent with the declared type contract after this refactor.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/developer-api/backend/src/server.ts` around lines 306 - 317, The
response still includes a modelName field even though the DeveloperApiKey type
no longer defines it; remove the modelName entry from the object produced in the
keys.map (the formatted variable) so the API response matches the
DeveloperApiKey contract (search for the keys.map(...) that sets modelName:
k.model?.name || 'Unknown' and delete that property), and do the same for the
other occurrence around the later mapping (line near the second occurrence) so
both responses no longer emit modelName.
plugins/developer-api/frontend/src/pages/DeveloperView.tsx (1)

224-226: Billing providers only load when visiting the Usage tab, not the API Keys tab.

loadBillingProviders is triggered by activeTab === 'usage', but the provider filter dropdown appears on the API Keys tab (the default tab). On initial load, billingProviders will be empty, so providerOptions will rely entirely on the fallback that extracts providers from apiKeys. This works, but the filter dropdown will show no options if there are zero API keys.

Consider also loading billing providers when activeTab === 'api-keys', or eagerly in loadData, so the filter is always populated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/developer-api/frontend/src/pages/DeveloperView.tsx` around lines 224
- 226, The current effect only calls loadBillingProviders when activeTab ===
'usage', which leaves billingProviders empty on the initial API Keys tab; update
the logic so billing providers are loaded for the API Keys view as well (or
eagerly on component mount). Modify the useEffect that references activeTab and
loadBillingProviders (or the loadData function) to call loadBillingProviders
when activeTab === 'usage' || activeTab === 'api-keys', or move the call into
loadData so billingProviders is populated regardless of activeTab; ensure
providerOptions still falls back to extracting from apiKeys when
billingProviders is empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/leaderboard-api-data-contract.md`:
- Around line 11-20: The docs still reference removed gateway concepts and the
five-endpoint flow; update the contract to reflect the PR that removed
gateway-related APIs and UI pieces: edit the description around
useNetworkCapabilities() to remove the `/api/network/demand` call and any
mentions of GatewayOffer, gatewayOffers, GatewayOfferCard, gateway tiers, and
gateway capacity/fees; ensure the endpoints list only includes the remaining
four calls (pipelines, regions, gpu/metrics, sla/compliance) and sweep the other
noted sections (lines cited) to delete or reword gateway-specific content so the
NetworkModel[] and Models tab consumers aren’t misled.
- Around line 4-5: Update the doc header branch metadata by replacing the
current "**Branch:** `wip/metrics`" entry with the correct branch name
"`refactor/remove-gatewayid`" (and adjust the date if needed) so the header
accurately reflects the PR change set; locate the header line that reads
"**Branch:** `wip/metrics`" and change the branch token to
"`refactor/remove-gatewayid`".

In `@docs/leaderboard-api-gaps.md`:
- Around line 37-42: The comment asks to clarify that the `gateway` field
referenced in `/api/network/demand` and the proposed `/api/gateways` endpoint
refer to leaderboard-domain data-plane metadata only (not developer-api
gateway-scoped models/fields); update docs/leaderboard-api-gaps.md around the
"Gateway Identity and Operator Metadata" section by adding a single sentence
explicitly stating that these gateway identifiers and the proposed
`/api/gateways` are leaderboard-specific metadata and do not reintroduce
Developer API gateway entities or change developer-api models/fields.
- Around line 47-57: The docs still reference the removed GatewayOffer concept
and its fields—replace provider-centric references to
GatewayOffer.latencyGuarantee and GatewayOffer.unitPrice with the new
provider/demand-centered terminology (e.g., gateway-reported latency SLA on
demand rows or a gateway configuration endpoint) and update mentions of
`/api/network/demand` and `e2e_latency_ms` to clarify that `e2e_latency_ms` is
an observed metric not a declared SLA; also change the pricing text to stop
calling ETH/min as `$x.xxx/min` and instead either document an ETH→USD
conversion requirement or label prices as `Ξ/min` until a price feed is added.

In `@services/workflows/developer-svc/src/server.ts`:
- Line 99: generateApiKey (and the in-memory key builder in
services/workflows/developer-svc/src/store/inMemory.ts) currently uses
Math.random() which is not cryptographically secure; replace the weak generator
with Node's crypto.randomBytes to produce a hex (or base64) string, prefix it
with the same "naap_" token pattern used by the sibling service, and ensure the
produced length/format matches the CSRF skip-logic expectations. Update the
generateApiKey call site and the key-construction helper in inMemory.ts to use
crypto.randomBytes(...) and format the result (e.g., "naap_"+hex) so keys are
unpredictable and consistent with plugins/developer-api/backend's
implementation.
- Around line 24-29: The cast on csrfMiddleware masks a `@types/express` version
mismatch; instead of using "as unknown as express.RequestHandler" on the
createCsrfMiddleware return, align the `@types/express` versions across packages:
update packages/utils (the package that exports createCsrfMiddleware) to use
`@types/express`@^4.17.25 (or downgrade the service to match) and run
install/lockfile update so the middleware type matches express.RequestHandler
used in app.use('/api', csrfMiddleware); remove the double-cast once versions
are consistent.

---

Outside diff comments:
In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`:
- Around line 35-39: Wrap the clipboard write in a try/catch inside handleCopy
(and mirror the change in APIKeysTab.tsx's handleCopyKey); attempt
navigator.clipboard.writeText(generatedKey) in the try, setCopied(true) and
start the timeout only on success, and in catch handle failures by falling back
to a programmatic copy fallback (create/select/execCommand on a hidden textarea)
or log/report the error (e.g., console.error or a toast/setError) so no
unhandled promise rejection occurs and the UI reflects only successful copies.

In `@apps/workflows/developer-web/src/components/tabs/APIKeysTab.tsx`:
- Around line 67-86: handleCreateSuccess currently expects modelId and uses
mockApiKeys[0].modelName and a hardcoded provider name, but
CreateKeyModal.onSuccess now emits { projectName, providerDisplayName, rawKey };
update handleCreateSuccess to accept ({ projectName, providerDisplayName, rawKey
}) and remove any references to modelId and modelName (including
mockApiKeys[0].modelName); use data.providerDisplayName for the
providerDisplayName field instead of the hardcoded 'Daydream', construct the new
DeveloperApiKey without modelId/modelName fields, and ensure setKeys call and
any types align with the updated payload from CreateKeyModal.onSuccess.

In `@apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx`:
- Around line 181-189: The onSuccess handler for CreateKeyModal (triggered when
showCreateKeyModal is true) currently only calls setShowCreateKeyModal(false)
and discards created key data; update the onSuccess to surface feedback—either
navigate to the API Keys tab (e.g., dispatch/navigation to the tab) or show a
toast/modal that displays or preserves the raw key returned by CreateKeyModal so
the user can copy it before it’s lost; ensure the handler accepts the created
key payload (adjust CreateKeyModal's onSuccess signature if needed), stores or
passes that raw key to the toast/modal or to the API Keys page, and still closes
the create modal via setShowCreateKeyModal(false).

In `@apps/workflows/developer-web/src/hooks/useApi.ts`:
- Around line 88-101: The createKey function in useApi.ts is dead/incorrect:
either remove the unused createKey export entirely (and any tests/imports) to
avoid stale code, or update it to match the backend contract and actual usage by
renaming/updating the signature to accept the required billingProviderId and
rawApiKey (and any projectName if needed), POST { billingProviderId, rawApiKey }
to `${API_BASE_URL}/keys`, handle the JSON error payload consistently, and
ensure CreateKeyModal or callers import and use this updated createKey function;
locate the function by name (createKey) to apply the change.

---

Nitpick comments:
In `@docs/leaderboard-api-data-contract.md`:
- Around line 168-170: Update the "API Key Scope Note" section to remove any
mention of "gateway" and replace gateway-centric wording with provider-centric
wording: change phrases like "gateway selection is informational in model detail
views" to "provider display is informational in model detail views" and
emphasize API keys are scoped to a single billing provider (e.g., Daydream) and
that provider display fields are informational only; ensure the section heading
"API Key Scope Note" and any sentences in that paragraph reference "billing
provider" or "provider display" instead of "gateway" everywhere.

In `@plugins/developer-api/backend/src/server.ts`:
- Around line 306-317: The response still includes a modelName field even though
the DeveloperApiKey type no longer defines it; remove the modelName entry from
the object produced in the keys.map (the formatted variable) so the API response
matches the DeveloperApiKey contract (search for the keys.map(...) that sets
modelName: k.model?.name || 'Unknown' and delete that property), and do the same
for the other occurrence around the later mapping (line near the second
occurrence) so both responses no longer emit modelName.

In `@plugins/developer-api/frontend/src/pages/DeveloperView.tsx`:
- Around line 224-226: The current effect only calls loadBillingProviders when
activeTab === 'usage', which leaves billingProviders empty on the initial API
Keys tab; update the logic so billing providers are loaded for the API Keys view
as well (or eagerly on component mount). Modify the useEffect that references
activeTab and loadBillingProviders (or the loadData function) to call
loadBillingProviders when activeTab === 'usage' || activeTab === 'api-keys', or
move the call into loadData so billingProviders is populated regardless of
activeTab; ensure providerOptions still falls back to extracting from apiKeys
when billingProviders is empty.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a90bbe1 and 12eac29.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json, !package-lock.json
📒 Files selected for processing (27)
  • apps/web-next/src/app/api/v1/developer/keys/route.ts
  • apps/web-next/src/app/api/v1/developer/models/[id]/gateways/route.ts
  • apps/web-next/src/app/api/v1/developer/models/[id]/route.ts
  • apps/web-next/src/app/api/v1/developer/models/route.ts
  • apps/web-next/src/lib/api/models.ts
  • apps/web-next/src/lib/data/developer-models.ts
  • apps/workflows/developer-web/src/components/api-keys/ApiKeyTable.tsx
  • apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx
  • apps/workflows/developer-web/src/components/api-keys/KeyDetailPanel.tsx
  • apps/workflows/developer-web/src/components/models/CompareDrawer.tsx
  • apps/workflows/developer-web/src/components/models/GatewayOfferCard.tsx
  • apps/workflows/developer-web/src/components/models/ModelCard.tsx
  • apps/workflows/developer-web/src/components/models/ModelDetailPanel.tsx
  • apps/workflows/developer-web/src/components/tabs/APIKeysTab.tsx
  • apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx
  • apps/workflows/developer-web/src/data/mockData.ts
  • apps/workflows/developer-web/src/hooks/useApi.ts
  • docs/database.md
  • docs/leaderboard-api-data-contract.md
  • docs/leaderboard-api-gaps.md
  • packages/database/prisma/schema.prisma
  • packages/types/src/index.ts
  • plugins/developer-api/backend/src/server.ts
  • plugins/developer-api/backend/src/store/inMemory.ts
  • plugins/developer-api/frontend/src/pages/DeveloperView.tsx
  • services/workflows/developer-svc/src/server.ts
  • services/workflows/developer-svc/src/store/inMemory.ts
💤 Files with no reviewable changes (7)
  • apps/web-next/src/app/api/v1/developer/keys/route.ts
  • apps/web-next/src/app/api/v1/developer/models/route.ts
  • apps/web-next/src/app/api/v1/developer/models/[id]/route.ts
  • apps/workflows/developer-web/src/components/models/CompareDrawer.tsx
  • apps/web-next/src/lib/api/models.ts
  • apps/web-next/src/app/api/v1/developer/models/[id]/gateways/route.ts
  • apps/workflows/developer-web/src/components/models/GatewayOfferCard.tsx

Comment thread docs/leaderboard-api-data-contract.md Outdated
Comment thread docs/leaderboard-api-data-contract.md Outdated
Comment thread docs/leaderboard-api-gaps.md Outdated
Comment thread docs/leaderboard-api-gaps.md Outdated
Comment thread services/workflows/developer-svc/src/server.ts
Comment thread services/workflows/developer-svc/src/server.ts
Copy link
Copy Markdown
Contributor

@vercel vercel Bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

Code references removed modelId field and model relation from DevApiKey schema, causing Prisma runtime errors

Fix on Vercel

Comment thread apps/workflows/developer-web/src/components/tabs/APIKeysTab.tsx
- Updated the API key generation method to use a secure random hex string format, improving security and uniqueness.
- Enhanced clipboard copy functionality in the CreateKeyModal and APIKeysTab components to handle potential clipboard access errors gracefully.
- Removed outdated model ID references from API key handling, focusing on project and provider details for clarity.
- Introduced a notification banner for successful key creation, improving user experience by providing immediate feedback.

These changes streamline the API key management process and enhance the overall user interface.
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

Caution

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

⚠️ Outside diff range comments (2)
plugins/developer-api/backend/src/server.ts (2)

298-317: 🛠️ Refactor suggestion | 🟠 Major

Remove the dead model include from both GET key queries

model: { select: { id: true, name: true } } is eagerly loaded in both GET /api/v1/developer/keys (Line 303) and GET /api/v1/developer/keys/:id (Line 343), but neither formatted response block uses it. Per the PR's goal of dropping model linkage from API keys, this leftover join adds unnecessary DB load on every request.

♻️ Proposed fix
  include: {
    project: { select: { id: true, name: true, isDefault: true } },
    billingProvider: {
      select: { id: true, slug: true, displayName: true },
    },
-   model: { select: { id: true, name: true } },
  },

Apply the same removal in both query blocks (Lines 299–305 and Lines 338–345).

Also applies to: 328-367

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/developer-api/backend/src/server.ts` around lines 298 - 317, Remove
the unused model include from the Prisma queries in the GET key handlers: locate
the route handlers for GET /api/v1/developer/keys and GET
/api/v1/developer/keys/:id in server.ts and delete the model: { select: { id:
true, name: true } } entry from the include object used when fetching keys (the
Prisma query that also includes project and billingProvider). Ensure both query
blocks (the one mapping keys to formatted response and the one returning a
single key) no longer request model to avoid the unnecessary DB join.

371-423: ⚠️ Potential issue | 🔴 Critical

Remove modelId handling from POST /api/v1/developer/keys—field was removed from Prisma schema

The DevApiKey model no longer has a modelId field in the Prisma schema, but the code at lines 371–423 still destructures modelId from the request body, resolves it against devApiAIModel, and attempts to write it to the database. This will cause a Prisma validation error at runtime when prisma.devApiKey.create() executes.

Remove the following:

  • Line 371: Remove modelId from destructuring
  • Lines 393–398: Remove the entire modelId resolution block
  • Line 423: Remove modelId: resolvedModelId || null from the create data object
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/developer-api/backend/src/server.ts` around lines 371 - 423, Remove
all handling of modelId from the POST /api/v1/developer/keys handler: stop
destructuring modelId from req.body, delete the block that looks up
devApiAIModel (the code that defines and assigns resolvedModelId by calling
prisma.devApiAIModel.findUnique), and remove the modelId property from the data
passed to prisma.devApiKey.create (and any references to resolvedModelId). Keep
the rest of the flow (billingProvider checks, resolveDevApiProjectId call, key
hashing/creation) unchanged.
♻️ Duplicate comments (2)
services/workflows/developer-svc/src/server.ts (1)

24-29: CSRF middleware wiring is clean — previous double-cast issue resolved.

The createCsrfMiddleware return value is now assigned directly without the as unknown as express.RequestHandler double-cast from the previous review. This is a good improvement.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/workflows/developer-svc/src/server.ts` around lines 24 - 29, The
CSRF middleware wiring has been fixed by removing the previous double-cast; keep
the direct assignment from createCsrfMiddleware to csrfMiddleware and continue
using app.use('/api', csrfMiddleware) with the existing options (skipPaths,
logOnly, logger) — no further changes required to createCsrfMiddleware,
csrfMiddleware, or the app.use call.
services/workflows/developer-svc/src/store/inMemory.ts (1)

1-2: Previous Math.random() concern is resolved — key generation is now cryptographically secure.

generateApiKey correctly uses crypto.randomBytes with the naap_ prefix, which also aligns with the CSRF middleware's skip logic for API tokens. Good improvement.

Also applies to: 141-143

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/workflows/developer-svc/src/store/inMemory.ts` around lines 1 - 2,
The Math.random() issue is already fixed by using crypto.randomBytes in
generateApiKey; verify that the function generateApiKey constructs keys with the
"naap_" prefix and uses randomBytes (not Math.random), and apply the same change
to the other occurrences referenced (around the logic at lines matching the
second generateApiKey usage near 141-143) so all API key generation paths
consistently use crypto.randomBytes and the "naap_" prefix.
🧹 Nitpick comments (2)
plugins/developer-api/backend/src/server.ts (1)

306-317: providerDisplayName is redundant alongside the already-returned billingProvider.displayName

Both GET responses include billingProvider: { id, slug, displayName } as well as a separately flattened providerDisplayName field derived from the same source. If the intention is a stable top-level convenience field for consumers, consider documenting that contract; otherwise this is duplicated data and one source of truth should be chosen.

Also applies to: 347-358

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/developer-api/backend/src/server.ts` around lines 306 - 317, The
response currently duplicates billingProvider.displayName via a flattened
providerDisplayName field inside the keys mapping; remove providerDisplayName
from the formatted object so consumers use billingProvider.displayName as the
single source of truth, and apply the same removal to the similar mapping block
that builds responses at the other location (the mapping around lines 347-358).
Ensure you only return billingProvider (with its displayName) and drop the
providerDisplayName property from the formatted payloads.
apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx (1)

207-222: Duplicate clipboard-copy-with-fallback logic — consider extracting a shared helper.

The same textarea-fallback clipboard pattern appears here (Lines 207–218) and in CreateKeyModal.tsx (Lines 31–48). Extracting a copyToClipboard(text: string) utility would reduce duplication and centralize the deprecated execCommand fallback in one place.

Example helper
// e.g., src/utils/clipboard.ts
export async function copyToClipboard(text: string): Promise<void> {
  try {
    await navigator.clipboard.writeText(text);
  } catch {
    const ta = document.createElement('textarea');
    ta.value = text;
    ta.style.position = 'fixed';
    ta.style.opacity = '0';
    document.body.appendChild(ta);
    ta.select();
    document.execCommand('copy');
    document.body.removeChild(ta);
  }
}

Also applies to: 31-48

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx` around lines
207 - 222, Extract the duplicated clipboard-fallback logic into a shared async
helper like copyToClipboard(text: string) and replace the inline logic in the
onClick handler that uses createdKeyInfo.rawKey (in ModelsTab.tsx) and the
similar block in CreateKeyModal.tsx with a call to that helper; the helper
should attempt navigator.clipboard.writeText(text) and fall back to creating a
hidden textarea, selecting it, calling document.execCommand('copy'), and
cleaning up the DOM, so both components simply await
copyToClipboard(createdKeyInfo.rawKey) and then
setCreatedKeyCopied(true)/timeout as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`:
- Line 22: The submit guard in CreateKeyModal's handleSubmit uses `if
(!projectName)` which treats whitespace-only names as valid; update handleSubmit
to mirror the `isValid` logic (e.g., check `projectName?.trim().length > 0` or
call `isValid`) so whitespace-only strings are rejected before proceeding,
ensuring the same validation used for the button disabled state is enforced on
programmatic or Enter-key submissions; reference `handleSubmit`, `projectName`,
and `isValid` when making the change.
- Around line 21-29: The handleSubmit function currently fabricates a
client-side key; replace that logic with a POST to '/api/v1/developer/keys'
sending JSON { projectName, providerDisplayName } and await the JSON response,
then call setGeneratedKey(response.rawApiKey) and setStep('success'); retain the
early return on missing projectName and handle HTTP/network errors by setting an
error state or logging as appropriate; update references to setGeneratedKey and
setStep in CreateKeyModal to use the server-returned rawApiKey instead of the
crypto-generated hex.

---

Outside diff comments:
In `@plugins/developer-api/backend/src/server.ts`:
- Around line 298-317: Remove the unused model include from the Prisma queries
in the GET key handlers: locate the route handlers for GET
/api/v1/developer/keys and GET /api/v1/developer/keys/:id in server.ts and
delete the model: { select: { id: true, name: true } } entry from the include
object used when fetching keys (the Prisma query that also includes project and
billingProvider). Ensure both query blocks (the one mapping keys to formatted
response and the one returning a single key) no longer request model to avoid
the unnecessary DB join.
- Around line 371-423: Remove all handling of modelId from the POST
/api/v1/developer/keys handler: stop destructuring modelId from req.body, delete
the block that looks up devApiAIModel (the code that defines and assigns
resolvedModelId by calling prisma.devApiAIModel.findUnique), and remove the
modelId property from the data passed to prisma.devApiKey.create (and any
references to resolvedModelId). Keep the rest of the flow (billingProvider
checks, resolveDevApiProjectId call, key hashing/creation) unchanged.

---

Duplicate comments:
In `@services/workflows/developer-svc/src/server.ts`:
- Around line 24-29: The CSRF middleware wiring has been fixed by removing the
previous double-cast; keep the direct assignment from createCsrfMiddleware to
csrfMiddleware and continue using app.use('/api', csrfMiddleware) with the
existing options (skipPaths, logOnly, logger) — no further changes required to
createCsrfMiddleware, csrfMiddleware, or the app.use call.

In `@services/workflows/developer-svc/src/store/inMemory.ts`:
- Around line 1-2: The Math.random() issue is already fixed by using
crypto.randomBytes in generateApiKey; verify that the function generateApiKey
constructs keys with the "naap_" prefix and uses randomBytes (not Math.random),
and apply the same change to the other occurrences referenced (around the logic
at lines matching the second generateApiKey usage near 141-143) so all API key
generation paths consistently use crypto.randomBytes and the "naap_" prefix.

---

Nitpick comments:
In `@apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx`:
- Around line 207-222: Extract the duplicated clipboard-fallback logic into a
shared async helper like copyToClipboard(text: string) and replace the inline
logic in the onClick handler that uses createdKeyInfo.rawKey (in ModelsTab.tsx)
and the similar block in CreateKeyModal.tsx with a call to that helper; the
helper should attempt navigator.clipboard.writeText(text) and fall back to
creating a hidden textarea, selecting it, calling document.execCommand('copy'),
and cleaning up the DOM, so both components simply await
copyToClipboard(createdKeyInfo.rawKey) and then
setCreatedKeyCopied(true)/timeout as before.

In `@plugins/developer-api/backend/src/server.ts`:
- Around line 306-317: The response currently duplicates
billingProvider.displayName via a flattened providerDisplayName field inside the
keys mapping; remove providerDisplayName from the formatted object so consumers
use billingProvider.displayName as the single source of truth, and apply the
same removal to the similar mapping block that builds responses at the other
location (the mapping around lines 347-358). Ensure you only return
billingProvider (with its displayName) and drop the providerDisplayName property
from the formatted payloads.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12eac29 and 800e149.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json, !package-lock.json
📒 Files selected for processing (10)
  • apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx
  • apps/workflows/developer-web/src/components/tabs/APIKeysTab.tsx
  • apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx
  • apps/workflows/developer-web/src/hooks/useApi.ts
  • packages/utils/package.json
  • plugins/developer-api/backend/src/server.ts
  • plugins/developer-api/frontend/src/pages/DeveloperView.tsx
  • services/workflows/developer-svc/package.json
  • services/workflows/developer-svc/src/server.ts
  • services/workflows/developer-svc/src/store/inMemory.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/workflows/developer-web/src/components/tabs/APIKeysTab.tsx
  • apps/workflows/developer-web/src/hooks/useApi.ts

Comment thread apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx Outdated
Comment thread apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx Outdated
…ndling

- Enhanced the CreateKeyModal component to support asynchronous submission for API key generation.
- Added state management for submission status and error messages to improve user feedback during the key creation process.
- Updated the handleSubmit function to validate input, make a POST request to the API, and handle potential errors gracefully.
- Improved the UI to reflect the submission state, providing users with immediate feedback on their actions.

These changes enhance the user experience by ensuring a smoother and more informative API key creation flow.
@eliteprox
Copy link
Copy Markdown
Contributor Author

@seanhanca I came across some "legacy" logic in the Models section of developer-api manager. Removed unused fields so that api keys are not associated with gateway or modelid.

API Keys remain associated with billing providers, and I've also updated the database to enforce the constraint between BillingProvider<->DevApiKey

Let me know if these changes make sense to you from a feature perspective, want to make sure I'm not removing anything you had plans to use

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

🧹 Nitpick comments (1)
apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx (1)

55-65: Deprecated document.execCommand('copy') in fallback; return value ignored.

document.execCommand is no longer recommended — though some browsers still support it, it may have already been removed from the relevant web standards or may only be kept for compatibility purposes. Additionally, execCommand returns a boolean indicating success, which is currently ignored, so setCopied(true) fires even when the copy fails.

♻️ Proposed fix — swap to a synchronous `ClipboardItem` write or at least guard the return value
-    } catch {
-      const ta = document.createElement('textarea');
-      ta.value = generatedKey;
-      ta.style.position = 'fixed';
-      ta.style.opacity = '0';
-      document.body.appendChild(ta);
-      ta.select();
-      document.execCommand('copy');
-      document.body.removeChild(ta);
-      setCopied(true);
-      setTimeout(() => setCopied(false), 2000);
-    }
+    } catch {
+      // execCommand is deprecated but remains the only fallback for non-HTTPS contexts
+      const ta = document.createElement('textarea');
+      ta.value = generatedKey;
+      ta.style.position = 'fixed';
+      ta.style.opacity = '0';
+      document.body.appendChild(ta);
+      ta.select();
+      const ok = document.execCommand('copy');
+      document.body.removeChild(ta);
+      if (ok) {
+        setCopied(true);
+        setTimeout(() => setCopied(false), 2000);
+      }
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`
around lines 55 - 65, The fallback copy code in CreateKeyModal currently uses
deprecated document.execCommand('copy') and unconditionally calls
setCopied(true); change it to use navigator.clipboard.writeText(generatedKey)
when available and await its result, setting setCopied(true) only on success and
setCopied(false) or handle the error on failure; if navigator.clipboard is
unavailable keep a synchronous fallback that calls document.execCommand('copy')
but check its boolean return value and only call setCopied(true) when it returns
true, and ensure any caught error or rejection is handled (clearing the
temporary textarea and not marking as copied).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`:
- Around line 69-75: The handleDone callback currently passes the untrimmed
projectName to onSuccess, causing inconsistency with handleSubmit which sends
projectName.trim() to the API; update handleDone to call onSuccess with
projectName.trim() (preserving providerDisplayName and generatedKey) so the
value returned to the caller matches the stored name (references: handleDone,
handleSubmit, onSuccess, projectName, providerDisplayName, generatedKey).
- Around line 40-41: The response parsing in CreateKeyModal currently trusts
response.json() as { rawApiKey: string } and calls
setGeneratedKey(data.rawApiKey) without a runtime check; add a guard after
parsing (check that data exists and typeof data.rawApiKey === 'string' and it's
non-empty) and only call setGeneratedKey when valid, otherwise set an error
state or show a user-visible error/notification and avoid displaying an empty
key; update the logic around response.json(), setGeneratedKey, and any
success-screen rendering to handle the error path cleanly.
- Around line 31-35: Replace the hardcoded backend host in CreateKeyModal.tsx by
using a deploy-safe base URL; locate the fetch call in CreateKeyModal (the POST
to 'http://localhost:4007/api/v1/developer/keys') and change it to use a
relative path ('/api/v1/developer/keys') or build the URL from a public env var
(e.g. NEXT_PUBLIC_API_BASE_URL or REACT_APP_API_BASE_URL) so the request targets
the correct backend in staging/production; ensure you read the env var once
(e.g. apiBase = process.env.NEXT_PUBLIC_API_BASE_URL || '') and concatenate
'/api/v1/developer/keys' when calling fetch.

---

Nitpick comments:
In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`:
- Around line 55-65: The fallback copy code in CreateKeyModal currently uses
deprecated document.execCommand('copy') and unconditionally calls
setCopied(true); change it to use navigator.clipboard.writeText(generatedKey)
when available and await its result, setting setCopied(true) only on success and
setCopied(false) or handle the error on failure; if navigator.clipboard is
unavailable keep a synchronous fallback that calls document.execCommand('copy')
but check its boolean return value and only call setCopied(true) when it returns
true, and ensure any caught error or rejection is handled (clearing the
temporary textarea and not marking as copied).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 800e149 and d6d2a43.

📒 Files selected for processing (1)
  • apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx

Comment thread apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx Outdated
Comment thread apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx Outdated
- Added @naap/plugin-sdk as a dependency in package.json and package-lock.json to streamline API interactions.
- Updated TypeScript configuration to include path resolution for @naap/plugin-sdk, enhancing module accessibility.
- Refactored CreateKeyModal and useApi hooks to utilize getServiceOrigin from @naap/plugin-sdk for dynamic API endpoint resolution, improving flexibility and maintainability.
- Enhanced error handling in API calls to ensure robust responses and user feedback during key management operations.

These changes improve the integration of the plugin SDK, facilitating a more cohesive development experience and better API key management.
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

♻️ Duplicate comments (1)
apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx (1)

57-73: ⚠️ Potential issue | 🟡 Minor

Don't mark copy as successful when fallback copy fails.

In the fallback path (line 69), document.execCommand('copy') result is ignored and copied is always set to true, even if the copy operation fails. This shows a false success state to the user.

🔧 Suggested change
     } catch {
       const ta = document.createElement('textarea');
       ta.value = generatedKey;
       ta.style.position = 'fixed';
       ta.style.opacity = '0';
       document.body.appendChild(ta);
       ta.select();
-      document.execCommand('copy');
+      const ok = document.execCommand('copy');
       document.body.removeChild(ta);
+      if (!ok) {
+        throw new Error('Copy failed');
+      }
       setCopied(true);
       setTimeout(() => setCopied(false), 2000);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`
around lines 57 - 73, In handleCopy, the fallback path unconditionally sets
copied=true even when document.execCommand('copy') may fail; change the fallback
to capture the boolean result of document.execCommand('copy'), remove the
textarea in a finally-style way, and only call setCopied(true) (and start the
timeout) when execCommand returns true—otherwise handle failure (e.g., leave
copied false or surface an error). Reference: handleCopy, generatedKey,
setCopied, and the temporary textarea usage.
🧹 Nitpick comments (1)
apps/workflows/developer-web/src/hooks/useApi.ts (1)

89-122: Deduplicate key mutation request logic to avoid drift.

rotateKey, renameKey, and revokeKey repeat the same fetch/error handling. A small shared helper keeps behavior consistent and easier to maintain.

♻️ Proposed refactor
+  const mutateKey = useCallback(
+    async (path: string, init: RequestInit = {}) => {
+      const response = await fetch(`${API_BASE}${path}`, {
+        headers: {
+          'Content-Type': 'application/json',
+          ...init.headers,
+        },
+        ...init,
+      });
+
+      const payload = await response.json().catch(() => ({}));
+      if (!response.ok) {
+        throw new Error((payload as { error?: string }).error || 'Key operation failed');
+      }
+      return payload;
+    },
+    []
+  );
+
-  const rotateKey = useCallback(async (keyId: string) => {
-    const response = await fetch(`${API_BASE}/keys/${keyId}/rotate`, {
-      method: 'POST',
-    });
-    if (!response.ok) {
-      const error = await response.json();
-      throw new Error(error.error || 'Failed to rotate key');
-    }
-    return response.json();
-  }, []);
+  const rotateKey = useCallback(
+    (keyId: string) => mutateKey(`/keys/${keyId}/rotate`, { method: 'POST' }),
+    [mutateKey]
+  );

-  const renameKey = useCallback(async (keyId: string, projectName: string) => {
-    const response = await fetch(`${API_BASE}/keys/${keyId}`, {
-      method: 'PATCH',
-      headers: { 'Content-Type': 'application/json' },
-      body: JSON.stringify({ projectName }),
-    });
-    if (!response.ok) {
-      const error = await response.json();
-      throw new Error(error.error || 'Failed to rename key');
-    }
-    return response.json();
-  }, []);
+  const renameKey = useCallback(
+    (keyId: string, projectName: string) =>
+      mutateKey(`/keys/${keyId}`, {
+        method: 'PATCH',
+        body: JSON.stringify({ projectName }),
+      }),
+    [mutateKey]
+  );

-  const revokeKey = useCallback(async (keyId: string) => {
-    const response = await fetch(`${API_BASE}/keys/${keyId}`, {
-      method: 'DELETE',
-    });
-    if (!response.ok) {
-      const error = await response.json();
-      throw new Error(error.error || 'Failed to revoke key');
-    }
-    return response.json();
-  }, []);
+  const revokeKey = useCallback(
+    (keyId: string) => mutateKey(`/keys/${keyId}`, { method: 'DELETE' }),
+    [mutateKey]
+  );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workflows/developer-web/src/hooks/useApi.ts` around lines 89 - 122, The
three functions rotateKey, renameKey, and revokeKey duplicate fetch/error
handling; extract a shared helper (e.g., requestJson or mutateKey) that accepts
(url: string, options?: RequestInit, errorMessage?: string) and performs fetch,
checks response.ok, parses JSON error body when not ok and throws new
Error(error.error || errorMessage), and returns response.json() on success; then
refactor rotateKey, renameKey, and revokeKey to call this helper with their
specific endpoint and options (PATCH body for renameKey) to ensure consistent
behavior and single place to adjust error handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`:
- Around line 7-13: The CreateKeyModal currently defaults providerDisplayName to
'Daydream', which can silently create keys for the wrong provider; update
CreateKeyModalProps so providerDisplayName is required (remove the optional ?
and the default value) and adjust the CreateKeyModal function signature to
accept providerDisplayName as a required prop; ensure callers of CreateKeyModal
pass providerDisplayName and update any usages that relied on the default so
onSuccess and key creation use the explicitly provided providerDisplayName.

In `@apps/workflows/developer-web/src/hooks/useApi.ts`:
- Line 5: The fetch requests to the developer API need to include credentials
for cross-origin session cookies: update every fetch call in useApi.ts that
targets `${API_BASE}${url}` (e.g., the internal helpers like apiFetch / request
/ get / post or any direct fetch usages) to add credentials: 'include' to the
options object passed to fetch; ensure the credentials option is included
alongside existing headers/body/method so cookies and session-based auth (CSRF
flows) work in development.

---

Duplicate comments:
In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`:
- Around line 57-73: In handleCopy, the fallback path unconditionally sets
copied=true even when document.execCommand('copy') may fail; change the fallback
to capture the boolean result of document.execCommand('copy'), remove the
textarea in a finally-style way, and only call setCopied(true) (and start the
timeout) when execCommand returns true—otherwise handle failure (e.g., leave
copied false or surface an error). Reference: handleCopy, generatedKey,
setCopied, and the temporary textarea usage.

---

Nitpick comments:
In `@apps/workflows/developer-web/src/hooks/useApi.ts`:
- Around line 89-122: The three functions rotateKey, renameKey, and revokeKey
duplicate fetch/error handling; extract a shared helper (e.g., requestJson or
mutateKey) that accepts (url: string, options?: RequestInit, errorMessage?:
string) and performs fetch, checks response.ok, parses JSON error body when not
ok and throws new Error(error.error || errorMessage), and returns
response.json() on success; then refactor rotateKey, renameKey, and revokeKey to
call this helper with their specific endpoint and options (PATCH body for
renameKey) to ensure consistent behavior and single place to adjust error
handling.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6d2a43 and 0401d19.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json, !package-lock.json
📒 Files selected for processing (5)
  • apps/workflows/developer-web/package.json
  • apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx
  • apps/workflows/developer-web/src/hooks/useApi.ts
  • apps/workflows/developer-web/tsconfig.json
  • apps/workflows/developer-web/vite.config.ts

Comment thread apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx Outdated
Comment thread apps/workflows/developer-web/src/hooks/useApi.ts
seanhanca
seanhanca previously approved these changes Feb 26, 2026
- Changed the providerDisplayName prop in CreateKeyModal to be required, ensuring that a valid display name is always provided.
- Updated the clipboard copy functionality to check for success before setting the copied state, enhancing user feedback.
- Set a default providerDisplayName of "Daydream" in APIKeysTab and ModelsTab components, ensuring consistency across the application.
- Introduced a new requestJson function in useApi to streamline API calls with included credentials and improved error handling.

These changes enhance the robustness and user experience of the API key management process.
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)
apps/workflows/developer-web/src/hooks/useApi.ts (1)

83-94: Add defensive handling for non-JSON error responses.

If the server returns an error with a non-JSON body (e.g., a 502 gateway error with HTML), response.json() at line 89 will throw a SyntaxError, and the meaningful errorMessage parameter will never be used.

🛡️ Suggested defensive handling
 function requestJson(url: string, options?: RequestInit, errorMessage?: string) {
   return fetch(url, {
     credentials: 'include',
     ...options,
   }).then(async (response) => {
     if (!response.ok) {
-      const error = await response.json();
-      throw new Error(error.error || errorMessage);
+      let message = errorMessage || 'Request failed';
+      try {
+        const error = await response.json();
+        message = error.error || message;
+      } catch {
+        // Non-JSON error response, use default message
+      }
+      throw new Error(message);
     }
     return response.json();
   });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workflows/developer-web/src/hooks/useApi.ts` around lines 83 - 94, In
requestJson, the code assumes error responses are JSON and calling
response.json() on non-JSON bodies will throw; change the error-path to attempt
parsing JSON but fall back to reading response.text() (or using response.status
/ errorMessage) if JSON parsing fails or returns no error field, and then throw
a new Error that includes the best available message (parsed error.error,
fallback text, response.status, or the provided errorMessage). Update the error
handling inside the fetch.then handler that references response to first try
await response.json() in a try/catch, and on catch/invalid shape use await
response.text() or a default message before throwing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx`:
- Around line 146-154: In CreateKeyModal, block modal closure while the create
request is in-flight by using the existing submitting state: when submitting is
true, prevent executing the modal's onClose/close handler and disable
backdrop-click and Escape-key dismissal (or pass the modal prop that disables
backdrop/escape close), and also disable the modal's close button/UI; once
submitting becomes false allow normal close behavior. Locate and modify the
modal root (the component that renders the dialog) and any close button handlers
to check submitting (or derive a disableClose prop) so the modal cannot be
closed until the request completes and the one-time key is shown.

In `@apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx`:
- Around line 207-222: The click handler currently calls
setCreatedKeyCopied(true) unconditionally which can show success even when copy
fails; update the onClick logic in the onClick handler for the copy button (the
block that uses navigator.clipboard.writeText and the textarea fallback that
calls document.execCommand('copy')) so that setCreatedKeyCopied(true) is only
called when the copy actually succeeds — await navigator.clipboard.writeText and
on success set the state, and in the catch attempt the textarea fallback and
inspect the boolean return value of document.execCommand('copy') (or wrap it in
a try/catch) and only call setCreatedKeyCopied(true) if that fallback returns
true; ensure failures do not set the copied state and optionally surface/log the
error.

---

Nitpick comments:
In `@apps/workflows/developer-web/src/hooks/useApi.ts`:
- Around line 83-94: In requestJson, the code assumes error responses are JSON
and calling response.json() on non-JSON bodies will throw; change the error-path
to attempt parsing JSON but fall back to reading response.text() (or using
response.status / errorMessage) if JSON parsing fails or returns no error field,
and then throw a new Error that includes the best available message (parsed
error.error, fallback text, response.status, or the provided errorMessage).
Update the error handling inside the fetch.then handler that references response
to first try await response.json() in a try/catch, and on catch/invalid shape
use await response.text() or a default message before throwing.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0401d19 and 1ccf724.

📒 Files selected for processing (4)
  • apps/workflows/developer-web/src/components/api-keys/CreateKeyModal.tsx
  • apps/workflows/developer-web/src/components/tabs/APIKeysTab.tsx
  • apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx
  • apps/workflows/developer-web/src/hooks/useApi.ts

Comment thread apps/workflows/developer-web/src/components/tabs/ModelsTab.tsx
…eractions

- Added keyboard event handling to close the modal with the Escape key when not submitting.
- Implemented backdrop click functionality to close the modal, improving user experience.
- Updated button state to disable when submitting, providing visual feedback to users.

These changes improve the usability and accessibility of the CreateKeyModal during API key management.
- Updated clipboard copy logic to check for success before setting the copied state, improving user feedback.
- Added fallback handling for older browsers to ensure clipboard functionality remains intact.
- Implemented a timeout to reset the copied state, providing a clearer indication of the copy action to users.

These changes enhance the user experience by ensuring reliable clipboard operations and immediate feedback during key copying actions.
coderabbitai[bot]
coderabbitai Bot previously approved these changes Feb 26, 2026
@eliteprox eliteprox disabled auto-merge February 26, 2026 21:06
@eliteprox eliteprox enabled auto-merge (squash) February 26, 2026 21:07
@eliteprox eliteprox requested a review from seanhanca February 26, 2026 21:07
- Updated multiple Rollup-related packages in package-lock.json to version 4.59.0, ensuring compatibility and access to the latest features and fixes.
- This change maintains the integrity and performance of the build process across various platforms.
@eliteprox eliteprox merged commit be75359 into main Feb 26, 2026
26 checks passed
@eliteprox eliteprox deleted the refactor/remove-gatewayid branch February 26, 2026 21:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

has-migration Includes database migration plugin/developer-api Developer API plugin scope/backend Backend service changes scope/packages Shared package changes scope/shell Shell app changes size/XL Extra large PR (500+ lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants