Skip to content

Add datetime filter for Workflows Page#693

Open
fernandol-nvidia wants to merge 5 commits intomainfrom
fernandol/workflow-date-filter
Open

Add datetime filter for Workflows Page#693
fernandol-nvidia wants to merge 5 commits intomainfrom
fernandol/workflow-date-filter

Conversation

@fernandol-nvidia
Copy link
Contributor

@fernandol-nvidia fernandol-nvidia commented Mar 11, 2026

Description

  • Add a datetime filter for workflows page
Screenshot 2026-03-11 at 3 25 01 PM Screenshot 2026-03-11 at 3 24 39 PM Screenshot 2026-03-11 at 3 24 31 PM Screenshot 2026-03-11 at 3 24 23 PM

Issue #682

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

Summary by CodeRabbit

  • New Features

    • Date-range filtering for workflow submissions with an interactive two-column date picker, presets, custom From/To inputs, keyboard navigation, and accessibility support.
    • "Submitted" added as a searchable field for workflows.
  • Style

    • Comprehensive styling and responsive layout for the date picker panel.
  • Documentation

    • Added "Reasoning Integrity: Avoiding Systematic Failure Modes" guidance.
  • Chores

    • Production authentication behavior updated; production-time auth mapping added. Date-range parsing and date-format utilities refined.

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

📝 Walkthrough

Walkthrough

Adds date-range filtering UI and plumbing (picker, types, keyboard flow, parsing, API adapter), introduces a production-first server auth module with dev fallbacks, minor config aliasing and export changes, and documents a new "Reasoning Integrity" section in CLAUDE.md.

Changes

Cohort / File(s) Summary
Date Range UI
src/ui/src/components/filter-bar/filter-bar-date-picker.tsx, src/ui/src/components/filter-bar/filter-bar-dropdown.tsx, src/ui/src/components/filter-bar/filter-bar.tsx, src/ui/src/components/filter-bar/filter-bar.css
Adds a two-column FilterBarDatePicker and integrates it into FilterBar/Dropdown. Dropdown conditionally renders the date picker, wires onDateCommit/onDateCycleStep, and CSS implements split-rail layout, hints, error and responsive styles.
Filter-bar Types & Hooks
src/ui/src/components/filter-bar/lib/types.ts, src/ui/src/components/filter-bar/hooks/use-filter-keyboard.ts, src/ui/src/components/filter-bar/hooks/use-filter-state.ts, src/ui/src/components/filter-bar/hooks/use-suggestions.ts
Introduces DateRangeSearchField, isDateRangeField guard, sentinel constants, and surfaces activeDateRangeField. Adds stepCycle/stepDateCycle keyboard support and handleDateCommit; useSuggestions surfaces presets/custom items for date-range fields.
Workflow Search Integration & Tests
src/ui/src/features/workflows/list/lib/workflow-search-fields.ts, src/ui/src/features/workflows/list/components/workflows-toolbar.tsx, src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts
Adds a "submitted" date-range search field, updates toolbar placeholder, and adapts tests to use centralized getFieldValues and isDateRangeField checks.
Date Parsing & API Adapter
src/ui/src/lib/date-range-utils.ts, src/ui/src/lib/api/adapter/workflows-shim.ts
Changes parsing to treat date-only ends as exclusive at next-midnight and datetimes with minute granularity; workflows-shim parses submitted chips into submitted_after/submitted_before, adds submitted to query keys, and refines all_users logic.
Auth Production Module & Refactor
src/ui/src/lib/auth/server.production.ts, src/ui/src/lib/auth/server.ts, src/ui/next.config.ts
Adds production header-based auth module exporting user/roles helpers; server.ts prefers production helpers with DEV_* fallbacks; next.config.ts maps @/lib/auth/server to the production variant in production builds.
Utilities & Exports
src/ui/src/lib/format-date.ts
Exports MONTHS_SHORT for reuse.
Documentation
src/ui/CLAUDE.md
Adds "Reasoning Integrity: Avoiding Systematic Failure Modes" section and adjusts the Architecture block formatting.
sequenceDiagram
    actor User
    participant UI as FilterBar UI
    participant FBP as FilterBarDatePicker
    participant KB as useFilterKeyboard
    participant State as useFilterState / FilterBar
    participant API as Workflows Adapter
    participant BE as Backend API

    User->>UI: open filter, focus submitted
    UI->>FBP: render date picker (presets/custom)
    User->>FBP: select preset or enter From/To
    FBP->>FBP: validate range (To > From)
    FBP->>State: onCommit(value)
    State->>State: handleDateCommit -> addChip(parsed.field, value)
    User->>KB: Tab / Shift-Tab
    KB->>FBP: stepCycle(direction, fromValue)
    State->>API: build query params (include submitted_after/submitted_before)
    API->>BE: GET /workflows?submitted_after=...&submitted_before=...
    BE-->>API: return workflows
    API-->>State: results
    State-->>UI: render filtered workflows
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I nibble presets and hop From to To,

