Skip to content

fix(dashboard): make auth prompt visible and resilient#229

Merged
SantiagoDePolonia merged 12 commits intomainfrom
feat/dashboard-auth
Apr 14, 2026
Merged

fix(dashboard): make auth prompt visible and resilient#229
SantiagoDePolonia merged 12 commits intomainfrom
feat/dashboard-auth

Conversation

@SantiagoDePolonia
Copy link
Copy Markdown
Contributor

@SantiagoDePolonia SantiagoDePolonia commented Apr 13, 2026

Summary

  • replace the hidden sidebar auth input with a root-level dashboard auth dialog and banner action
  • accept pasted raw keys or Bearer tokens without duplicating the Authorization prefix
  • suppress stale 401 responses so old unauthenticated requests cannot reopen the dialog after a valid key is submitted
  • move outside-click dismissal to the top dialog layer

Tests

  • make test-dashboard
  • go test ./internal/admin/dashboard
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Modal authentication dialog with dedicated "Enter/Change API key" control and end-to-end API key entry (normalize, validate, persist, refresh).
  • Bug Fixes

    • Prevents stale/older request responses from reopening auth UI or overwriting state; improved handling of aborted/failed requests.
  • Style

    • Updated dialog, banner, footer and dropdown styling with backdrop, transitions and mobile-responsive adjustments.
  • Tests

    • Expanded test coverage for auth flows, stale-auth handling, request cancellation, and UI markup expectations.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

Warning

Rate limit exceeded

@SantiagoDePolonia has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 45 minutes and 29 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 45 minutes and 29 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9c694235-fc20-4226-b584-0a0939396445

📥 Commits

Reviewing files that changed from the base of the PR and between d72e133 and 9245e27.

📒 Files selected for processing (11)
  • .github/workflows/release.yml
  • .github/workflows/test.yml
  • CLAUDE.md
  • Dockerfile
  • README.md
  • docs/2026-03-23_benchmark_scripts/README.md
  • docs/about/benchmarks.mdx
  • go.mod
  • internal/providers/openai/passthrough_semantics_test.go
  • internal/providers/oracle/oracle_test.go
  • internal/providers/xai/xai_test.go
📝 Walkthrough

Walkthrough

📝 Walkthrough

Added a modal auth dialog and CSS, centralized request options and auth-generation tagging for fetches, implemented stale-auth suppression logic, normalized/persisted API keys, updated many fetch call sites to use the new request flow, and extended tests to cover auth and request-cancellation behaviors.

Changes

Cohort / File(s) Summary
Styles & UI
internal/admin/dashboard/static/css/dashboard.css, internal/admin/dashboard/templates/auth-banner.html, internal/admin/dashboard/templates/layout.html
Add modal auth dialog markup and styles, [x-cloak] rule, responsive dialog/button styles, update auth banner and replace inline sidebar API-key input with modal dialog trigger.
Core dashboard logic
internal/admin/dashboard/static/js/dashboard.js
Add auth dialog state, API key normalization, hasApiKey(), submit/open/close dialog flows, authRequestGeneration counter, centralized requestOptions(), headers() usage, and stale-auth handling in handleFetchResponse.
Request centralization (modules)
internal/admin/dashboard/static/js/modules/*
aliases.js, audit-list.js, auth-keys.js, contribution-calendar.js, conversation-drawer.js, guardrails.js, providers.js, usage.js, workflows.js
Replace inline { headers: this.headers() } with this.requestOptions() when present; pass constructed request into fetch and handleFetchResponse; capture handled result and short-circuit on stale-auth via isStaleAuthFetchResult. Add emptyUsageSummary().
Alias/workflows mutations
internal/admin/dashboard/static/js/modules/aliases.js, internal/admin/dashboard/static/js/modules/workflows.js
Refactor mutation flows to use adminRequestOptions / requestOptions, tag requests with authGeneration, and handle stale-auth sentinel without mutating auth UI/state.
Tests — dashboard & modules
internal/admin/dashboard/static/js/modules/*.test.js (dashboard-display.test.js, dashboard-layout.test.js, aliases.test.js, providers.test.js, request-cancellation.test.js, etc.)
Add/adjust tests: localStorage harness, auth dialog and API-key normalization tests, stale-auth sentinel behavior, request-abort/failure coverage, and updated CSS/markup assertions.
Go unit tests (early returns)
internal/.../*.go (multiple test files across internal/*/_test.go)
Add explicit early returns after t.Fatal in many tests to prevent nil dereferences and stop execution after fatal failures.
Misc JS tests & helpers
internal/admin/dashboard/static/js/modules/aliases.test.js, other test harness changes
Allow VM/window overrides in tests; assert generation propagation and single-fetch behavior when stale-auth sentinel is returned.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant UI as Dashboard UI
  participant LS as LocalStorage
  participant Fetch as FetchLayer
  participant Server

  User->>UI: Click "Enter API key" (openAuthDialog)
  UI->>UI: focus `#authDialogApiKey`
  User->>UI: Submit API key (submitApiKey)
  UI->>UI: normalizeApiKey(), increment authRequestGeneration
  UI->>LS: save normalized key (gomodel_api_key)
  UI->>Fetch: build request via requestOptions(headers(), authGeneration)
  Fetch->>Server: fetch(..., Authorization: Bearer token, authGeneration)
  Server-->>Fetch: 401 Unauthorized (or OK)
  Fetch->>UI: handleFetchResponse(res, label, request)
  alt response authGeneration < current
    UI->>UI: return staleAuthResponseResult (ignore)
  else response is 401 and current
    UI->>UI: set authError/needsAuth, open auth dialog
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

