Skip to content

Commit ccbcdca

Browse files
authored
Merge pull request #241 from Community-VyProjects/feat/responsive-ui
feat: responsive UI overhaul and HTTPS auth fix (Edge cases)
2 parents 88d92c1 + 4238e4d commit ccbcdca

81 files changed

Lines changed: 2639 additions & 2187 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frontend/src/app/api/dashboard/[...path]/route.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ async function proxyRequest(
3636

3737
try {
3838
// Get the session token from request cookies
39-
const sessionToken = request.cookies.get("better-auth.session_token");
39+
// When BETTER_AUTH_SECURE_COOKIES=true (HTTPS), cookies are prefixed with __Secure-
40+
const sessionToken =
41+
request.cookies.get("__Secure-better-auth.session_token") ||
42+
request.cookies.get("better-auth.session_token");
4043

4144
// Build the backend URL
4245
const backendPath = `/dashboard/${path.join("/")}`;
@@ -51,7 +54,7 @@ async function proxyRequest(
5154
// Prepare headers
5255
const headers: HeadersInit = {};
5356

54-
// Add the session token cookie if it exists
57+
// Always forward as the non-prefixed name since the backend expects that
5558
if (sessionToken) {
5659
headers["Cookie"] = `better-auth.session_token=${sessionToken.value}`;
5760
}

frontend/src/app/api/session/[...path]/route.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ async function proxyRequest(
5252

5353
try {
5454
// Get the session token from request cookies
55-
const sessionToken = request.cookies.get("better-auth.session_token");
55+
// When BETTER_AUTH_SECURE_COOKIES=true (HTTPS), cookies are prefixed with __Secure-
56+
const sessionToken =
57+
request.cookies.get("__Secure-better-auth.session_token") ||
58+
request.cookies.get("better-auth.session_token");
5659

5760
// Build the backend URL
5861
const backendPath = `/session/${path.join("/")}`;
@@ -68,6 +71,7 @@ async function proxyRequest(
6871
const headers: HeadersInit = {};
6972

7073
// Add the session token cookie if it exists
74+
// Always forward as the non-prefixed name since the backend expects that
7175
if (sessionToken) {
7276
headers["Cookie"] = `better-auth.session_token=${sessionToken.value}`;
7377
}

frontend/src/app/api/user-management/[...path]/route.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ async function proxyRequest(
5252

5353
try {
5454
// Get the session token from request cookies
55-
const sessionToken = request.cookies.get("better-auth.session_token");
55+
// When BETTER_AUTH_SECURE_COOKIES=true (HTTPS), cookies are prefixed with __Secure-
56+
const sessionToken =
57+
request.cookies.get("__Secure-better-auth.session_token") ||
58+
request.cookies.get("better-auth.session_token");
5659

5760
// Build the backend URL
5861
const backendPath = `/user-management/${path.join("/")}`;
@@ -67,7 +70,7 @@ async function proxyRequest(
6770
// Prepare headers
6871
const headers: HeadersInit = {};
6972

70-
// Add the session token cookie if it exists
73+
// Always forward as the non-prefixed name since the backend expects that
7174
if (sessionToken) {
7275
headers["Cookie"] = `better-auth.session_token=${sessionToken.value}`;
7376
}

frontend/src/app/api/vyos/[...path]/route.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ async function proxyRequest(
6060

6161
try {
6262
// Get the session token from request cookies
63-
const sessionToken = request.cookies.get("better-auth.session_token");
63+
// When BETTER_AUTH_SECURE_COOKIES=true (HTTPS), cookies are prefixed with __Secure-
64+
const sessionToken =
65+
request.cookies.get("__Secure-better-auth.session_token") ||
66+
request.cookies.get("better-auth.session_token");
6467

6568
// Build the backend URL
6669
const backendPath = `/vyos/${path.join("/")}`;
@@ -75,7 +78,7 @@ async function proxyRequest(
7578
// Prepare headers
7679
const headers: HeadersInit = {};
7780

78-
// Add the session token cookie if it exists
81+
// Always forward as the non-prefixed name since the backend expects that
7982
if (sessionToken) {
8083
headers["Cookie"] = `better-auth.session_token=${sessionToken.value}`;
8184
}

frontend/src/app/firewall/bridge/page.tsx

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,14 @@ import {
5757
type BridgeChain,
5858
} from "@/lib/api/firewall-bridge";
5959
import { cn } from "@/lib/utils";
60+
import { LoadingState } from "@/components/ui/loading-state";
6061
import { CreateBridgeRuleModal } from "@/components/firewall/CreateBridgeRuleModal";
6162
import { EditBridgeRuleModal } from "@/components/firewall/EditBridgeRuleModal";
6263
import { DeleteBridgeRuleModal } from "@/components/firewall/DeleteBridgeRuleModal";
6364
import { CreateCustomBridgeChainModal } from "@/components/firewall/CreateCustomBridgeChainModal";
6465
import { DeleteCustomBridgeChainModal } from "@/components/firewall/DeleteCustomBridgeChainModal";
6566
import { BridgeRuleRow } from "@/components/firewall/BridgeRuleRow";
66-
import { BridgeReorderBanner } from "@/components/firewall/BridgeReorderBanner";
67+
import { ReorderBanner } from "@/components/ui/reorder-banner";
6768

6869
export default function BridgeFirewallPage() {
6970
const [config, setConfig] = useState<BridgeConfigResponse | null>(null);
@@ -331,18 +332,90 @@ export default function BridgeFirewallPage() {
331332
if (loading && !config) {
332333
return (
333334
<AppLayout>
334-
<div className="flex items-center justify-center h-96">
335-
<RefreshCw className="h-8 w-8 animate-spin text-muted-foreground" />
336-
</div>
335+
<LoadingState message="Loading bridge firewall configuration..." />
337336
</AppLayout>
338337
);
339338
}
340339

341340
return (
342341
<AppLayout>
343-
<div className="flex h-full">
342+
<div className="flex flex-col lg:flex-row h-full">
343+
{/* Mobile Chain Selector */}
344+
<div className="lg:hidden border-b border-border bg-card px-4 py-3">
345+
<div className="flex items-center gap-2 mb-2">
346+
<Network className="h-5 w-5 text-primary" />
347+
<h2 className="text-sm font-semibold text-foreground">Bridge Firewall</h2>
348+
</div>
349+
<div className="flex gap-2 overflow-x-auto pb-1">
350+
{/* Base chain pills */}
351+
<button
352+
onClick={() => handleChainSelect("forward", false)}
353+
className={cn(
354+
"flex-shrink-0 px-3 py-1.5 rounded-full text-xs font-medium transition-all",
355+
selectedChain === "forward" && !isCustomChain
356+
? "bg-primary text-primary-foreground"
357+
: "bg-muted text-muted-foreground hover:bg-accent"
358+
)}
359+
>
360+
Forward
361+
</button>
362+
{isV15 && (
363+
<>
364+
<button
365+
onClick={() => handleChainSelect("input", false)}
366+
className={cn(
367+
"flex-shrink-0 px-3 py-1.5 rounded-full text-xs font-medium transition-all",
368+
selectedChain === "input" && !isCustomChain
369+
? "bg-primary text-primary-foreground"
370+
: "bg-muted text-muted-foreground hover:bg-accent"
371+
)}
372+
>
373+
Input
374+
</button>
375+
<button
376+
onClick={() => handleChainSelect("output", false)}
377+
className={cn(
378+
"flex-shrink-0 px-3 py-1.5 rounded-full text-xs font-medium transition-all",
379+
selectedChain === "output" && !isCustomChain
380+
? "bg-primary text-primary-foreground"
381+
: "bg-muted text-muted-foreground hover:bg-accent"
382+
)}
383+
>
384+
Output
385+
</button>
386+
<button
387+
onClick={() => handleChainSelect("prerouting", false)}
388+
className={cn(
389+
"flex-shrink-0 px-3 py-1.5 rounded-full text-xs font-medium transition-all",
390+
selectedChain === "prerouting" && !isCustomChain
391+
? "bg-primary text-primary-foreground"
392+
: "bg-muted text-muted-foreground hover:bg-accent"
393+
)}
394+
>
395+
Prerouting
396+
</button>
397+
</>
398+
)}
399+
{/* Custom chain pills */}
400+
{customChains.map((chain) => (
401+
<button
402+
key={chain.name}
403+
onClick={() => handleChainSelect(chain.name, true)}
404+
className={cn(
405+
"flex-shrink-0 px-3 py-1.5 rounded-full text-xs font-medium transition-all",
406+
selectedChain === chain.name && isCustomChain
407+
? "bg-primary text-primary-foreground"
408+
: "bg-muted text-muted-foreground hover:bg-accent"
409+
)}
410+
>
411+
{chain.name}
412+
</button>
413+
))}
414+
</div>
415+
</div>
416+
344417
{/* Sidebar */}
345-
<div className="w-72 border-r border-border bg-card/50 flex flex-col h-full">
418+
<div className="w-72 border-r border-border bg-card/50 hidden lg:flex flex-col h-full">
346419
<div className="p-6 pb-4">
347420
<div className="flex items-center gap-3 mb-6">
348421
<div className="h-10 w-10 rounded-lg bg-primary/10 flex items-center justify-center">
@@ -571,7 +644,7 @@ export default function BridgeFirewallPage() {
571644
<div className="flex-1 flex flex-col overflow-hidden">
572645
{/* Reorder Banner */}
573646
{hasChanges && (
574-
<BridgeReorderBanner
647+
<ReorderBanner
575648
onSave={handleSaveReorder}
576649
onCancel={handleCancelReorder}
577650
saving={savingReorder}

frontend/src/app/firewall/flowtables/page.tsx

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
Plus,
2424
Search,
2525
RefreshCw,
26-
AlertCircle,
2726
Zap,
2827
MoreHorizontal,
2928
Pencil,
@@ -40,7 +39,9 @@ import {
4039
type FlowtablesCapabilities,
4140
} from "@/lib/api/firewall-flowtables";
4241
import { cn } from "@/lib/utils";
43-
import { LoadingSpinner } from "@/components/ui/loading-spinner";
42+
import { PageHeader } from "@/components/ui/page-header";
43+
import { LoadingState } from "@/components/ui/loading-state";
44+
import { ErrorState } from "@/components/ui/error-state";
4445
import { CreateFlowtableModal } from "@/components/firewall/CreateFlowtableModal";
4546
import { EditFlowtableModal } from "@/components/firewall/EditFlowtableModal";
4647
import { DeleteFlowtableModal } from "@/components/firewall/DeleteFlowtableModal";
@@ -119,33 +120,27 @@ export default function FlowtablesPage() {
119120
<div className="flex flex-col h-full">
120121
{/* Header */}
121122
<div className="border-b border-border bg-card/50 px-6 py-4">
122-
<div className="flex items-center justify-between">
123-
<div className="flex items-center gap-3">
124-
<div className="h-10 w-10 rounded-lg bg-primary/10 flex items-center justify-center">
125-
<Zap className="h-5 w-5 text-primary" />
123+
<PageHeader
124+
title="Flowtables"
125+
subtitle="Manage fast-path packet offloading for established connections"
126+
icon={<Zap className="h-5 w-5 text-primary" />}
127+
actions={
128+
<div className="flex items-center gap-2">
129+
<Button
130+
variant="outline"
131+
size="icon"
132+
onClick={() => fetchConfig(true)}
133+
disabled={loading}
134+
>
135+
<RefreshCw className={cn("h-4 w-4", loading && "animate-spin")} />
136+
</Button>
137+
<Button onClick={() => setCreateModalOpen(true)}>
138+
<Plus className="h-4 w-4 mr-2" />
139+
Create Flowtable
140+
</Button>
126141
</div>
127-
<div>
128-
<h1 className="text-2xl font-bold text-foreground">Flowtables</h1>
129-
<p className="text-sm text-muted-foreground">
130-
Manage fast-path packet offloading for established connections
131-
</p>
132-
</div>
133-
</div>
134-
<div className="flex items-center gap-2">
135-
<Button
136-
variant="outline"
137-
size="icon"
138-
onClick={() => fetchConfig(true)}
139-
disabled={loading}
140-
>
141-
<RefreshCw className={cn("h-4 w-4", loading && "animate-spin")} />
142-
</Button>
143-
<Button onClick={() => setCreateModalOpen(true)}>
144-
<Plus className="h-4 w-4 mr-2" />
145-
Create Flowtable
146-
</Button>
147-
</div>
148-
</div>
142+
}
143+
/>
149144

150145
{/* Search and Stats */}
151146
<div className="flex items-center gap-4 mt-4">
@@ -181,22 +176,13 @@ export default function FlowtablesPage() {
181176
{/* Content */}
182177
<div className="flex-1 overflow-auto p-6">
183178
{loading ? (
184-
<LoadingSpinner message="Loading flowtables configuration..." />
179+
<LoadingState message="Loading flowtables configuration..." />
185180
) : error ? (
186-
<div className="flex items-center justify-center h-full">
187-
<Card className="border-destructive max-w-md">
188-
<CardContent className="flex items-center gap-4 py-8">
189-
<AlertCircle className="h-8 w-8 text-destructive" />
190-
<div className="flex-1">
191-
<h3 className="font-semibold text-destructive">Error Loading Configuration</h3>
192-
<p className="text-sm text-muted-foreground mt-1">{error}</p>
193-
</div>
194-
<Button onClick={() => fetchConfig(true)} variant="outline">
195-
Try Again
196-
</Button>
197-
</CardContent>
198-
</Card>
199-
</div>
181+
<ErrorState
182+
title="Error Loading Configuration"
183+
message={error}
184+
onRetry={() => fetchConfig(true)}
185+
/>
200186
) : filteredFlowtables.length === 0 ? (
201187
<div className="flex items-center justify-center h-full">
202188
<Card className="max-w-md">

0 commit comments

Comments
 (0)