Skip to content

Commit 20092f6

Browse files
committed
feat(site): add connection log page
1 parent 33d8acb commit 20092f6

25 files changed

+1370
-45
lines changed

site/src/api/api.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1802,6 +1802,14 @@ class ApiMethods {
18021802
return response.data;
18031803
};
18041804

1805+
getConnectionLogs = async (
1806+
options: TypesGen.ConnectionLogsRequest,
1807+
): Promise<TypesGen.ConnectionLogResponse> => {
1808+
const url = getURLWithSearchParams("/api/v2/connectionlogs", options);
1809+
const response = await this.axios.get(url);
1810+
return response.data;
1811+
};
1812+
18051813
getTemplateDAUs = async (
18061814
templateId: string,
18071815
): Promise<TypesGen.DAUsResponse> => {

site/src/api/queries/connectionlog.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { API } from "api/api";
2+
import type { ConnectionLogResponse } from "api/typesGenerated";
3+
import { useFilterParamsKey } from "components/Filter/Filter";
4+
import type { UsePaginatedQueryOptions } from "hooks/usePaginatedQuery";
5+
6+
export function paginatedConnectionLogs(
7+
searchParams: URLSearchParams,
8+
): UsePaginatedQueryOptions<ConnectionLogResponse, string> {
9+
return {
10+
searchParams,
11+
queryPayload: () => searchParams.get(useFilterParamsKey) ?? "",
12+
queryKey: ({ payload, pageNumber }) => {
13+
return ["connectionLogs", payload, pageNumber] as const;
14+
},
15+
queryFn: ({ payload, limit, offset }) => {
16+
return API.getConnectionLogs({
17+
offset,
18+
limit,
19+
q: payload,
20+
});
21+
},
22+
prefetch: false,
23+
};
24+
}

site/src/components/Filter/UserFilter.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,15 @@ export type UserFilterMenu = ReturnType<typeof useUserFilterMenu>;
8282

8383
interface UserMenuProps {
8484
menu: UserFilterMenu;
85+
placeholder?: string;
8586
width?: number;
8687
}
8788

88-
export const UserMenu: FC<UserMenuProps> = ({ menu, width }) => {
89+
export const UserMenu: FC<UserMenuProps> = ({ menu, width, placeholder }) => {
8990
return (
9091
<SelectFilter
9192
label="Select user"
92-
placeholder="All users"
93+
placeholder={placeholder ?? "All users"}
9394
emptyText="No users found"
9495
options={menu.searchOptions}
9596
onSelect={menu.selectOption}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Pill } from "components/Pill/Pill";
2+
import {
3+
Tooltip,
4+
TooltipContent,
5+
TooltipProvider,
6+
TooltipTrigger,
7+
} from "components/Tooltip/Tooltip";
8+
import type { FC } from "react";
9+
import { httpStatusColor } from "utils/http";
10+
11+
interface StatusPillProps {
12+
code: number;
13+
isHttpCode: boolean;
14+
label?: string;
15+
}
16+
17+
export const StatusPill: FC<StatusPillProps> = ({
18+
code,
19+
isHttpCode,
20+
label,
21+
}) => {
22+
const pill = (
23+
<Pill
24+
className="text-[10px] h-5 px-2.5 font-semibold"
25+
type={
26+
isHttpCode ? httpStatusColor(code) : code === 0 ? "success" : "error"
27+
}
28+
>
29+
{code.toString()}
30+
</Pill>
31+
);
32+
if (!label) {
33+
return pill;
34+
}
35+
return (
36+
<TooltipProvider>
37+
<Tooltip delayDuration={150}>
38+
<TooltipTrigger asChild>{pill}</TooltipTrigger>
39+
<TooltipContent>{label}</TooltipContent>
40+
</Tooltip>
41+
</TooltipProvider>
42+
);
43+
};

site/src/modules/dashboard/Navbar/DeploymentDropdown.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,22 @@ interface DeploymentDropdownProps {
1616
canViewDeployment: boolean;
1717
canViewOrganizations: boolean;
1818
canViewAuditLog: boolean;
19+
canViewConnectionLog: boolean;
1920
canViewHealth: boolean;
2021
}
2122

2223
export const DeploymentDropdown: FC<DeploymentDropdownProps> = ({
2324
canViewDeployment,
2425
canViewOrganizations,
2526
canViewAuditLog,
27+
canViewConnectionLog,
2628
canViewHealth,
2729
}) => {
2830
const theme = useTheme();
2931

3032
if (
3133
!canViewAuditLog &&
34+
!canViewConnectionLog &&
3235
!canViewOrganizations &&
3336
!canViewDeployment &&
3437
!canViewHealth
@@ -59,6 +62,7 @@ export const DeploymentDropdown: FC<DeploymentDropdownProps> = ({
5962
canViewDeployment={canViewDeployment}
6063
canViewOrganizations={canViewOrganizations}
6164
canViewAuditLog={canViewAuditLog}
65+
canViewConnectionLog={canViewConnectionLog}
6266
canViewHealth={canViewHealth}
6367
/>
6468
</PopoverContent>
@@ -71,6 +75,7 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
7175
canViewOrganizations,
7276
canViewAuditLog,
7377
canViewHealth,
78+
canViewConnectionLog,
7479
}) => {
7580
const popover = usePopover();
7681

@@ -108,6 +113,16 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
108113
Audit Logs
109114
</MenuItem>
110115
)}
116+
{canViewConnectionLog && (
117+
<MenuItem
118+
component={NavLink}
119+
to="/connectionlog"
120+
css={styles.menuItem}
121+
onClick={onPopoverClose}
122+
>
123+
Connection Logs
124+
</MenuItem>
125+
)}
111126
{canViewHealth && (
112127
<MenuItem
113128
component={NavLink}

site/src/modules/dashboard/Navbar/MobileMenu.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type MobileMenuPermissions = {
3737
canViewDeployment: boolean;
3838
canViewOrganizations: boolean;
3939
canViewAuditLog: boolean;
40+
canViewConnectionLog: boolean;
4041
canViewHealth: boolean;
4142
};
4243

@@ -192,6 +193,7 @@ const AdminSettingsSub: FC<MobileMenuPermissions> = ({
192193
canViewDeployment,
193194
canViewOrganizations,
194195
canViewAuditLog,
196+
canViewConnectionLog,
195197
canViewHealth,
196198
}) => {
197199
const [open, setOpen] = useState(false);
@@ -237,6 +239,14 @@ const AdminSettingsSub: FC<MobileMenuPermissions> = ({
237239
<Link to="/audit">Audit logs</Link>
238240
</DropdownMenuItem>
239241
)}
242+
{canViewConnectionLog && (
243+
<DropdownMenuItem
244+
asChild
245+
className={cn(itemStyles.default, itemStyles.sub)}
246+
>
247+
<Link to="/connectionlog">Connection logs</Link>
248+
</DropdownMenuItem>
249+
)}
240250
{canViewHealth && (
241251
<DropdownMenuItem
242252
asChild

site/src/modules/dashboard/Navbar/Navbar.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export const Navbar: FC = () => {
2222
const canViewHealth = permissions.viewDebugInfo;
2323
const canViewAuditLog =
2424
featureVisibility.audit_log && permissions.viewAnyAuditLog;
25+
const canViewConnectionLog =
26+
featureVisibility.connection_log && permissions.viewAnyConnectionLog;
2527

2628
return (
2729
<NavbarView
@@ -34,6 +36,7 @@ export const Navbar: FC = () => {
3436
canViewOrganizations={canViewOrganizations}
3537
canViewHealth={canViewHealth}
3638
canViewAuditLog={canViewAuditLog}
39+
canViewConnectionLog={canViewConnectionLog}
3740
proxyContextValue={proxyContextValue}
3841
/>
3942
);

site/src/modules/dashboard/Navbar/NavbarView.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe("NavbarView", () => {
3333
canViewOrganizations
3434
canViewHealth
3535
canViewAuditLog
36+
canViewConnectionLog
3637
/>,
3738
);
3839
const workspacesLink =
@@ -50,6 +51,7 @@ describe("NavbarView", () => {
5051
canViewOrganizations
5152
canViewHealth
5253
canViewAuditLog
54+
canViewConnectionLog
5355
/>,
5456
);
5557
const templatesLink =
@@ -67,6 +69,7 @@ describe("NavbarView", () => {
6769
canViewOrganizations
6870
canViewHealth
6971
canViewAuditLog
72+
canViewConnectionLog
7073
/>,
7174
);
7275
const deploymentMenu = await screen.findByText("Admin settings");
@@ -85,6 +88,7 @@ describe("NavbarView", () => {
8588
canViewOrganizations
8689
canViewHealth
8790
canViewAuditLog
91+
canViewConnectionLog
8892
/>,
8993
);
9094
const deploymentMenu = await screen.findByText("Admin settings");

site/src/modules/dashboard/Navbar/NavbarView.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ interface NavbarViewProps {
2424
canViewDeployment: boolean;
2525
canViewOrganizations: boolean;
2626
canViewAuditLog: boolean;
27+
canViewConnectionLog: boolean;
2728
canViewHealth: boolean;
2829
proxyContextValue?: ProxyContextValue;
2930
}
@@ -44,6 +45,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
4445
canViewOrganizations,
4546
canViewHealth,
4647
canViewAuditLog,
48+
canViewConnectionLog,
4749
proxyContextValue,
4850
}) => {
4951
const webPush = useWebpushNotifications();
@@ -73,6 +75,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
7375
canViewOrganizations={canViewOrganizations}
7476
canViewDeployment={canViewDeployment}
7577
canViewHealth={canViewHealth}
78+
canViewConnectionLog={canViewConnectionLog}
7679
/>
7780
</div>
7881

@@ -124,6 +127,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
124127
supportLinks={supportLinks}
125128
onSignOut={onSignOut}
126129
canViewAuditLog={canViewAuditLog}
130+
canViewConnectionLog={canViewConnectionLog}
127131
canViewOrganizations={canViewOrganizations}
128132
canViewDeployment={canViewDeployment}
129133
canViewHealth={canViewHealth}

site/src/modules/permissions/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ export const permissionChecks = {
156156
},
157157
action: "read",
158158
},
159+
viewAnyConnectionLog: {
160+
object: {
161+
resource_type: "connection_log",
162+
any_org: true,
163+
},
164+
action: "read",
165+
},
159166
viewDebugInfo: {
160167
object: {
161168
resource_type: "debug_info",

site/src/pages/AuditPage/AuditFilter.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,17 @@ export const useActionFilterMenu = ({
8282
value,
8383
onChange,
8484
}: Pick<UseFilterMenuOptions, "value" | "onChange">) => {
85-
const actionOptions: SelectFilterOption[] = AuditActions.map((action) => ({
86-
value: action,
87-
label: capitalize(action),
88-
}));
85+
const actionOptions: SelectFilterOption[] = AuditActions
86+
// TODO(ethanndickson): Logs with these action types are no longer produced.
87+
// Until we remove them from the database and API, we shouldn't suggest them
88+
// in the filter dropdown.
89+
.filter(
90+
(action) => !["connect", "disconnect", "open", "close"].includes(action),
91+
)
92+
.map((action) => ({
93+
value: action,
94+
label: capitalize(action),
95+
}));
8996
return useFilterMenu({
9097
onChange,
9198
value,

site/src/pages/AuditPage/AuditLogRow/AuditLogRow.tsx

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ import Tooltip from "@mui/material/Tooltip";
66
import type { AuditLog } from "api/typesGenerated";
77
import { Avatar } from "components/Avatar/Avatar";
88
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
9-
import { Pill } from "components/Pill/Pill";
109
import { Stack } from "components/Stack/Stack";
10+
import { StatusPill } from "components/StatusPill/StatusPill";
1111
import { TimelineEntry } from "components/Timeline/TimelineEntry";
1212
import { InfoIcon } from "lucide-react";
1313
import { NetworkIcon } from "lucide-react";
1414
import { type FC, useState } from "react";
1515
import { Link as RouterLink } from "react-router-dom";
16-
import type { ThemeRole } from "theme/roles";
1716
import userAgentParser from "ua-parser-js";
1817
import { AuditLogDescription } from "./AuditLogDescription/AuditLogDescription";
1918
import { AuditLogDiff } from "./AuditLogDiff/AuditLogDiff";
@@ -22,21 +21,6 @@ import {
2221
determineIdPSyncMappingDiff,
2322
} from "./AuditLogDiff/auditUtils";
2423

25-
const httpStatusColor = (httpStatus: number): ThemeRole => {
26-
// Treat server errors (500) as errors
27-
if (httpStatus >= 500) {
28-
return "error";
29-
}
30-
31-
// Treat client errors (400) as warnings
32-
if (httpStatus >= 400) {
33-
return "warning";
34-
}
35-
36-
// OK (200) and redirects (300) are successful
37-
return "success";
38-
};
39-
4024
interface AuditLogRowProps {
4125
auditLog: AuditLog;
4226
// Useful for Storybook
@@ -139,7 +123,7 @@ export const AuditLogRow: FC<AuditLogRowProps> = ({
139123
</Stack>
140124

141125
<Stack direction="row" alignItems="center">
142-
<StatusPill code={auditLog.status_code} />
126+
<StatusPill isHttpCode={true} code={auditLog.status_code} />
143127

144128
{/* With multi-org, there is not enough space so show
145129
everything in a tooltip. */}
@@ -243,19 +227,6 @@ export const AuditLogRow: FC<AuditLogRowProps> = ({
243227
);
244228
};
245229

246-
function StatusPill({ code }: { code: number }) {
247-
const isHttp = code >= 100;
248-
249-
return (
250-
<Pill
251-
css={styles.statusCodePill}
252-
type={isHttp ? httpStatusColor(code) : code === 0 ? "success" : "error"}
253-
>
254-
{code.toString()}
255-
</Pill>
256-
);
257-
}
258-
259230
const styles = {
260231
auditLogCell: {
261232
padding: "0 !important",
@@ -311,14 +282,6 @@ const styles = {
311282
width: "100%",
312283
},
313284

314-
statusCodePill: {
315-
fontSize: 10,
316-
height: 20,
317-
paddingLeft: 10,
318-
paddingRight: 10,
319-
fontWeight: 600,
320-
},
321-
322285
deletedLabel: (theme) => ({
323286
...(theme.typography.caption as CSSObject),
324287
color: theme.palette.text.secondary,

0 commit comments

Comments
 (0)