I’m a rabbit with a key so neat 🐰🔑
I hopped and normalized it, trimmed each beat.
If stale auth comes, I gently ignore—
I bounce to the dialog and open the door.
Fetch safe, stored, and tidy — hop, celebrate! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.32% 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 'fix(dashboard): make auth prompt visible and resilient' directly and accurately describes the main changes in the PR: replacing a hidden sidebar auth input with a visible root-level dialog, improving auth resilience by suppressing stale 401 responses, and enhancing the overall auth UX.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/dashboard-auth

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.

@SantiagoDePolonia
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

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

Caution

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

⚠️ Outside diff range comments (1)
internal/admin/dashboard/static/js/modules/contribution-calendar.js (1)

10-25: ⚠️ Potential issue | 🟠 Major

Construct options inside try so abort-controller cleanup is guaranteed.

A throw from this.requestOptions()/this.headers() on Line 22 skips finally, which can leave _calendarFetchController uncleared.

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

In `@internal/admin/dashboard/static/js/modules/contribution-calendar.js` around
lines 10 - 25, The options object is built before the try block so if
this.requestOptions() or this.headers() throws the finally cleanup for the abort
controller can be skipped; move creation of options (the call to
requestOptions() or headers()) and the assignment of options.signal into the try
block that surrounds the fetch/async work so that _calendarFetchController is
always cleared in the finally; keep the existing logic that obtains controller
via this._startAbortableRequest('_calendarFetchController') and the
isCurrentRequest helper that uses this._isCurrentAbortableRequest or compares
this._calendarFetchController and controller.signal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/static/js/dashboard.js`:
- Around line 482-490: handleFetchResponse currently returns false for both real
401 errors and stale auth responses, causing callers like fetchCategories and
fetchExecutionPlans to treat late stale 401s as real failures and clear state;
change handleFetchResponse (and its callers) to distinguish stale responses by
returning/throwing a distinct sentinel (e.g. return "STALE_AUTH" or throw a
StaleAuthError) when isStaleAuthResponse(request) is true, update callers to
check for that sentinel (or catch StaleAuthError) and early-return without
mutating state (do not clear this.categories / this.executionPlans when the
response is stale), and keep the existing behavior for genuine 401s.
- Around line 310-316: submitApiKey() currently treats an empty/whitespace
apiKey as a successful unlock; before calling saveApiKey(), incrementing
authRequestGeneration, closing the dialog, or calling fetchAll(), validate the
stored/entered apiKey (from saveApiKey()/this.apiKey) and if it's empty or only
whitespace, do not proceed—keep the dialog open (do not call closeAuthDialog()),
do not increment authRequestGeneration, and set authError (or another UI flag)
so the user sees an error; only on a non-blank trimmed apiKey call saveApiKey(),
increment authRequestGeneration, clear authError/needsAuth, closeAuthDialog(),
and then call fetchAll().

In `@internal/admin/dashboard/static/js/modules/aliases.js`:
- Around line 150-154: In fetchAliases (and the similar block around lines
180–184), move construction of the request object (the call to
this.requestOptions() or headers via this.headers()) inside the try block so any
exceptions thrown during requestOptions()/headers() are caught; ensure
aliasLoading and aliasError are set/reset in the finally or catch so
aliasLoading cannot remain true after an error, referencing the fetchAliases
function, this.requestOptions, this.headers, aliasLoading and aliasError to
locate the changes.

In `@internal/admin/dashboard/static/js/modules/auth-keys.js`:
- Around line 90-94: The code sets this.authKeysLoading = true then constructs
request = typeof this.requestOptions === 'function' ? this.requestOptions() : {
headers: this.headers() } outside the try, so if this.requestOptions() or
this.headers() throws the finally won’t run and authKeysLoading can remain true;
move the request construction into the try block that wraps the fetch call (the
block containing fetch('/admin/api/v1/auth-keys', request)) so any exceptions
from this.requestOptions()/this.headers() are caught and the existing finally
will always execute to reset this.authKeysLoading and handle this.authKeyError.

In `@internal/admin/dashboard/static/js/modules/guardrails.js`:
- Around line 250-253: The guardrail fetch paths currently set
this.guardrailTypesLoading and build const request = typeof this.requestOptions
=== 'function' ? this.requestOptions() : { headers: this.headers() }; outside
the try block, so if requestOptions() or this.headers() throws the finally block
won't run and the loading flags (e.g., this.guardrailTypesLoading) can remain
true; move the request construction into the try in both the guardrail types
fetch (around fetch('/admin/api/v1/guardrails/types', request)) and the other
guardrail fetch path (the block around lines referencing requestOptions/headers
and fetch at the 282–286 area) so any exceptions during request creation are
caught and the finally handlers always execute to clear loading state.

In `@internal/admin/dashboard/static/js/modules/providers.js`:
- Around line 65-75: The code constructs request options before entering the try
block which can throw (e.g., in requestOptions() or headers()) and bypass the
finally that clears _providerStatusFetchController; move the options
construction so the controller is acquired first via
this._startAbortableRequest('_providerStatusFetchController') and then build
options (calling this.requestOptions() or this.headers()) inside the try block
(or ensure the try begins before calling requestOptions()/headers()), and keep
the existing logic that sets options.signal when controller exists and that
calls this.handleFetchResponse; this ensures _providerStatusFetchController is
always cleared in the finally.

In `@internal/admin/dashboard/static/js/modules/usage.js`:
- Around line 40-46: The request-options and header construction (calls to
this.requestOptions() / this.headers()) and the creation of the abort controller
(this._startAbortableRequest('_cacheOverviewFetchController')) are executed
before the try blocks, so any exception there skips the finally cleanup; move
the controller creation and evaluation of requestOptions()/headers() inside the
try block for each fetcher (the five spots that use this._startAbortableRequest,
requestOptions, and headers), only assign options.signal if controller exists
after those calls, and keep the existing finally to always call
controller.abort()/this._stopAbortableRequest so the abort-controller cleanup
runs even if requestOptions()/headers() throw.

---

Outside diff comments:
In `@internal/admin/dashboard/static/js/modules/contribution-calendar.js`:
- Around line 10-25: The options object is built before the try block so if
this.requestOptions() or this.headers() throws the finally cleanup for the abort
controller can be skipped; move creation of options (the call to
requestOptions() or headers()) and the assignment of options.signal into the try
block that surrounds the fetch/async work so that _calendarFetchController is
always cleared in the finally; keep the existing logic that obtains controller
via this._startAbortableRequest('_calendarFetchController') and the
isCurrentRequest helper that uses this._isCurrentAbortableRequest or compares
this._calendarFetchController and controller.signal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: de0a288b-c270-478d-9f6f-1e6f8dd9d70f

📥 Commits

Reviewing files that changed from the base of the PR and between 2b08948 and 29e7ae3.

📒 Files selected for processing (15)
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/dashboard.js
  • internal/admin/dashboard/static/js/modules/aliases.js
  • internal/admin/dashboard/static/js/modules/audit-list.js
  • internal/admin/dashboard/static/js/modules/auth-keys.js
  • internal/admin/dashboard/static/js/modules/contribution-calendar.js
  • internal/admin/dashboard/static/js/modules/conversation-drawer.js
  • internal/admin/dashboard/static/js/modules/dashboard-display.test.js
  • internal/admin/dashboard/static/js/modules/dashboard-layout.test.js
  • internal/admin/dashboard/static/js/modules/execution-plans.js
  • internal/admin/dashboard/static/js/modules/guardrails.js
  • internal/admin/dashboard/static/js/modules/providers.js
  • internal/admin/dashboard/static/js/modules/usage.js
  • internal/admin/dashboard/templates/auth-banner.html
  • internal/admin/dashboard/templates/layout.html

Copy link
Copy Markdown
Contributor

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/static/js/dashboard.js`:
- Around line 270-274: normalizeApiKey currently returns the literal "Bearer"
when given "Bearer" or "Bearer  "; change normalizeApiKey to explicitly treat a
bare Bearer scheme as empty (e.g., if key matches /^Bearer\s*$/i return ''),
otherwise extract the token as before, and add a regression test asserting
normalizeApiKey('Bearer') and normalizeApiKey('Bearer   ') both return '' and
that submitApiKey()/headers() only emit an Authorization header when the
normalized key is non-empty; reference normalizeApiKey, submitApiKey, and
headers when making these changes.

