Skip to content

feat(FR-2576): add Project Admin Sessions page using projectSessionsV2#6696

Open
yomybaby wants to merge 2 commits intomainfrom
04-14-feat_fr-2576_add_project_admin_sessions_page_using_projectsessionsv2
Open

feat(FR-2576): add Project Admin Sessions page using projectSessionsV2#6696
yomybaby wants to merge 2 commits intomainfrom
04-14-feat_fr-2576_add_project_admin_sessions_page_using_projectsessionsv2

Conversation

@yomybaby
Copy link
Copy Markdown
Member

@yomybaby yomybaby commented Apr 14, 2026

Resolves #6695(FR-2576)

Summary

Adds a new Project Admin Sessions page at /project-admin-sessions that lets project admins view and manage compute sessions within their current project, backed by the new projectSessionsV2 GraphQL query and terminateProjectSessionsV2 mutation.

  • New page shell react/src/pages/ProjectAdminSessionsPage.tsx mirroring ProjectAdminUsersPage (BAICard + BAIErrorBoundary + Suspense + gated on currentProject.id).
  • New table component react/src/components/ProjectSessionV2Nodes.tsx rendering a Relay plural fragment on SessionV2. Does not modify the existing v1 SessionNodes.
  • Menu registration in useWebUIMenuItems.tsx under the admin-operations group with BAISessionsIcon and the webui.menu.ProjectSessions i18n label. Added to VALID_MENU_KEYS, ALL_ADMIN_PAGE_KEYS, and PROJECT_ADMIN_PAGE_KEYS.
  • Lazy route in routes.tsx and English i18n key webui.menu.ProjectSessions added to resources/i18n/en.json (translations follow /i18n).
  • Filtering: BAIGraphQLPropertyFilter with id, name, userUuid, domainName + a Running/Finished BAIRadioGroup that maps to SessionV2Status buckets.
  • Sorting: SessionV2OrderBy with fields CREATED_AT, TERMINATED_AT, STATUS, ID, NAME. Default CREATED_AT DESC.
  • Pagination/refresh: useBAIPaginationOptionStateOnSearchParam + BAIFetchKeyButton with 15s auto-refresh.
  • Terminate action: Modal.confirmterminateProjectSessionsV2 with { scope: { projectId }, sessionIds: [id], forced: false } → toast + fetch key bump. Disabled for TERMINATED / TERMINATING / CANCELLED sessions.
  • Session name click navigates with the sessionDetail search param (v1 drawer compatibility caveat inline-commented — full v2 drawer wiring is out of scope).

Column mapping

Column Source on SessionV2
Name (click → session detail) metadata.name
Status lifecycle.status
AI Accelerator resource.allocation.{used,requested}.entries (non-cpu/mem)
CPU resource.allocation.{used,requested}.entries (cpu)
Memory resource.allocation.{used,requested}.entries (mem, formatted)
Elapsed Time lifecycle.createdAtlifecycle.terminatedAt ?? now
Environment images.edges[0].node.identity
Resource Group resource.resourceGroupName
Session Type metadata.sessionType
Cluster Mode metadata.clusterMode
Created At lifecycle.createdAt
Owner (superadmin only) user.basicInfo.email

Default-visible: name, status, cpu, mem, accelerator, elapsedTime, owner (superadmin only). Default-hidden: environment, resourceGroup, sessionType, clusterMode, createdAt.

Intentionally omitted: dependencies and agent_ids columns — these fields do not exist on the v2 SessionV2 schema.

Verification

bash scripts/verify.sh output tail:

=== Relay ===
--- Relay: PASS ---
=== Lint ===
--- Lint: PASS ---
=== Format ===
--- Format: PASS ---
=== TypeScript ===
--- TypeScript: PASS ---
=== ALL PASS ===

Checklist

  • Documentation — none required for internal admin page; label added to source locale only
  • Minimum required manager version — depends on projectSessionsV2 / terminateProjectSessionsV2 availability
  • Specific setting for review — log in as a project admin, switch current project, open /project-admin-sessions
  • Minimum requirements to check during review — menu visibility for project admins, list scoped to current project, switching project refreshes list, terminate action works on a running session
  • Test case(s) to demonstrate the difference of before/after

Screenshots

Skipped. The running local dev server belongs to another worktree and the projectSessionsV2 / terminateProjectSessionsV2 APIs this page depends on may not be deployed to the shared test server yet. Capture manually after backend v2 support is available in the target environment.

Copilot AI review requested due to automatic review settings April 14, 2026 12:22
@github-actions github-actions Bot added area:ux UI / UX issue. area:i18n Localization labels Apr 14, 2026
@github-actions github-actions Bot added the size:XL 500~ LoC label Apr 14, 2026
Copy link
Copy Markdown
Member Author


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • flow:merge-queue - adds this PR to the back of the merge queue
  • flow:hotfix - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has required the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 14, 2026

