Skip to content

Commit ffb3cbd

Browse files
committed
frontend: TopBar: Display user info from endpoint in user menu
1 parent 9a6761c commit ffb3cbd

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

frontend/src/components/App/TopBar.tsx

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,59 @@ export default function TopBar({}: TopBarProps) {
8787
const history = useHistory();
8888
const { appBarActions, appBarActionsProcessors } = useAppBarActionsProcessed();
8989

90+
// Fetch current user info from backend
91+
const [me, setMe] = React.useState<{ username?: string; email?: string } | null>(null);
92+
React.useEffect(() => {
93+
let aborted = false;
94+
async function loadMe() {
95+
if (!cluster) {
96+
setMe(null);
97+
return;
98+
}
99+
100+
const headlampBaseUrl = (window as any).headlampBaseUrl || '';
101+
const isFile = typeof window !== 'undefined' && window.location?.protocol === 'file:';
102+
const base = isFile ? 'http://localhost:4466/' : headlampBaseUrl || '';
103+
const apiBase = base === '' ? '/' : base.replace(/\/?$/, '/');
104+
105+
try {
106+
// Use per-cluster endpoint so cookies stay scoped with cluster name in the path
107+
const url = `${apiBase}cluster/${cluster}/me`;
108+
const res = await fetch(url, {
109+
credentials: 'include',
110+
cache: 'no-store',
111+
headers: {
112+
'Cache-Control': 'no-cache',
113+
},
114+
});
115+
116+
if (res.status === 304) {
117+
// Keep previous user info when the backend signals "not modified".
118+
return;
119+
}
120+
121+
if (!res.ok) {
122+
if (!aborted) {
123+
setMe(null);
124+
}
125+
return;
126+
}
127+
128+
const data = await res.json();
129+
130+
if (!aborted) {
131+
setMe({ username: data.username, email: data.email });
132+
}
133+
} catch {
134+
if (!aborted) setMe(null);
135+
}
136+
}
137+
loadMe();
138+
return () => {
139+
aborted = true;
140+
};
141+
}, [cluster]);
142+
90143
const logoutCallback = useCallback(async () => {
91144
if (!!cluster) {
92145
await logout(cluster);
@@ -118,6 +171,7 @@ export default function TopBar({}: TopBarProps) {
118171
onToggleOpen={handletoggleOpen}
119172
cluster={cluster || undefined}
120173
clusters={clustersConfig || undefined}
174+
userInfo={me || undefined}
121175
/>
122176
);
123177
}
@@ -134,6 +188,7 @@ export interface PureTopBarProps {
134188
cluster?: string;
135189
isSidebarOpen?: boolean;
136190
isSidebarOpenUserSelected?: boolean;
191+
userInfo?: { username?: string; email?: string };
137192

138193
/** Called when sidebar toggles between open and closed. */
139194
onToggleOpen: () => void;
@@ -210,6 +265,7 @@ export const PureTopBar = memo(
210265
isSidebarOpen,
211266
isSidebarOpenUserSelected,
212267
onToggleOpen,
268+
userInfo,
213269
}: PureTopBarProps) => {
214270
const { t } = useTranslation();
215271
const theme = useTheme();
@@ -243,6 +299,11 @@ export const PureTopBar = memo(
243299
setMobileMoreAnchorEl(event.currentTarget);
244300
};
245301
const userMenuId = 'primary-user-menu';
302+
const userDisplayName = userInfo?.username || userInfo?.email || '';
303+
const userSecondaryInfo =
304+
userInfo?.username && userInfo?.email && userInfo.username !== userInfo.email
305+
? userInfo.email
306+
: undefined;
246307

247308
const renderUserMenu = !!isClusterContext && (
248309
<Menu
@@ -262,6 +323,26 @@ export const PureTopBar = memo(
262323
},
263324
}}
264325
>
326+
{userInfo && (
327+
<MenuItem
328+
disableRipple
329+
sx={{
330+
pointerEvents: 'none',
331+
cursor: 'default',
332+
'&:hover': { backgroundColor: 'inherit' },
333+
}}
334+
>
335+
<ListItemIcon>
336+
<Icon icon="mdi:account" />
337+
</ListItemIcon>
338+
<ListItemText
339+
primaryTypographyProps={{ variant: 'subtitle2' }}
340+
secondaryTypographyProps={{ variant: 'body2' }}
341+
primary={userDisplayName}
342+
secondary={userSecondaryInfo}
343+
/>
344+
</MenuItem>
345+
)}
265346
<MenuItem
266347
component="a"
267348
onClick={async () => {

frontend/vite.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ export default defineConfig({
2929
server: {
3030
port: 3000,
3131
proxy: {
32+
'/api': {
33+
target: 'http://localhost:4466',
34+
changeOrigin: true,
35+
},
36+
'/clusters': {
37+
target: 'http://localhost:4466',
38+
changeOrigin: true,
39+
},
3240
'/plugins': {
3341
target: 'http://localhost:4466',
3442
changeOrigin: true,

0 commit comments

Comments
 (0)