In `@internal/admin/dashboard/static/js/modules/aliases.js`:
- Around line 153-161: The write-path functions toggleAliasEnabled,
submitAliasForm, submitModelOverrideForm, and deleteModelOverride still send
bare headers() and set needsAuth unconditionally on 401; update each to
construct the request the same way as fetchAliases/fetchModelOverrides (const
request = typeof this.requestOptions === 'function' ? this.requestOptions() : {
headers: this.headers() };), use that request in the fetch call, and pass the
request into handleFetchResponse(res, ...) so that auth handling is
generation-aware; remove the unconditional this.needsAuth = true on 401 and let
handleFetchResponse decide/return whether auth is needed.

In `@internal/admin/dashboard/static/js/modules/dashboard-layout.test.js`:
- Around line 187-190: Remove the four negative assertions that forbid dialog
semantics and instead assert the dialog's required behavior: delete the
assert.doesNotMatch lines that reference aria-describedby, the two helper
sentences, and the <label> for "authDialogApiKey", and replace them with
positive checks on the template (the same template variable used in this test)
that verify the API-key input and accompanying guidance are present (e.g.,
assert.match(template, /id="authDialogApiKey"/ and assert.match(template, /Enter
a different API key for this browser\./) or similar), so accessibility
attributes like aria-describedby or an explicit <label> are allowed.

In `@internal/admin/dashboard/static/js/modules/providers.js`:
- Around line 74-79: After awaiting fetch('/admin/api/v1/providers/status',
options) check whether the request was aborted (e.g., if options.signal &&
options.signal.aborted) and return early before calling
this.handleFetchResponse; this prevents handling a stale/aborted response and
avoids later branches (the !handled branch that can reopen auth or clear
providerStatus) from running. Update the sequence around the fetch so the abort
check occurs immediately after the fetch resolves and before invoking
handleFetchResponse (reference: options, handleFetchResponse,
isStaleAuthFetchResult).

In `@internal/admin/dashboard/static/js/modules/usage.js`:
- Around line 116-123: The early-return on failed live fetches leaves stale UI
data; after determining neither summaryHandled nor dailyHandled is truthy (but
before returning), reset this.summary and this.daily to the empty usage state
(e.g., this.summary = { total: 0, ... } and this.daily = [] or whatever the
component's empty shape is) and trigger a rerender (call the component's render
method, e.g., this.render()) while preserving the existing STALE_AUTH check via
isStaleAuthFetchResult and leaving that early return unchanged; update the block
around handleFetchResponse, summaryHandled, dailyHandled and
isStaleAuthFetchResult to perform the reset+render on real failures before
returning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7f276a06-b0b3-489c-b564-ed08466c95d3

📥 Commits

Reviewing files that changed from the base of the PR and between 29e7ae3 and 3a2ab7f.

📒 Files selected for processing (14)
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/dashboard.js
  • internal/admin/dashboard/static/js/modules/aliases.js
  • internal/admin/dashboard/static/js/modules/audit-list.js
  • internal/admin/dashboard/static/js/modules/auth-keys.js
  • internal/admin/dashboard/static/js/modules/contribution-calendar.js
  • internal/admin/dashboard/static/js/modules/conversation-drawer.js
  • internal/admin/dashboard/static/js/modules/dashboard-display.test.js
  • internal/admin/dashboard/static/js/modules/dashboard-layout.test.js
  • internal/admin/dashboard/static/js/modules/guardrails.js
  • internal/admin/dashboard/static/js/modules/providers.js
  • internal/admin/dashboard/static/js/modules/usage.js
  • internal/admin/dashboard/static/js/modules/workflows.js
  • internal/admin/dashboard/templates/layout.html

Comment on lines +270 to +274
normalizeApiKey(value) {
const key = String(value || '').trim();
const match = key.match(/^Bearer\s+(.+)$/i);
return match ? match[1].trim() : key;
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reject a bare Bearer scheme as an empty key.

normalizeApiKey('Bearer') currently returns 'Bearer', so submitApiKey() accepts it and headers() emits Authorization: Bearer Bearer. That still breaks the pasted-token flow for incomplete header values. Normalize Bearer/Bearer to '' and add a regression test for that input.

🔧 Suggested fix
         normalizeApiKey(value) {
             const key = String(value || '').trim();
-            const match = key.match(/^Bearer\s+(.+)$/i);
-            return match ? match[1].trim() : key;
+            const match = key.match(/^Bearer(?:\s+(.*))?$/i);
+            return match ? String(match[1] || '').trim() : key;
         },
📝 Committable suggestion

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

Suggested change
normalizeApiKey(value) {
const key = String(value || '').trim();
const match = key.match(/^Bearer\s+(.+)$/i);
return match ? match[1].trim() : key;
},
normalizeApiKey(value) {
const key = String(value || '').trim();
const match = key.match(/^Bearer(?:\s+(.*))?$/i);
return match ? String(match[1] || '').trim() : key;
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/admin/dashboard/static/js/dashboard.js` around lines 270 - 274,
normalizeApiKey currently returns the literal "Bearer" when given "Bearer" or
"Bearer  "; change normalizeApiKey to explicitly treat a bare Bearer scheme as
empty (e.g., if key matches /^Bearer\s*$/i return ''), otherwise extract the
token as before, and add a regression test asserting normalizeApiKey('Bearer')
and normalizeApiKey('Bearer   ') both return '' and that
submitApiKey()/headers() only emit an Authorization header when the normalized
key is non-empty; reference normalizeApiKey, submitApiKey, and headers when
making these changes.

Copy link
Copy Markdown
Contributor

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/static/js/modules/dashboard-layout.test.js`:
- Line 158: The test assertion using assert.doesNotMatch(template, /<input
id="apiKey"/) is attribute-order dependent and can miss inputs where id is not
the first attribute; update the assertion in dashboard-layout.test.js to use an
order-agnostic regex that looks for an <input> element containing an id="apiKey"
anywhere in its attribute list (e.g. match
<input\b[^>]*\bid\s*=\s*["']?apiKey["']?) so the check on the variable template
reliably detects any legacy input with id apiKey.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 88411a1d-2d24-4044-abe5-eb2c0092ef09

📥 Commits

Reviewing files that changed from the base of the PR and between 3a2ab7f and cf44e09.

📒 Files selected for processing (9)
  • internal/admin/dashboard/static/js/dashboard.js
  • internal/admin/dashboard/static/js/modules/aliases.js
  • internal/admin/dashboard/static/js/modules/aliases.test.js
  • internal/admin/dashboard/static/js/modules/dashboard-display.test.js
  • internal/admin/dashboard/static/js/modules/dashboard-layout.test.js
  • internal/admin/dashboard/static/js/modules/providers.js
  • internal/admin/dashboard/static/js/modules/providers.test.js
  • internal/admin/dashboard/static/js/modules/request-cancellation.test.js
  • internal/admin/dashboard/static/js/modules/usage.js

const template = readFixture('../../../templates/layout.html');
const css = readFixture('../../css/dashboard.css');

assert.doesNotMatch(template, /<input id="apiKey"/);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Make the legacy input absence check attribute-order agnostic.

Line 158 can miss <input ... id="apiKey" ...> when id is not the first attribute, so the test may falsely pass.

Suggested fix
-    assert.doesNotMatch(template, /<input id="apiKey"/);
+    assert.doesNotMatch(template, /<input[^>]*\bid="apiKey"\b/);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/admin/dashboard/static/js/modules/dashboard-layout.test.js` at line
158, The test assertion using assert.doesNotMatch(template, /<input
id="apiKey"/) is attribute-order dependent and can miss inputs where id is not
the first attribute; update the assertion in dashboard-layout.test.js to use an
order-agnostic regex that looks for an <input> element containing an id="apiKey"
anywhere in its attribute list (e.g. match
<input\b[^>]*\bid\s*=\s*["']?apiKey["']?) so the check on the variable template
reliably detects any legacy input with id apiKey.

Copy link
Copy Markdown
Contributor

@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.

♻️ Duplicate comments (1)
internal/admin/dashboard/static/js/modules/dashboard-layout.test.js (1)

158-158: ⚠️ Potential issue | 🟡 Minor

Make legacy API-key input absence check attribute-order agnostic.

Line 158 can miss <input ... id="apiKey" ...> when id is not first, leading to false passes.

Suggested fix
-    assert.doesNotMatch(template, /<input id="apiKey"/);
+    assert.doesNotMatch(template, /<input\b[^>]*\bid\s*=\s*["']apiKey["'][^>]*>/);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/admin/dashboard/static/js/modules/dashboard-layout.test.js` at line
158, The test currently looks for the legacy API-key input by matching the
literal sequence with id immediately after the input tag, which fails if
attributes are reordered; update the assertion in dashboard-layout.test.js (the
assert.doesNotMatch call that inspects template) to use an order-agnostic
pattern that matches an input element with any attributes followed by an
id="apiKey" attribute (i.e., match an input tag, allow any attributes, then
require the id attribute), so the check passes regardless of attribute order.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@internal/admin/dashboard/static/js/modules/dashboard-layout.test.js`:
- Line 158: The test currently looks for the legacy API-key input by matching
the literal sequence with id immediately after the input tag, which fails if
attributes are reordered; update the assertion in dashboard-layout.test.js (the
assert.doesNotMatch call that inspects template) to use an order-agnostic
pattern that matches an input element with any attributes followed by an
id="apiKey" attribute (i.e., match an input tag, allow any attributes, then
require the id attribute), so the check passes regardless of attribute order.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 24d5983b-9d69-445f-8f4a-3f4dbd79def8

📥 Commits

Reviewing files that changed from the base of the PR and between cf44e09 and d72e133.

📒 Files selected for processing (14)
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/modules/dashboard-layout.test.js
  • internal/app/app_test.go
  • internal/authkeys/service_test.go
  • internal/cache/modelcache/local_test.go
  • internal/cache/modelcache/redis_test.go
  • internal/httpclient/client_test.go
  • internal/llmclient/client_test.go
  • internal/modeldata/enricher_test.go
  • internal/modeldata/fetcher_test.go
  • internal/modeldata/merger_test.go
  • internal/observability/metrics_test.go
  • internal/providers/anthropic/anthropic_test.go
  • internal/providers/anthropic/passthrough_semantics_test.go

@SantiagoDePolonia SantiagoDePolonia merged commit 06c0eda into main Apr 14, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant