Merge branch 'dev' into feat/ip-filtering

This commit is contained in:
Fred KISSIE
2026-08-24 22:06:55 +02:00
38 changed files with 410 additions and 97 deletions
@@ -8,6 +8,8 @@ import ExitNodesTable, {
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { getTranslations } from "next-intl/server";
import type { Metadata } from "next";
import { build } from "@server/build";
import { redirect } from "next/navigation";
export const metadata: Metadata = {
title: "Remote Exit Nodes"
@@ -22,6 +24,10 @@ export const dynamic = "force-dynamic";
export default async function RemoteExitNodesPage(
props: RemoteExitNodesPageProps
) {
if (build != "saas") {
redirect("/");
}
const params = await props.params;
let remoteExitNodes: ListRemoteExitNodesResponse["remoteExitNodes"] = [];
try {
@@ -88,8 +88,8 @@ export default async function ClientResourcesPage(
siteNiceIds: siteResource.siteNiceIds,
niceId: siteResource.niceId,
enabled: siteResource.enabled,
tcpPortRangeString: siteResource.tcpPortRangeString || null,
udpPortRangeString: siteResource.udpPortRangeString || null,
tcpPortRangeString: siteResource.tcpPortRangeString ?? null,
udpPortRangeString: siteResource.udpPortRangeString ?? null,
disableIcmp: siteResource.disableIcmp || false,
authDaemonMode: siteResource.authDaemonMode ?? null,
authDaemonPort: siteResource.authDaemonPort ?? null,
+5 -5
View File
@@ -79,7 +79,7 @@ function MessageBubble({ message }: { message: NormalizedAiMessage }) {
)}
</div>
<div
className={`max-w-[80%] rounded-lg px-3 py-2 text-sm whitespace-pre-wrap break-words ${
className={`min-w-0 max-w-[80%] rounded-lg px-3 py-2 text-sm whitespace-pre-wrap break-words ${
isUser
? "bg-primary text-primary-foreground"
: isTool
@@ -108,7 +108,7 @@ function RawFallbackBlock({
}) {
const pretty = prettyRaw(raw);
return (
<div className="rounded-md border bg-muted/30 p-3">
<div className="min-w-0 rounded-md border bg-muted/30 p-3">
<div className="mb-1 text-xs font-medium text-muted-foreground">
{label}
{pretty && unparsedLabel && (
@@ -117,7 +117,7 @@ function RawFallbackBlock({
</span>
)}
</div>
<pre className="max-h-64 overflow-auto whitespace-pre-wrap break-words text-xs text-muted-foreground">
<pre className="max-h-64 overflow-auto whitespace-pre-wrap break-all text-xs text-muted-foreground">
{pretty ?? noDataLabel}
</pre>
</div>
@@ -172,7 +172,7 @@ export function AiSessionChatView({
</Button>
</div>
{rawMode ? (
<div className="flex max-h-[32rem] flex-col gap-3 overflow-y-auto rounded-md border bg-background p-4">
<div className="flex min-w-0 max-h-[32rem] flex-col gap-3 overflow-y-auto rounded-md border bg-background p-4">
<RawFallbackBlock
label={t("aiSessionRequest")}
raw={normalizedRequest}
@@ -185,7 +185,7 @@ export function AiSessionChatView({
/>
</div>
) : (
<div className="flex max-h-[32rem] flex-col gap-3 overflow-y-auto rounded-md border bg-background p-4">
<div className="flex min-w-0 max-h-[32rem] flex-col gap-3 overflow-y-auto rounded-md border bg-background p-4">
{hasRequestMessages ? (
requestMessages!.map((message, i) => (
<MessageBubble key={`req-${i}`} message={message} />
+29 -1
View File
@@ -57,6 +57,7 @@ export interface Destination {
sendActionLogs: boolean;
sendConnectionLogs: boolean;
sendRequestLogs: boolean;
sendAISessionLogs: boolean;
lastError: string | null;
lastErrorAt: number | null;
createdAt: number;
@@ -180,6 +181,7 @@ export function HttpDestinationCredenza({
const [sendActionLogs, setSendActionLogs] = useState(false);
const [sendConnectionLogs, setSendConnectionLogs] = useState(false);
const [sendRequestLogs, setSendRequestLogs] = useState(false);
const [sendAISessionLogs, setSendAISessionLogs] = useState(false);
useEffect(() => {
if (open) {
@@ -190,6 +192,7 @@ export function HttpDestinationCredenza({
setSendActionLogs(editing?.sendActionLogs ?? false);
setSendConnectionLogs(editing?.sendConnectionLogs ?? false);
setSendRequestLogs(editing?.sendRequestLogs ?? false);
setSendAISessionLogs(editing?.sendAISessionLogs ?? false);
}
}, [open, editing]);
@@ -226,7 +229,8 @@ export function HttpDestinationCredenza({
sendAccessLogs,
sendActionLogs,
sendConnectionLogs,
sendRequestLogs
sendRequestLogs,
sendAISessionLogs
};
if (editing) {
await api.post(
@@ -778,6 +782,30 @@ export function HttpDestinationCredenza({
</p>
</div>
</div>
<div className="flex items-start gap-3 rounded-md border p-3">
<Checkbox
id="log-ai-session"
checked={sendAISessionLogs}
onCheckedChange={(v) =>
setSendAISessionLogs(v === true)
}
className="mt-0.5"
/>
<div>
<label
htmlFor="log-ai-session"
className="text-sm font-medium cursor-pointer"
>
{t("httpDestAISessionLogsTitle")}
</label>
<p className="text-xs text-muted-foreground mt-0.5">
{t(
"httpDestAISessionLogsDescription"
)}
</p>
</div>
</div>
</div>
</div>
</HorizontalTabs>
+16 -3
View File
@@ -313,6 +313,15 @@ export function LogDataTable<TData, TValue>({
}
}, [currentPage, table, isServerPagination]);
// Collapse any expanded rows whenever the page changes, since row ids
// are reused across pages and would otherwise show the wrong content
// in the same expanded position.
const pageIndex = table.getState().pagination.pageIndex;
useEffect(() => {
setExpandedRows(new Set());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageIndex]);
const handleTabChange = (value: string) => {
if (disabled) return;
@@ -515,9 +524,13 @@ export function LogDataTable<TData, TValue>({
}
className="p-4 bg-muted/50"
>
{renderExpandedRow(
row.original
)}
{/* w-0 min-w-full keeps this cell's content from */}
{/* blowing out the table's auto column widths */}
<div className="w-0 min-w-full">
{renderExpandedRow(
row.original
)}
</div>
</TableCell>
</TableRow>
)
+28 -1
View File
@@ -90,6 +90,7 @@ export function S3DestinationCredenza({
const [sendActionLogs, setSendActionLogs] = useState(false);
const [sendConnectionLogs, setSendConnectionLogs] = useState(false);
const [sendRequestLogs, setSendRequestLogs] = useState(false);
const [sendAISessionLogs, setSendAISessionLogs] = useState(false);
useEffect(() => {
if (open) {
@@ -98,6 +99,7 @@ export function S3DestinationCredenza({
setSendActionLogs(editing?.sendActionLogs ?? false);
setSendConnectionLogs(editing?.sendConnectionLogs ?? false);
setSendRequestLogs(editing?.sendRequestLogs ?? false);
setSendAISessionLogs(editing?.sendAISessionLogs ?? false);
}
}, [open, editing]);
@@ -121,7 +123,8 @@ export function S3DestinationCredenza({
sendAccessLogs,
sendActionLogs,
sendConnectionLogs,
sendRequestLogs
sendRequestLogs,
sendAISessionLogs
};
if (editing) {
await api.post(
@@ -510,6 +513,30 @@ export function S3DestinationCredenza({
</p>
</div>
</div>
<div className="flex items-start gap-3 rounded-md border p-3">
<Checkbox
id="s3-log-ai-session"
checked={sendAISessionLogs}
onCheckedChange={(v) =>
setSendAISessionLogs(v === true)
}
className="mt-0.5"
/>
<div>
<Label
htmlFor="s3-log-ai-session"
className="cursor-pointer font-medium"
>
{t("httpDestAISessionLogsTitle")}
</Label>
<p className="text-xs text-muted-foreground mt-0.5">
{t(
"httpDestAISessionLogsDescription"
)}
</p>
</div>
</div>
</div>
</div>
</HorizontalTabs>
@@ -129,6 +129,7 @@ export function ToggleableTrendChart(props: ToggleableTrendChartProps) {
payload?.[0]?.payload?.day
)
}
valueFormatter={valueFormatter}
/>
}
/>
@@ -172,6 +173,7 @@ export function ToggleableTrendChart(props: ToggleableTrendChartProps) {
payload?.[0]?.payload?.day
)
}
valueFormatter={valueFormatter}
/>
}
/>
+2 -1
View File
@@ -47,7 +47,8 @@ export function buildSeriesFromData(
export const currencyFormatter = new Intl.NumberFormat(undefined, {
style: "currency",
currency: "USD",
maximumFractionDigits: 2
minimumFractionDigits: 2,
maximumFractionDigits: 4
});
export const compactNumberFormatter = new Intl.NumberFormat(undefined, {
+16 -10
View File
@@ -135,6 +135,7 @@ type ChartTooltipContentProps = React.ComponentProps<"div"> & {
labelKey?: string;
color?: string;
labelClassName?: string;
valueFormatter?: (value: number) => string;
};
const ChartTooltipContent = React.forwardRef<
@@ -155,7 +156,8 @@ const ChartTooltipContent = React.forwardRef<
formatter,
color,
nameKey,
labelKey
labelKey,
valueFormatter
},
ref
) => {
@@ -302,19 +304,23 @@ const ChartTooltipContent = React.forwardRef<
item.name}
</span>
</div>
{item.value && (
{item.value !== undefined && (
<span className="font-mono font-medium tabular-nums text-foreground">
{!isNaN(
item.value as number
)
? new Intl.NumberFormat(
navigator.language,
{
maximumFractionDigits: 0
}
).format(
item.value as number
)
? valueFormatter
? valueFormatter(
item.value as number
)
: new Intl.NumberFormat(
navigator.language,
{
maximumFractionDigits: 0
}
).format(
item.value as number
)
: item.value.toLocaleString()}
</span>
)}
+2 -2
View File
@@ -43,8 +43,8 @@ export async function fetchSiteResourceByNiceId(
aliasAddress: match.aliasAddress || null,
siteNiceIds: match.siteNiceIds,
niceId: match.niceId,
tcpPortRangeString: match.tcpPortRangeString || null,
udpPortRangeString: match.udpPortRangeString || null,
tcpPortRangeString: match.tcpPortRangeString ?? null,
udpPortRangeString: match.udpPortRangeString ?? null,
disableIcmp: match.disableIcmp || false,
authDaemonMode: match.authDaemonMode ?? null,
authDaemonPort: match.authDaemonPort ?? null,