Coverage report for ./react

St.
Category Percentage Covered / Total
🔴 Statements
8.52% (-0.06% 🔻)
1757/20618
🔴 Branches
7.82% (-0.05% 🔻)
1131/14456
🔴 Functions
5.09% (-0.04% 🔻)
285/5594
🔴 Lines
8.25% (-0.05% 🔻)
1649/19997
Show new covered files 🐣
St.
File Statements Branches Functions Lines
🔴
... / ProjectSessionV2Nodes.tsx
0% 0% 0% 0%
🔴
... / ProjectAdminSessionsPage.tsx
0% 0% 0% 0%

Test suite run success

856 tests passing in 39 suites.

Report generated by 🧪jest coverage report action from 749274a

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new Project Admin → Sessions page to the Backend.AI WebUI, backed by the projectSessionsV2 GraphQL API, and wires it into routing + the admin menu so project admins can browse and manage sessions in the current project.

Changes:

  • Add webui.menu.ProjectSessions i18n key (English).
  • Add /project-admin-sessions route + admin menu entry for project admins.
  • Introduce ProjectAdminSessionsPage (query/filter/sort/paginate + terminate action) and reusable ProjectSessionV2Nodes table component for SessionV2 lists.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
resources/i18n/en.json Adds the new menu label for “Project Sessions”.
react/src/routes.tsx Registers the /project-admin-sessions route and lazy-loads the page.
react/src/pages/ProjectAdminSessionsPage.tsx Implements the new Project Admin Sessions page using projectSessionsV2 + termination mutation.
react/src/hooks/useWebUIMenuItems.tsx Adds the “Project Sessions” admin menu item and includes it in project-admin page keys.
react/src/components/ProjectSessionV2Nodes.tsx Adds a SessionV2 table component with sorting and per-row actions (terminate, etc.).

Comment on lines +212 to +219
{
key: 'terminate',
title: t('session.TerminateSession'),
icon: <PowerOffIcon />,
type: 'danger' as const,
disabled: isTerminated,
onClick: () => onClickTerminate?.(session),
},
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

