Skip to content

Commit 391d1c5

Browse files
authored
Merge pull request #13 from CryptoGnome/claude/issue-5-20250928-1133
Fix configuration persistence and dashboard authentication issues
2 parents bb16c35 + 023c44b commit 391d1c5

File tree

10 files changed

+106
-22
lines changed

10 files changed

+106
-22
lines changed

src/app/api/auth/check/route.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { NextResponse } from 'next/server';
2+
import { configLoader } from '@/lib/config/configLoader';
3+
4+
export async function GET() {
5+
try {
6+
// Load config to check if password is set
7+
const config = await configLoader.loadConfig();
8+
const dashboardPassword = config.global?.server?.dashboardPassword;
9+
10+
return NextResponse.json({
11+
passwordRequired: !!dashboardPassword && dashboardPassword.length > 0,
12+
});
13+
} catch (error) {
14+
console.error('Failed to check auth status:', error);
15+
return NextResponse.json({ passwordRequired: false });
16+
}
17+
}

src/app/api/auth/logout/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import { NextRequest, NextResponse } from 'next/server';
33
export async function POST(_request: NextRequest) {
44
const response = NextResponse.json({ success: true });
55

6-
// Delete the auth cookie
6+
// Delete the auth cookies
77
response.cookies.delete('auth-token');
8+
response.cookies.delete('password-required');
89

910
return response;
1011
}

src/components/AuthCheck.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use client';
2+
3+
import { useEffect } from 'react';
4+
import { useRouter } from 'next/navigation';
5+
6+
export function AuthCheck() {
7+
const router = useRouter();
8+
9+
useEffect(() => {
10+
const checkAuth = async () => {
11+
try {
12+
// Check if password is required
13+
const response = await fetch('/api/auth/check');
14+
const data = await response.json();
15+
16+
// Set cookie to indicate if password is required
17+
if (data.passwordRequired) {
18+
document.cookie = 'password-required=true; path=/; max-age=86400'; // 24 hours
19+
20+
// Check if we have a valid auth token
21+
const authToken = document.cookie
22+
.split('; ')
23+
.find(row => row.startsWith('auth-token='));
24+
25+
if (!authToken) {
26+
// No auth token, redirect to login
27+
router.push('/login?redirect=' + encodeURIComponent(window.location.pathname));
28+
}
29+
} else {
30+
// No password required, clear the cookie
31+
document.cookie = 'password-required=false; path=/; max-age=86400';
32+
}
33+
} catch (error) {
34+
console.error('Failed to check auth status:', error);
35+
}
36+
};
37+
38+
checkAuth();
39+
}, [router]);
40+
41+
return null; // This component doesn't render anything
42+
}

src/components/ConfigProvider.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ export default function ConfigProvider({ children }: { children: React.ReactNode
6464
server: {
6565
dashboardPassword: "",
6666
dashboardPort: 3000,
67-
websocketPort: 8080
67+
websocketPort: 8080,
68+
useRemoteWebSocket: false,
69+
websocketHost: null
6870
}
6971
},
7072
version: "1.1.0"

src/components/dashboard-layout.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Separator } from "@/components/ui/separator"
1414
import { Button } from "@/components/ui/button"
1515
import { LogOut } from "lucide-react"
1616
import { useConfig } from "@/components/ConfigProvider"
17+
import { AuthCheck } from "@/components/AuthCheck"
1718

1819
interface DashboardLayoutProps {
1920
children: React.ReactNode
@@ -42,7 +43,9 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
4243
};
4344

4445
return (
45-
<SidebarProvider>
46+
<>
47+
<AuthCheck />
48+
<SidebarProvider>
4649
<AppSidebar />
4750
<SidebarInset>
4851
<header className="flex h-12 shrink-0 items-center gap-2 border-b px-4">
@@ -123,6 +126,7 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
123126
{children}
124127
</main>
125128
</SidebarInset>
126-
</SidebarProvider>
129+
</SidebarProvider>
130+
</>
127131
)
128132
}

src/hooks/useWebSocketUrl.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ export function useWebSocketUrl() {
88
fetch('/api/config')
99
.then(res => res.json())
1010
.then(data => {
11-
const port = data.config?.global?.server?.websocketPort || 8080;
12-
const useRemoteWebSocket = data.config?.global?.server?.useRemoteWebSocket || false;
13-
const configHost = data.config?.global?.server?.websocketHost;
11+
const port = data.global?.server?.websocketPort || 8080;
12+
const useRemoteWebSocket = data.global?.server?.useRemoteWebSocket || false;
13+
const configHost = data.global?.server?.websocketHost;
1414

1515
// Determine the host based on configuration
1616
let host = 'localhost'; // default

src/lib/config/defaults.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ export const DEFAULT_CONFIG: Config = {
4444
paperMode: true,
4545
positionMode: 'HEDGE',
4646
maxOpenPositions: 10,
47+
server: {
48+
dashboardPassword: '',
49+
dashboardPort: 3000,
50+
websocketPort: 8080,
51+
useRemoteWebSocket: false,
52+
websocketHost: null,
53+
},
4754
},
4855
version: DEFAULT_CONFIG_VERSION,
4956
};

src/lib/config/migrator.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,18 @@ export function addMissingFields(userConfig: any, defaultConfig: any): any {
129129
if (!(field in result.global)) {
130130
result.global[field] = defaultConfig.global[field];
131131
console.log(`Added missing global field: ${field}`);
132+
} else if (field === 'server' && typeof defaultConfig.global[field] === 'object' && typeof result.global[field] === 'object') {
133+
// Merge server config nested fields
134+
const defaultServer = defaultConfig.global.server;
135+
const userServer = result.global.server || {};
136+
result.global.server = { ...defaultServer, ...userServer };
137+
138+
// Log any new server fields that were added
139+
for (const serverField in defaultServer) {
140+
if (!(serverField in userServer)) {
141+
console.log(`Added missing server field: ${serverField}`);
142+
}
143+
}
132144
}
133145
}
134146
}

src/lib/config/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export const serverConfigSchema = z.object({
4444
dashboardPassword: z.string().optional(),
4545
dashboardPort: z.number().optional(),
4646
websocketPort: z.number().optional(),
47+
useRemoteWebSocket: z.boolean().optional(),
48+
websocketHost: z.string().nullable().optional(),
4749
}).optional();
4850

4951
export const globalConfigSchema = z.object({

src/middleware.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NextResponse } from 'next/server';
22
import type { NextRequest } from 'next/server';
33

4-
const PUBLIC_PATHS = ['/login', '/api/auth', '/api/health'];
4+
const PUBLIC_PATHS = ['/login', '/api/auth', '/api/health', '/api/config'];
55

66
export async function middleware(request: NextRequest) {
77
const pathname = request.nextUrl.pathname;
@@ -13,27 +13,24 @@ export async function middleware(request: NextRequest) {
1313

1414
// Check for authentication cookie
1515
const authCookie = request.cookies.get('auth-token');
16+
const passwordSetCookie = request.cookies.get('password-required');
1617

17-
// Check if this looks like a valid auth token (basic validation)
18-
// Real validation happens in the API routes
18+
// If we have a valid auth token, allow access
1919
if (authCookie && authCookie.value && authCookie.value.startsWith('YXV0aGVudGljYXRlZDo')) {
2020
return NextResponse.next();
2121
}
2222

23-
// Check if we're in development mode (no auth required)
24-
if (process.env.NODE_ENV === 'development' && !authCookie) {
25-
// In development, only redirect if there's evidence a password was set
26-
// This is determined by the presence of a redirect parameter from a previous attempt
27-
const hasRedirect = request.nextUrl.searchParams.get('redirect');
28-
if (!hasRedirect) {
29-
return NextResponse.next();
30-
}
23+
// Check if password is required (based on cookie set by config check)
24+
if (passwordSetCookie && passwordSetCookie.value === 'true') {
25+
// Password is required but no valid auth token, redirect to login
26+
const loginUrl = new URL('/login', request.url);
27+
loginUrl.searchParams.set('redirect', pathname);
28+
return NextResponse.redirect(loginUrl);
3129
}
3230

33-
// Redirect to login page
34-
const loginUrl = new URL('/login', request.url);
35-
loginUrl.searchParams.set('redirect', pathname);
36-
return NextResponse.redirect(loginUrl);
31+
// No password required or not yet determined, allow access
32+
// The client will check and set the password-required cookie if needed
33+
return NextResponse.next();
3734
}
3835

3936
export const config = {

0 commit comments

Comments
 (0)