mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-27 07:16:40 +00:00
🚧WIP: Separate user & machine clients
This commit is contained in:
@@ -1166,6 +1166,8 @@
|
|||||||
"sidebarIdentityProviders": "Identity Providers",
|
"sidebarIdentityProviders": "Identity Providers",
|
||||||
"sidebarLicense": "License",
|
"sidebarLicense": "License",
|
||||||
"sidebarClients": "Clients",
|
"sidebarClients": "Clients",
|
||||||
|
"sidebarUserDevices": "User devices",
|
||||||
|
"sidebarMachineClients": "Machine Clients",
|
||||||
"sidebarDomains": "Domains",
|
"sidebarDomains": "Domains",
|
||||||
"sidebarBluePrints": "Blueprints",
|
"sidebarBluePrints": "Blueprints",
|
||||||
"blueprints": "Blueprints",
|
"blueprints": "Blueprints",
|
||||||
|
|||||||
92
src/app/[orgId]/settings/clients/machine/page.tsx
Normal file
92
src/app/[orgId]/settings/clients/machine/page.tsx
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import { internal } from "@app/lib/api";
|
||||||
|
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||||
|
import { AxiosResponse } from "axios";
|
||||||
|
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||||
|
import { ListClientsResponse } from "@server/routers/client";
|
||||||
|
import { getTranslations } from "next-intl/server";
|
||||||
|
import type { ClientRow } from "@app/components/ClientsTable";
|
||||||
|
import ClientsTable from "@app/components/ClientsTable";
|
||||||
|
|
||||||
|
type ClientsPageProps = {
|
||||||
|
params: Promise<{ orgId: string }>;
|
||||||
|
searchParams: Promise<{ view?: string }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
export default async function ClientsPage(props: ClientsPageProps) {
|
||||||
|
const t = await getTranslations();
|
||||||
|
|
||||||
|
const params = await props.params;
|
||||||
|
const searchParams = await props.searchParams;
|
||||||
|
|
||||||
|
// Default to 'user' view, or use the query param if provided
|
||||||
|
let defaultView: "user" | "machine" = "user";
|
||||||
|
defaultView = searchParams.view === "machine" ? "machine" : "user";
|
||||||
|
|
||||||
|
let userClients: ListClientsResponse["clients"] = [];
|
||||||
|
let machineClients: ListClientsResponse["clients"] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [userRes, machineRes] = await Promise.all([
|
||||||
|
internal.get<AxiosResponse<ListClientsResponse>>(
|
||||||
|
`/org/${params.orgId}/clients?filter=user`,
|
||||||
|
await authCookieHeader()
|
||||||
|
),
|
||||||
|
internal.get<AxiosResponse<ListClientsResponse>>(
|
||||||
|
`/org/${params.orgId}/clients?filter=machine`,
|
||||||
|
await authCookieHeader()
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
userClients = userRes.data.data.clients;
|
||||||
|
machineClients = machineRes.data.data.clients;
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
function formatSize(mb: number): string {
|
||||||
|
if (mb >= 1024 * 1024) {
|
||||||
|
return `${(mb / (1024 * 1024)).toFixed(2)} TB`;
|
||||||
|
} else if (mb >= 1024) {
|
||||||
|
return `${(mb / 1024).toFixed(2)} GB`;
|
||||||
|
} else {
|
||||||
|
return `${mb.toFixed(2)} MB`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapClientToRow = (
|
||||||
|
client: ListClientsResponse["clients"][0]
|
||||||
|
): ClientRow => {
|
||||||
|
return {
|
||||||
|
name: client.name,
|
||||||
|
id: client.clientId,
|
||||||
|
subnet: client.subnet.split("/")[0],
|
||||||
|
mbIn: formatSize(client.megabytesIn || 0),
|
||||||
|
mbOut: formatSize(client.megabytesOut || 0),
|
||||||
|
orgId: params.orgId,
|
||||||
|
online: client.online,
|
||||||
|
olmVersion: client.olmVersion || undefined,
|
||||||
|
olmUpdateAvailable: client.olmUpdateAvailable || false,
|
||||||
|
userId: client.userId,
|
||||||
|
username: client.username,
|
||||||
|
userEmail: client.userEmail
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const userClientRows: ClientRow[] = userClients.map(mapClientToRow);
|
||||||
|
const machineClientRows: ClientRow[] = machineClients.map(mapClientToRow);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SettingsSectionTitle
|
||||||
|
title={t("manageClients")}
|
||||||
|
description={t("manageClientsDescription")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ClientsTable
|
||||||
|
userClients={userClientRows}
|
||||||
|
machineClients={machineClientRows}
|
||||||
|
orgId={params.orgId}
|
||||||
|
defaultView={defaultView}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
|||||||
import { ListClientsResponse } from "@server/routers/client";
|
import { ListClientsResponse } from "@server/routers/client";
|
||||||
import ClientsTable from "../../../../components/ClientsTable";
|
import ClientsTable from "../../../../components/ClientsTable";
|
||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations } from "next-intl/server";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
type ClientsPageProps = {
|
type ClientsPageProps = {
|
||||||
params: Promise<{ orgId: string }>;
|
params: Promise<{ orgId: string }>;
|
||||||
@@ -18,73 +19,6 @@ export default async function ClientsPage(props: ClientsPageProps) {
|
|||||||
const t = await getTranslations();
|
const t = await getTranslations();
|
||||||
|
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
const searchParams = await props.searchParams;
|
|
||||||
|
|
||||||
// Default to 'user' view, or use the query param if provided
|
redirect(`/${params.orgId}/settings/clients/user`);
|
||||||
let defaultView: "user" | "machine" = "user";
|
|
||||||
defaultView = searchParams.view === "machine" ? "machine" : "user";
|
|
||||||
|
|
||||||
let userClients: ListClientsResponse["clients"] = [];
|
|
||||||
let machineClients: ListClientsResponse["clients"] = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const [userRes, machineRes] = await Promise.all([
|
|
||||||
internal.get<AxiosResponse<ListClientsResponse>>(
|
|
||||||
`/org/${params.orgId}/clients?filter=user`,
|
|
||||||
await authCookieHeader()
|
|
||||||
),
|
|
||||||
internal.get<AxiosResponse<ListClientsResponse>>(
|
|
||||||
`/org/${params.orgId}/clients?filter=machine`,
|
|
||||||
await authCookieHeader()
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
userClients = userRes.data.data.clients;
|
|
||||||
machineClients = machineRes.data.data.clients;
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
function formatSize(mb: number): string {
|
|
||||||
if (mb >= 1024 * 1024) {
|
|
||||||
return `${(mb / (1024 * 1024)).toFixed(2)} TB`;
|
|
||||||
} else if (mb >= 1024) {
|
|
||||||
return `${(mb / 1024).toFixed(2)} GB`;
|
|
||||||
} else {
|
|
||||||
return `${mb.toFixed(2)} MB`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapClientToRow = (client: ListClientsResponse["clients"][0]): ClientRow => {
|
|
||||||
return {
|
|
||||||
name: client.name,
|
|
||||||
id: client.clientId,
|
|
||||||
subnet: client.subnet.split("/")[0],
|
|
||||||
mbIn: formatSize(client.megabytesIn || 0),
|
|
||||||
mbOut: formatSize(client.megabytesOut || 0),
|
|
||||||
orgId: params.orgId,
|
|
||||||
online: client.online,
|
|
||||||
olmVersion: client.olmVersion || undefined,
|
|
||||||
olmUpdateAvailable: client.olmUpdateAvailable || false,
|
|
||||||
userId: client.userId,
|
|
||||||
username: client.username,
|
|
||||||
userEmail: client.userEmail
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const userClientRows: ClientRow[] = userClients.map(mapClientToRow);
|
|
||||||
const machineClientRows: ClientRow[] = machineClients.map(mapClientToRow);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SettingsSectionTitle
|
|
||||||
title={t("manageClients")}
|
|
||||||
description={t("manageClientsDescription")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ClientsTable
|
|
||||||
userClients={userClientRows}
|
|
||||||
machineClients={machineClientRows}
|
|
||||||
orgId={params.orgId}
|
|
||||||
defaultView={defaultView}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
92
src/app/[orgId]/settings/clients/user/page.tsx
Normal file
92
src/app/[orgId]/settings/clients/user/page.tsx
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import { internal } from "@app/lib/api";
|
||||||
|
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||||
|
import { AxiosResponse } from "axios";
|
||||||
|
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||||
|
import { ListClientsResponse } from "@server/routers/client";
|
||||||
|
import { getTranslations } from "next-intl/server";
|
||||||
|
import type { ClientRow } from "@app/components/ClientsTable";
|
||||||
|
import ClientsTable from "@app/components/ClientsTable";
|
||||||
|
|
||||||
|
type ClientsPageProps = {
|
||||||
|
params: Promise<{ orgId: string }>;
|
||||||
|
searchParams: Promise<{ view?: string }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
export default async function ClientsPage(props: ClientsPageProps) {
|
||||||
|
const t = await getTranslations();
|
||||||
|
|
||||||
|
const params = await props.params;
|
||||||
|
const searchParams = await props.searchParams;
|
||||||
|
|
||||||
|
// Default to 'user' view, or use the query param if provided
|
||||||
|
let defaultView: "user" | "machine" = "user";
|
||||||
|
defaultView = searchParams.view === "machine" ? "machine" : "user";
|
||||||
|
|
||||||
|
let userClients: ListClientsResponse["clients"] = [];
|
||||||
|
let machineClients: ListClientsResponse["clients"] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [userRes, machineRes] = await Promise.all([
|
||||||
|
internal.get<AxiosResponse<ListClientsResponse>>(
|
||||||
|
`/org/${params.orgId}/clients?filter=user`,
|
||||||
|
await authCookieHeader()
|
||||||
|
),
|
||||||
|
internal.get<AxiosResponse<ListClientsResponse>>(
|
||||||
|
`/org/${params.orgId}/clients?filter=machine`,
|
||||||
|
await authCookieHeader()
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
userClients = userRes.data.data.clients;
|
||||||
|
machineClients = machineRes.data.data.clients;
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
function formatSize(mb: number): string {
|
||||||
|
if (mb >= 1024 * 1024) {
|
||||||
|
return `${(mb / (1024 * 1024)).toFixed(2)} TB`;
|
||||||
|
} else if (mb >= 1024) {
|
||||||
|
return `${(mb / 1024).toFixed(2)} GB`;
|
||||||
|
} else {
|
||||||
|
return `${mb.toFixed(2)} MB`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapClientToRow = (
|
||||||
|
client: ListClientsResponse["clients"][0]
|
||||||
|
): ClientRow => {
|
||||||
|
return {
|
||||||
|
name: client.name,
|
||||||
|
id: client.clientId,
|
||||||
|
subnet: client.subnet.split("/")[0],
|
||||||
|
mbIn: formatSize(client.megabytesIn || 0),
|
||||||
|
mbOut: formatSize(client.megabytesOut || 0),
|
||||||
|
orgId: params.orgId,
|
||||||
|
online: client.online,
|
||||||
|
olmVersion: client.olmVersion || undefined,
|
||||||
|
olmUpdateAvailable: client.olmUpdateAvailable || false,
|
||||||
|
userId: client.userId,
|
||||||
|
username: client.username,
|
||||||
|
userEmail: client.userEmail
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const userClientRows: ClientRow[] = userClients.map(mapClientToRow);
|
||||||
|
const machineClientRows: ClientRow[] = machineClients.map(mapClientToRow);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SettingsSectionTitle
|
||||||
|
title={t("manageClients")}
|
||||||
|
description={t("manageClientsDescription")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ClientsTable
|
||||||
|
userClients={userClientRows}
|
||||||
|
machineClients={machineClientRows}
|
||||||
|
orgId={params.orgId}
|
||||||
|
defaultView={defaultView}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -18,7 +18,8 @@ import {
|
|||||||
Logs,
|
Logs,
|
||||||
SquareMousePointer,
|
SquareMousePointer,
|
||||||
ScanEye,
|
ScanEye,
|
||||||
GlobeLock
|
GlobeLock,
|
||||||
|
Smartphone
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
export type SidebarNavSection = {
|
export type SidebarNavSection = {
|
||||||
@@ -73,9 +74,22 @@ export const orgNavSections = (
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
title: "sidebarClients",
|
title: "sidebarClients",
|
||||||
href: "/{orgId}/settings/clients",
|
|
||||||
icon: <MonitorUp className="size-4 flex-none" />,
|
icon: <MonitorUp className="size-4 flex-none" />,
|
||||||
isBeta: true
|
isBeta: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
href: "/{orgId}/settings/clients/user",
|
||||||
|
title: "sidebarUserDevices",
|
||||||
|
icon: (
|
||||||
|
<Smartphone className="size-4 flex-none" />
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/{orgId}/settings/clients/machine",
|
||||||
|
title: "sidebarMachineClients",
|
||||||
|
icon: <Server className="size-4 flex-none" />
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
|||||||
Reference in New Issue
Block a user