The "Terminate" row action is always rendered even when onClickTerminate is not provided, which makes the action clickable but a no-op in consumers that omit the handler. Consider only including the terminate action when onClickTerminate is defined (or set disabled: true when it's undefined) to avoid misleading UX.

Suggested change
{
key: 'terminate',
title: t('session.TerminateSession'),
icon: <PowerOffIcon />,
type: 'danger' as const,
disabled: isTerminated,
onClick: () => onClickTerminate?.(session),
},
onClickTerminate
? {
key: 'terminate',
title: t('session.TerminateSession'),
icon: <PowerOffIcon />,
type: 'danger' as const,
disabled: isTerminated,
onClick: () => onClickTerminate(session),
}
: undefined,

Copilot uses AI. Check for mistakes.
Comment thread resources/i18n/en.json
"ProfileUpdated": "Profile has been successfully updated.",
"Project": "Project",
"ProjectMembers": "Users",
"ProjectSessions": "Project Sessions",
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

This new menu label key is added only to en.json. Other locale files under resources/i18n/ do not define webui.menu.ProjectSessions, so non-English locales will fall back to the raw key at runtime. Please add the same key to all supported locale JSON files (even if temporarily copying the English string) and then provide proper translations.

Copilot uses AI. Check for mistakes.
filterProperties={[
{
key: 'id',
propertyLabel: 'ID',
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

propertyLabel: 'ID' is user-facing text in the filter UI but is hard-coded. Please use an existing i18n key (e.g., a session/general ID label) via t(...) so this label is translated like the other filter property labels.

Suggested change
propertyLabel: 'ID',
propertyLabel: t('general.ID'),

Copilot uses AI. Check for mistakes.
@yomybaby
Copy link
Copy Markdown
Member Author

Security & Performance Review

Comprehensive review of the 717 added lines across ProjectAdminSessionsPage.tsx, ProjectSessionV2Nodes.tsx, useWebUIMenuItems.tsx, routes.tsx, and the i18n key.

Summary: 0 Critical, 0 High, 1 Medium, 3 Low. 2 fixed in 58a5757ee, 2 left as code-quality notes.

Findings

# Severity File:Line Finding Status
1 Medium react/src/components/ProjectSessionV2Nodes.tsx:284-291 Elapsed Time column was computed once per render. Without an interval timer, it only refreshed when the page refetched (15 s auto-refresh tick), producing a visibly stale clock for running sessions. The v1 SessionNodes uses BAIIntervalView with a 1 s tick; the v2 page now matches that behavior, with an early return when terminatedAt is set so terminated rows do not schedule unnecessary timers. Fixed
2 Low react/src/components/ProjectSessionV2Nodes.tsx:201 Redundant as SessionV2Status cast inside _.includes(TERMINATED_STATUSES, status as SessionV2Status). status is already typed as SessionV2Status | null, so the assertion was a no-op. Fixed
3 Low react/src/components/ProjectSessionV2Nodes.tsx:137-184 Fragment uses @required(action: NONE) only on id. Other nullable fields used downstream (e.g. metadata, lifecycle, resource, metadata.name, lifecycle.status) rely on optional chaining and ?? '-' fallbacks. Functional impact is nil because the renderers are already null-safe; flagging for awareness, not blocking. Left (defensive code is sufficient)
4 Low react/src/pages/ProjectAdminSessionsPage.tsx:196-199 message.error(error.message) directly surfaces the raw GraphQL error message to the user, which can leak server-side internals on unexpected failures. This mirrors the pattern used elsewhere in the codebase (e.g. existing session/serving termination flows), so changing it in this PR alone would be inconsistent. Worth a separate cross-cutting issue if we want to standardize on a sanitized fallback. Left (codebase-wide pattern)

Areas explicitly checked and found clean

Security

  • Relay query variables: projectId, filter, orderBy, limit, offset all flow through Relay variables — no string concatenation, no template-literal injection. BAIGraphQLPropertyFilter shape is consumed as a typed object.
  • Terminate TOCTOU: handleTerminate closes over the specific session.id at modal-open time, then dispatches terminateProjectSessionsV2 with that captured id and forced: false. The mutation is locked to scope: { projectId }, so even a stale id cannot escape the project boundary. Modal confirmation is required before the network call.
  • XSS: no dangerouslySetInnerHTML, no href/template-literal sinks; user-controlled fields (metadata.name, images.edges[].node.identity.canonicalName, user.basicInfo.email, lifecycle.status) are rendered as JSX text via <BAINameActionCell>, <BAITag>, or plain expressions, all of which are auto-escaped by React.
  • Menu / route gating: 3-tier gating in useWebUIMenuItems (effectiveAdminRole) lists project-admin-sessions in VALID_MENU_KEYS, ALL_ADMIN_PAGE_KEYS, and PROJECT_ADMIN_PAGE_KEYS. isCurrentPageUnauthorized correctly rejects non-admins, gates project admins to the project-admin subset, and lets domain/super admins through. The routes.tsx entry sits inside MainLayout, so the same PageAccessGuard applies — page is unreachable to non-admins.
  • PII in logs: no console.log/useBAILogger calls were introduced. Error toast can leak server-side message text (see finding Adopt Electron shell #4).

Performance

  • useDeferredValue already wraps both queryVariables and fetchKey in ProjectAdminSessionsPage.tsx.
  • 'use memo' directives present in both new components, so React Compiler covers per-render allocations.
  • fetchPolicy switches between store-and-network (initial) and network-only (post-refetch) based on INITIAL_FETCH_KEY — a reasonable choice that avoids serving stale data after an explicit refresh while still hydrating from cache on first paint.
  • Auto-refresh via BAIFetchKeyButton autoUpdateDelay={15_000} mirrors sibling pages (ProjectAdminUsersPage).

Relay/GraphQL correctness

  • convertToOrderBy<Required<SessionV2OrderBy>>(queryParams.order) handles null/undefined (returns undefined), and the call site falls back to [{ field: 'CREATED_AT', direction: 'DESC' }].
  • Terminate mutation does not need a manual updater: invalidation is driven by updateFetchKey() which forces a refetch. The mutation response itself returns counts (cancelled, terminating, forceTerminated, skipped) but no list deltas, so cache surgery would be more brittle than the current refetch-on-success approach.
  • handleTerminate's Promise resolves on both onCompleted and onError, which is intentional so Modal.confirm always closes.

Verification

bash scripts/verify.sh after the fix:

=== Relay === --- Relay: PASS ---
=== Lint  === --- Lint: PASS ---
=== Format=== --- Format: PASS ---
=== TypeScript === --- TypeScript: PASS ---
=== ALL PASS ===

Additional commit: 58a5757ee.

@yomybaby yomybaby force-pushed the 04-14-feat_fr-2576_add_project_admin_sessions_page_using_projectsessionsv2 branch from 58a5757 to 749274a Compare April 23, 2026 09:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:i18n Localization area:ux UI / UX issue. size:XL 500~ LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Project Admin Sessions page using projectSessionsV2

2 participants