I press Apply and watch the results flow,
Focus skips, keys twirl in tidy rhyme,
Date-range filters now dance in time. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 accurately summarizes the main change: adding a datetime filter capability to the Workflows Page, which is the primary feature across all modified files.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fernandol/workflow-date-filter

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/ui/src/components/filter-bar/hooks/use-suggestions.ts (1)

115-136: Variable shadowing: query redeclared on line 118.

The query variable on line 118 shadows the outer query from line 82. While both represent lowercased queries, the shadowing can cause confusion during maintenance.

♻️ Suggested fix: rename inner variable
     if (isDateRangeField(parsedInput.field)) {
-      const query = parsedInput.query.toLowerCase();
+      const presetQuery = parsedInput.query.toLowerCase();
       for (const preset of DATE_RANGE_PRESETS) {
-        if (!query || preset.label.toLowerCase().includes(query)) {
+        if (!presetQuery || preset.label.toLowerCase().includes(presetQuery)) {
           items.push({
             type: "value",
             field: parsedInput.field,
             value: preset.label,
             label: preset.label,
           });
         }
       }
       // Custom range inputs are always shown (only when not filtering presets)
-      if (!query) {
+      if (!presetQuery) {
         items.push({ type: "value", field: parsedInput.field, value: DATE_CUSTOM_FROM, label: "From date" });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/components/filter-bar/hooks/use-suggestions.ts` around lines 115 -
136, The inner lowercase query shadows the outer variable; in the
isDateRangeField block (around isDateRangeField(parsedInput.field) and
DATE_RANGE_PRESETS) rename the inner variable (e.g., to lowerQuery or
fieldQuery) instead of reusing query, and update all uses in that block (the if
check and preset.label.toLowerCase().includes(...) call and the subsequent if
(!...) that decides showing custom inputs) so the outer parsedInput.query
remains unchanged and shadowing is removed.
src/ui/src/components/filter-bar/filter-bar-date-picker.tsx (1)

151-159: Minor: Redundant aria-label.

The <label htmlFor="fb-date-from">From</label> already provides an accessible name for the input. The additional aria-label="From date" is redundant (and slightly differs from the visible label text).

🔧 Suggested simplification
             <input
               ref={fromRef}
               id="fb-date-from"
               type="datetime-local"
               value={fromDate}
               onChange={(e) => handleFromChange(e.target.value)}
               className="fb-date-input"
-              aria-label="From date"
             />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/components/filter-bar/filter-bar-date-picker.tsx` around lines 151
- 159, The input element using ref fromRef with id "fb-date-from" already has an
associated visible label (<label htmlFor="fb-date-from">From</label>), so remove
the redundant aria-label="From date" from that input; keep the other props
(type="datetime-local", value={fromDate}, onChange={e =>
handleFromChange(e.target.value)}, className="fb-date-input") unchanged to
preserve behavior and accessibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ui/CLAUDE.md`:
- Around line 33-36: The two markdown code fences containing the examples
starting with "❌ A strips time from a datetime-local input → downgrade input to
type=\"date\" for \"consistency\"" and the block beginning "Fact: backend
accepts datetime.datetime" are missing fence languages and trigger MD040; update
both triple-backtick fences to use a language token (e.g., ```text) so they read
```text ... ``` and apply the same fix to the other pair of example blocks
referenced (the ones around the "Fact: backend accepts datetime.datetime" area
reported as also affected).

In `@src/ui/src/lib/auth/server.production.ts`:
- Around line 1-15: Update the top-of-file copyright header so the line
containing the copyright holder reads "Copyright (c) 2026 NVIDIA CORPORATION &
AFFILIATES. All rights reserved." (i.e., keep "All rights reserved." on the same
line as "NVIDIA CORPORATION & AFFILIATES"); modify the header block at the very
top of server.production.ts to restore the exact required NVIDIA copyright
header text and SPDX identifier.

In `@src/ui/src/lib/auth/server.ts`:
- Line 41: The re-export always pulls hasServerAdminRole from the
production-only implementation so it bypasses the DEV_USER_ROLES dev fallback;
change the export to use the environment-aware implementation (or conditionally
delegate to the production implementation only in production). Concretely:
import the production and the environment-aware/dev implementations of
hasServerAdminRole (the current hasServerAdminRole from server.production and
the one that honors DEV_USER_ROLES used by getServerUserRoles/getServerUser) and
re-export a single hasServerAdminRole that selects the correct implementation
based on the runtime environment (e.g. use the production implementation only
when NODE_ENV === 'production', otherwise use the dev/environment-aware
version).

In `@src/ui/src/lib/date-range-utils.ts`:
- Around line 154-168: parseIsoDate currently treats "YYYY-MM-DDTHH:mm" by
calling new Date(str) which interprets the value in the runtime's local timezone
and can cause SSR hydration mismatches; change the implementation so the
datetime-local branch only parses on the client (e.g. guard with a window check
or isBrowser helper) or else parse deterministically (construct a Date from
numeric year/month/day/hour/minute using Date.UTC if you want UTC). Update the
branch matching /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/ to either skip/return null on
the server or replace new Date(str) with an explicit, timezone-agnostic
construction; add a short comment in parseIsoDate explaining the chosen
approach.

---

Nitpick comments:
In `@src/ui/src/components/filter-bar/filter-bar-date-picker.tsx`:
- Around line 151-159: The input element using ref fromRef with id
"fb-date-from" already has an associated visible label (<label
htmlFor="fb-date-from">From</label>), so remove the redundant aria-label="From
date" from that input; keep the other props (type="datetime-local",
value={fromDate}, onChange={e => handleFromChange(e.target.value)},
className="fb-date-input") unchanged to preserve behavior and accessibility.

In `@src/ui/src/components/filter-bar/hooks/use-suggestions.ts`:
- Around line 115-136: The inner lowercase query shadows the outer variable; in
the isDateRangeField block (around isDateRangeField(parsedInput.field) and
DATE_RANGE_PRESETS) rename the inner variable (e.g., to lowerQuery or
fieldQuery) instead of reusing query, and update all uses in that block (the if
check and preset.label.toLowerCase().includes(...) call and the subsequent if
(!...) that decides showing custom inputs) so the outer parsedInput.query
remains unchanged and shadowing is removed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6d3bafb1-a923-4b49-b4ba-1ee67cdf85bb

📥 Commits

Reviewing files that changed from the base of the PR and between 5edeab4 and 1b06303.

📒 Files selected for processing (18)
  • src/ui/CLAUDE.md
  • src/ui/next.config.ts
  • src/ui/src/components/filter-bar/filter-bar-date-picker.tsx
  • src/ui/src/components/filter-bar/filter-bar-dropdown.tsx
  • src/ui/src/components/filter-bar/filter-bar.css
  • src/ui/src/components/filter-bar/filter-bar.tsx
  • src/ui/src/components/filter-bar/hooks/use-filter-keyboard.ts
  • src/ui/src/components/filter-bar/hooks/use-filter-state.ts
  • src/ui/src/components/filter-bar/hooks/use-suggestions.ts
  • src/ui/src/components/filter-bar/lib/types.ts
  • src/ui/src/features/workflows/list/components/workflows-toolbar.tsx
  • src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts
  • src/ui/src/features/workflows/list/lib/workflow-search-fields.ts
  • src/ui/src/lib/api/adapter/workflows-shim.ts
  • src/ui/src/lib/auth/server.production.ts
  • src/ui/src/lib/auth/server.ts
  • src/ui/src/lib/date-range-utils.ts
  • src/ui/src/lib/format-date.ts

Copy link

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

🧹 Nitpick comments (1)
src/ui/src/lib/auth/server.ts (1)

68-75: Dev fallback user construction is functional but differs slightly from production.

The production getServerUser uses deriveDisplayName(username) for the name and getInitials(name) for initials, while the dev fallback uses the raw username as the name and a simple slice(0, 2).toUpperCase() for initials. This is acceptable for development purposes, but if you want exact parity with production behavior, consider importing and using these helper functions.

♻️ Optional: match production behavior
+import { deriveDisplayName, getInitials } from "@/lib/auth/server.production";
 // ...
 
   return {
     id: username,
-    name: username,
+    name: deriveDisplayName(username),
     email,
     username,
     isAdmin: hasAdminRole(roles),
-    initials: username.slice(0, 2).toUpperCase(),
+    initials: getInitials(deriveDisplayName(username)),
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/lib/auth/server.ts` around lines 68 - 75, The dev fallback user
construction returns the raw username and uses slice for initials which differs
from production; update the fallback in the server-side auth (the object
returned where id/name/email/username/isAdmin/initials are set) to call
deriveDisplayName(username) for the name and getInitials(name) (or
getInitials(deriveDisplayName(username))) for initials, and add the necessary
imports for deriveDisplayName and getInitials so the dev fallback matches
getServerUser's display-name and initials logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/ui/src/lib/auth/server.ts`:
- Around line 68-75: The dev fallback user construction returns the raw username
and uses slice for initials which differs from production; update the fallback
in the server-side auth (the object returned where
id/name/email/username/isAdmin/initials are set) to call
deriveDisplayName(username) for the name and getInitials(name) (or
getInitials(deriveDisplayName(username))) for initials, and add the necessary
imports for deriveDisplayName and getInitials so the dev fallback matches
getServerUser's display-name and initials logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5f2958d4-335f-429a-b518-e34ccb9cbf04

📥 Commits

Reviewing files that changed from the base of the PR and between 1b06303 and 79993a4.

📒 Files selected for processing (3)
  • src/ui/CLAUDE.md
  • src/ui/src/lib/auth/server.ts
  • src/ui/src/lib/date-range-utils.ts

@fernandol-nvidia fernandol-nvidia enabled auto-merge (squash) March 11, 2026 23:01
RyaliNvidia
RyaliNvidia previously approved these changes Mar 11, 2026
Copy link

@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

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

Inline comments:
In `@src/ui/src/components/filter-bar/filter-bar-date-picker.tsx`:
- Around line 116-121: The fb-date-picker element's onKeyDown currently calls
e.stopPropagation() unconditionally, which prevents the parent's
onEscape/closeDropdown from receiving Escape; update the onKeyDown handler in
filter-bar-date-picker.tsx (the element with className "fb-date-picker") to only
stop propagation for keys other than Escape so Escape can bubble up to the
parent's onEscape and allow closeDropdown() to run.

In `@src/ui/src/lib/auth/server.ts`:
- Around line 43-49: prodGetServerUserRoles currently conflates "no auth
headers" and "authenticated user with empty roles" by returning an empty array;
update the logic so prodGetServerUserRoles returns null (or undefined) when the
required auth headers are missing, and keep returning an empty array for an
authenticated user with no roles. Then change getServerUserRoles to only fall
back to process.env.DEV_USER_ROLES when prodGetServerUserRoles() returns
null/undefined; if it returns an empty array, return that empty array unchanged.
Update references: prodGetServerUserRoles and getServerUserRoles to implement
this sentinel behavior so DEV_USER_ROLES is used strictly when auth headers are
absent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 413bc287-cba4-45ee-8a29-16261cbb584f

📥 Commits

Reviewing files that changed from the base of the PR and between 79993a4 and 367452d.

📒 Files selected for processing (4)
  • src/ui/src/components/filter-bar/filter-bar-date-picker.tsx
  • src/ui/src/components/filter-bar/hooks/use-suggestions.ts
  • src/ui/src/lib/auth/server.production.ts
  • src/ui/src/lib/auth/server.ts

Comment on lines +116 to +121
return (
<div
className="fb-date-picker"
role="none"
onKeyDown={(e) => e.stopPropagation()}
>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how parent components handle keyboard events and whether Escape is expected to bubble
rg -n "onKeyDown|handleKeyDown" --type=ts --type=tsx -C3 src/ui/src/components/filter-bar/ | head -80

Repository: NVIDIA/OSMO

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Search for onKeyDown handlers in filter-bar components
rg -n "onKeyDown|handleKeyDown" -C3 src/ui/src/components/filter-bar/

Repository: NVIDIA/OSMO

Length of output: 10776


🏁 Script executed:

#!/bin/bash
# List files in filter-bar directory
find src/ui/src/components/filter-bar -type f -name "*.ts" -o -name "*.tsx" | head -20

Repository: NVIDIA/OSMO

Length of output: 1180


🏁 Script executed:

#!/bin/bash
# Read the filter-bar-date-picker.tsx file to understand the context
wc -l src/ui/src/components/filter-bar/filter-bar-date-picker.tsx

Repository: NVIDIA/OSMO

Length of output: 118


🏁 Script executed:

#!/bin/bash
# Read the date picker file
cat -n src/ui/src/components/filter-bar/filter-bar-date-picker.tsx | head -230

Repository: NVIDIA/OSMO

Length of output: 9873


🏁 Script executed:

#!/bin/bash
# Check the Escape key handling in use-filter-keyboard.ts
sed -n '169,202p' src/ui/src/components/filter-bar/hooks/use-filter-keyboard.ts

Repository: NVIDIA/OSMO

Length of output: 1020


🏁 Script executed:

#!/bin/bash
# Search for onEscape implementation in the keyboard hook
rg -n "onEscape" src/ui/src/components/filter-bar/hooks/use-filter-keyboard.ts -A 10

Repository: NVIDIA/OSMO

Length of output: 674


🏁 Script executed:

#!/bin/bash
# Look at FilterBarInput which seems to manage the date picker visibility
cat -n src/ui/src/components/filter-bar/filter-bar-input.tsx | sed -n '1,150p'

Repository: NVIDIA/OSMO

Length of output: 5741


🏁 Script executed:

#!/bin/bash
# Get the full onEscape function
sed -n '338,360p' src/ui/src/components/filter-bar/hooks/use-filter-keyboard.ts

Repository: NVIDIA/OSMO

Length of output: 567


🏁 Script executed:

#!/bin/bash
# Look for the date picker rendering in FilterBar
rg -n "FilterBarDatePicker\|showDropdown\|activeDateRangeField" src/ui/src/components/filter-bar/filter-bar.tsx -B 2 -A 2

Repository: NVIDIA/OSMO

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Get more of the onEscape function
sed -n '338,375p' src/ui/src/components/filter-bar/hooks/use-filter-keyboard.ts

Repository: NVIDIA/OSMO

Length of output: 830


🏁 Script executed:

#!/bin/bash
# Search for FilterBarDatePicker in FilterBar component
cat -n src/ui/src/components/filter-bar/filter-bar.tsx | sed -n '1,200p'

Repository: NVIDIA/OSMO

Length of output: 7204


🏁 Script executed:

#!/bin/bash
# Check what closeDropdown does
rg -n "closeDropdown" src/ui/src/components/filter-bar/hooks/use-filter-state.ts -B 2 -A 5

Repository: NVIDIA/OSMO

Length of output: 394


Remove stopPropagation() or allow Escape to bubble to parent's keyboard handler.

The onKeyDown={(e) => e.stopPropagation()} blocks all keyboard events, including Escape, from bubbling to the parent's keyboard navigation system. The parent's onEscape handler expects Escape to propagate and calls closeDropdown() to close the date picker. Since the date picker does not handle Escape itself, pressing Escape has no effect. Only Tab has custom handling on the Apply button (intentional), so consider replacing the blanket stopPropagation() with a selective check: if (e.key !== "Escape") e.stopPropagation().

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

In `@src/ui/src/components/filter-bar/filter-bar-date-picker.tsx` around lines 116
- 121, The fb-date-picker element's onKeyDown currently calls
e.stopPropagation() unconditionally, which prevents the parent's
onEscape/closeDropdown from receiving Escape; update the onKeyDown handler in
filter-bar-date-picker.tsx (the element with className "fb-date-picker") to only
stop propagation for keys other than Escape so Escape can bubble up to the
parent's onEscape and allow closeDropdown() to run.

Comment on lines 43 to 49
export async function getServerUserRoles(): Promise<string[]> {
const headersList = await headers();
const rolesHeader = headersList.get("x-osmo-roles") || "";

// Parse roles (comma-separated or space-separated)
const roles = rolesHeader
const roles = await prodGetServerUserRoles();
if (roles.length > 0) return roles;
return (process.env.DEV_USER_ROLES ?? "")
.split(/[,\s]+/)
.map((role) => role.trim())
.map((r) => r.trim())
.filter(Boolean);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Only use DEV_USER_ROLES when auth headers are actually missing.

prodGetServerUserRoles() normalizes both “no x-osmo-roles header” and “authenticated user with an empty role list” to [] in src/ui/src/lib/auth/server.production.ts Lines 28-34. With the current roles.length === 0 check, a proxied user who legitimately has no roles will inherit DEV_USER_ROLES and can appear admin in local testing.

Suggested fix
 export async function getServerUserRoles(): Promise<string[]> {
   const roles = await prodGetServerUserRoles();
   if (roles.length > 0) return roles;
+  if (await prodGetServerUsername()) return roles;
   return (process.env.DEV_USER_ROLES ?? "")
     .split(/[,\s]+/)
     .map((r) => r.trim())
     .filter(Boolean);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/src/lib/auth/server.ts` around lines 43 - 49, prodGetServerUserRoles
currently conflates "no auth headers" and "authenticated user with empty roles"
by returning an empty array; update the logic so prodGetServerUserRoles returns
null (or undefined) when the required auth headers are missing, and keep
returning an empty array for an authenticated user with no roles. Then change
getServerUserRoles to only fall back to process.env.DEV_USER_ROLES when
prodGetServerUserRoles() returns null/undefined; if it returns an empty array,
return that empty array unchanged. Update references: prodGetServerUserRoles and
getServerUserRoles to implement this sentinel behavior so DEV_USER_ROLES is used
strictly when auth headers are absent.

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.

3 participants