code cleanup

This commit is contained in:
Fran Jurmanović
2025-09-18 22:04:34 +02:00
parent b0ee67c2be
commit fe4d299eae
20 changed files with 753 additions and 250 deletions

View File

@@ -1,6 +1,6 @@
'use server';
import { revalidatePath } from 'next/cache';
import { revalidatePath, revalidateTag } from 'next/cache';
import { requireAuth } from '@/lib/auth/server';
import { startService, stopService, restartService } from '@/lib/api/server/servers';
@@ -10,6 +10,7 @@ export async function startServerAction(serverId: string) {
await startService(session.token!, serverId);
revalidatePath('/dashboard');
revalidatePath(`/dashboard/server/${serverId}`);
revalidateTag('/server');
} catch (error) {
return {
success: false,
@@ -28,6 +29,7 @@ export async function stopServerAction(serverId: string) {
await stopService(session.token!, serverId);
revalidatePath('/dashboard');
revalidatePath(`/dashboard/server/${serverId}`);
revalidateTag('/server');
} catch (error) {
return {
success: false,
@@ -46,6 +48,7 @@ export async function restartServerAction(serverId: string) {
await restartService(session.token!, serverId);
revalidatePath('/dashboard');
revalidatePath(`/dashboard/server/${serverId}`);
revalidateTag('/server');
} catch (error) {
return {
success: false,

View File

@@ -22,7 +22,8 @@ export async function fetchServerAPI<T>(
const response = await fetch(`${BASE_URL}${endpoint}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined
body: body ? JSON.stringify(body) : undefined,
next: { tags: [endpoint] }
});
if (!response.ok) {

View File

@@ -1,61 +1,61 @@
'use client';
import { createContext, useContext, useState, ReactNode } from 'react';
import { createContext, useContext, useState, ReactNode, useCallback } from 'react';
interface ServerCreationPopupState {
serverId: string;
serverName: string;
isOpen: boolean;
serverId: string;
serverName: string;
isOpen: boolean;
}
interface ServerCreationPopupContextType {
popup: ServerCreationPopupState | null;
showPopup: (serverId: string, serverName: string) => void;
hidePopup: () => void;
isPopupOpen: boolean;
popup: ServerCreationPopupState | null;
showPopup: (serverId: string, serverName: string) => void;
hidePopup: () => void;
isPopupOpen: boolean;
}
const ServerCreationPopupContext = createContext<ServerCreationPopupContextType | null>(null);
export function useServerCreationPopup() {
const context = useContext(ServerCreationPopupContext);
if (!context) {
throw new Error('useServerCreationPopup must be used within a ServerCreationPopupProvider');
}
return context;
const context = useContext(ServerCreationPopupContext);
if (!context) {
throw new Error('useServerCreationPopup must be used within a ServerCreationPopupProvider');
}
return context;
}
interface ServerCreationPopupProviderProps {
children: ReactNode;
children: ReactNode;
}
export function ServerCreationPopupProvider({ children }: ServerCreationPopupProviderProps) {
const [popup, setPopup] = useState<ServerCreationPopupState | null>(null);
const [popup, setPopup] = useState<ServerCreationPopupState | null>(null);
const showPopup = (serverId: string, serverName: string) => {
setPopup({
serverId,
serverName,
isOpen: true
});
};
const showPopup = useCallback((serverId: string, serverName: string) => {
setPopup({
serverId,
serverName,
isOpen: true
});
}, []);
const hidePopup = () => {
setPopup(null);
};
const hidePopup = useCallback(() => {
setPopup(null);
}, []);
const isPopupOpen = popup?.isOpen || false;
const isPopupOpen = popup?.isOpen || false;
return (
<ServerCreationPopupContext.Provider
value={{
popup,
showPopup,
hidePopup,
isPopupOpen
}}
>
{children}
</ServerCreationPopupContext.Provider>
);
return (
<ServerCreationPopupContext.Provider
value={{
popup,
showPopup,
hidePopup,
isPopupOpen
}}
>
{children}
</ServerCreationPopupContext.Provider>
);
}

View File

@@ -1,69 +1,79 @@
'use client';
import { createContext, useContext, useState, ReactNode, useEffect } from 'react';
import { createContext, useContext, useState, ReactNode, useEffect, useCallback } from 'react';
import { useWebSocket } from '@/lib/websocket/context';
import { WebSocketMessage, StepData } from '@/lib/websocket/client';
interface SteamCMDContextType {
isSteamCMDRunning: boolean;
runningSteamServers: Set<string>;
isSteamCMDRunning: boolean;
runningSteamServers: Set<string>;
dissociateServer: (serverId: string) => void;
}
const SteamCMDContext = createContext<SteamCMDContextType | null>(null);
export function useSteamCMD() {
const context = useContext(SteamCMDContext);
if (!context) {
throw new Error('useSteamCMD must be used within a SteamCMDProvider');
}
return context;
const context = useContext(SteamCMDContext);
if (!context) {
throw new Error('useSteamCMD must be used within a SteamCMDProvider');
}
return context;
}
interface SteamCMDProviderProps {
children: ReactNode;
children: ReactNode;
}
export function SteamCMDProvider({ children }: SteamCMDProviderProps) {
const [runningSteamServers, setRunningSteamServers] = useState<Set<string>>(new Set());
const { addMessageHandler, removeMessageHandler } = useWebSocket();
const [runningSteamServers, setRunningSteamServers] = useState<Set<string>>(new Set());
const { addMessageHandler, removeMessageHandler } = useWebSocket();
const isSteamCMDRunning = runningSteamServers.size > 0;
const isSteamCMDRunning = runningSteamServers.size > 0;
useEffect(() => {
const handleWebSocketMessage = (message: WebSocketMessage) => {
if (message.type === 'step') {
const data = message.data as StepData;
useEffect(() => {
const handleWebSocketMessage = (message: WebSocketMessage) => {
if (message.type === 'step') {
const data = message.data as StepData;
if (data.step === 'steam_download') {
setRunningSteamServers(prev => {
const newSet = new Set(prev);
if (data.step === 'steam_download') {
setRunningSteamServers((prev) => {
const newSet = new Set(prev);
if (data.status === 'in_progress') {
newSet.add(message.server_id);
} else if (data.status === 'completed' || data.status === 'failed') {
newSet.delete(message.server_id);
}
if (data.status === 'in_progress') {
newSet.add(message.server_id);
} else if (data.status === 'completed' || data.status === 'failed') {
newSet.delete(message.server_id);
}
return newSet;
});
}
}
};
return newSet;
});
}
}
};
addMessageHandler(handleWebSocketMessage);
return () => {
removeMessageHandler(handleWebSocketMessage);
};
}, [addMessageHandler, removeMessageHandler]);
addMessageHandler(handleWebSocketMessage);
return () => {
removeMessageHandler(handleWebSocketMessage);
};
}, [addMessageHandler, removeMessageHandler]);
return (
<SteamCMDContext.Provider
value={{
isSteamCMDRunning,
runningSteamServers
}}
>
{children}
</SteamCMDContext.Provider>
);
const dissociateServer = useCallback((serverId: string) => {
setRunningSteamServers((prev) => {
const newSet = new Set(prev);
newSet.delete(serverId);
return newSet;
});
}, []);
return (
<SteamCMDContext.Provider
value={{
isSteamCMDRunning,
runningSteamServers,
dissociateServer
}}
>
{children}
</SteamCMDContext.Provider>
);
}

View File

@@ -15,7 +15,6 @@ export enum ServerTab {
settings = 'settings'
}
// Configuration interfaces
export interface Configuration {
udpPort: number;
tcpPort: number;

View File

@@ -1,4 +1,3 @@
// Re-export all types for easier imports
export * from './server';
export * from './user';
export * from './config';

View File

@@ -20,7 +20,7 @@ export function hasPermission(user: User | null, permission: string): boolean {
if (!user || !user.role || !user.role.permissions) {
return false;
}
// Super Admins have all permissions
if (user.role.name === 'Super Admin') {
return true;
}

View File

@@ -1,4 +1,4 @@
const BASE_URL = process.env.NEXT_PUBLIC_WEBSOCKET_BASE_URL || 'ws://localhost:8080';
const BASE_URL = process.env.NEXT_PUBLIC_WEBSOCKET_URL || 'ws://localhost:3000/ws';
export interface WebSocketMessage {
type: 'step' | 'steam_output' | 'error' | 'complete';
@@ -52,8 +52,8 @@ export class WebSocketClient {
private connectionPromise: Promise<void> | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 10;
private reconnectDelay = 1000; // Start with 1 second
private maxReconnectDelay = 30000; // Max 30 seconds
private reconnectDelay = 1000; // 1 second
private maxReconnectDelay = 30000; // 30 seconds
private reconnectTimer: NodeJS.Timeout | null = null;
private shouldReconnect = true;
private associatedServerId: string | null = null;
@@ -80,7 +80,6 @@ export class WebSocketClient {
this.reconnectDelay = 5000;
this.notifyStatus('connected');
// Re-associate with server if we had one
if (this.associatedServerId) {
this.associateWithServer(this.associatedServerId);
}

View File

@@ -42,13 +42,16 @@ export function WebSocketProvider({ children }: WebSocketProviderProps) {
const connect = useCallback(
async (token: string) => {
if (client?.isConnected()) {
return;
}
if (client) {
client.disconnect();
}
const newClient = new WebSocketClient(token);
// Add connection status handler
const statusHandler: ConnectionStatusHandler = (status, error) => {
setConnectionStatus(status);
setIsConnected(status === 'connected');