From 7957ed06e404542859714e18994f266addd69f68 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Thu, 21 Aug 2025 22:47:38 -0500 Subject: [PATCH 01/27] Fix localhost connection issues --- src/ui/Homepage/HomepageAuth.tsx | 118 +++++++------ src/ui/main-axios.ts | 284 ++++++++++++++++++++++--------- 2 files changed, 265 insertions(+), 137 deletions(-) diff --git a/src/ui/Homepage/HomepageAuth.tsx b/src/ui/Homepage/HomepageAuth.tsx index 43241e55..8cb5a221 100644 --- a/src/ui/Homepage/HomepageAuth.tsx +++ b/src/ui/Homepage/HomepageAuth.tsx @@ -1,10 +1,21 @@ import React, {useState, useEffect} from "react"; -import {cn} from "@/lib/utils.ts"; -import {Button} from "@/components/ui/button.tsx"; -import {Input} from "@/components/ui/input.tsx"; -import {Label} from "@/components/ui/label.tsx"; -import {Alert, AlertTitle, AlertDescription} from "@/components/ui/alert.tsx"; -import axios from "axios"; +import {cn} from "../../lib/utils.ts"; +import {Button} from "../../components/ui/button.tsx"; +import {Input} from "../../components/ui/input.tsx"; +import {Label} from "../../components/ui/label.tsx"; +import {Alert, AlertTitle, AlertDescription} from "../../components/ui/alert.tsx"; +import { + registerUser, + loginUser, + getUserInfo, + getRegistrationAllowed, + getOIDCConfig, + getUserCount, + initiatePasswordReset, + verifyPasswordResetCode, + completePasswordReset, + getOIDCAuthorizeUrl +} from "../main-axios.ts"; function setCookie(name: string, value: string, days = 7) { const expires = new Date(Date.now() + days * 864e5).toUTCString(); @@ -18,11 +29,7 @@ function getCookie(name: string) { }, ""); } -const apiBase = import.meta.env.DEV ? "http://localhost:8081/users" : "/users"; -const API = axios.create({ - baseURL: apiBase, -}); interface HomepageAuthProps extends React.ComponentProps<"div"> { setLoggedIn: (loggedIn: boolean) => void; @@ -74,14 +81,14 @@ export function HomepageAuth({ }, [loggedIn]); useEffect(() => { - API.get("/registration-allowed").then(res => { - setRegistrationAllowed(res.data.allowed); + getRegistrationAllowed().then(res => { + setRegistrationAllowed(res.allowed); }); }, []); useEffect(() => { - API.get("/oidc-config").then((response) => { - if (response.data) { + getOIDCConfig().then((response) => { + if (response) { setOidcConfigured(true); } else { setOidcConfigured(false); @@ -96,8 +103,8 @@ export function HomepageAuth({ }, []); useEffect(() => { - API.get("/count").then(res => { - if (res.data.count === 0) { + getUserCount().then(res => { + if (res.count === 0) { setFirstUser(true); setTab("signup"); } else { @@ -123,7 +130,7 @@ export function HomepageAuth({ try { let res, meRes; if (tab === "login") { - res = await API.post("/login", {username: localUsername, password}); + res = await loginUser(localUsername, password); } else { if (password !== signupConfirmPassword) { setError("Passwords do not match"); @@ -135,31 +142,37 @@ export function HomepageAuth({ setLoading(false); return; } - await API.post("/create", {username: localUsername, password}); - res = await API.post("/login", {username: localUsername, password}); + + await registerUser(localUsername, password); + res = await loginUser(localUsername, password); } - setCookie("jwt", res.data.token); + + if (!res || !res.token) { + throw new Error('No token received from login'); + } + + setCookie("jwt", res.token); [meRes] = await Promise.all([ - API.get("/me", {headers: {Authorization: `Bearer ${res.data.token}`}}), - API.get("/db-health") + getUserInfo(), ]); + setInternalLoggedIn(true); setLoggedIn(true); - setIsAdmin(!!meRes.data.is_admin); - setUsername(meRes.data.username || null); - setUserId(meRes.data.id || null); + setIsAdmin(!!meRes.is_admin); + setUsername(meRes.username || null); + setUserId(meRes.userId || null); setDbError(null); onAuthSuccess({ - isAdmin: !!meRes.data.is_admin, - username: meRes.data.username || null, - userId: meRes.data.id || null + isAdmin: !!meRes.is_admin, + username: meRes.username || null, + userId: meRes.userId || null }); setInternalLoggedIn(true); if (tab === "signup") { setSignupConfirmPassword(""); } } catch (err: any) { - setError(err?.response?.data?.error || "Unknown error"); + setError(err?.response?.data?.error || err?.message || "Unknown error"); setInternalLoggedIn(false); setLoggedIn(false); setIsAdmin(false); @@ -176,29 +189,26 @@ export function HomepageAuth({ } } - async function initiatePasswordReset() { + async function handleInitiatePasswordReset() { setError(null); setResetLoading(true); try { - await API.post("/initiate-reset", {username: localUsername}); + const result = await initiatePasswordReset(localUsername); setResetStep("verify"); setError(null); } catch (err: any) { - setError(err?.response?.data?.error || "Failed to initiate password reset"); + setError(err?.response?.data?.error || err?.message || "Failed to initiate password reset"); } finally { setResetLoading(false); } } - async function verifyResetCode() { + async function handleVerifyResetCode() { setError(null); setResetLoading(true); try { - const response = await API.post("/verify-reset-code", { - username: localUsername, - resetCode: resetCode - }); - setTempToken(response.data.tempToken); + const response = await verifyPasswordResetCode(localUsername, resetCode); + setTempToken(response.tempToken); setResetStep("newPassword"); setError(null); } catch (err: any) { @@ -208,7 +218,7 @@ export function HomepageAuth({ } } - async function completePasswordReset() { + async function handleCompletePasswordReset() { setError(null); setResetLoading(true); @@ -225,11 +235,7 @@ export function HomepageAuth({ } try { - await API.post("/complete-reset", { - username: localUsername, - tempToken: tempToken, - newPassword: newPassword - }); + await completePasswordReset(localUsername, tempToken, newPassword); setResetStep("initiate"); setResetCode(""); @@ -267,8 +273,8 @@ export function HomepageAuth({ setError(null); setOidcLoading(true); try { - const authResponse = await API.get("/oidc/authorize"); - const {auth_url: authUrl} = authResponse.data; + const authResponse = await getOIDCAuthorizeUrl(); + const {auth_url: authUrl} = authResponse; if (!authUrl || authUrl === 'undefined') { throw new Error('Invalid authorization URL received from backend'); @@ -299,18 +305,18 @@ export function HomepageAuth({ setError(null); setCookie("jwt", token); - API.get("/me", {headers: {Authorization: `Bearer ${token}`}}) + getUserInfo() .then(meRes => { setInternalLoggedIn(true); setLoggedIn(true); - setIsAdmin(!!meRes.data.is_admin); - setUsername(meRes.data.username || null); - setUserId(meRes.data.id || null); + setIsAdmin(!!meRes.is_admin); + setUsername(meRes.username || null); + setUserId(meRes.id || null); setDbError(null); onAuthSuccess({ - isAdmin: !!meRes.data.is_admin, - username: meRes.data.username || null, - userId: meRes.data.id || null + isAdmin: !!meRes.is_admin, + username: meRes.username || null, + userId: meRes.id || null }); setInternalLoggedIn(true); window.history.replaceState({}, document.title, window.location.pathname); @@ -486,7 +492,7 @@ export function HomepageAuth({ type="button" className="w-full h-11 text-base font-semibold" disabled={resetLoading || !localUsername.trim()} - onClick={initiatePasswordReset} + onClick={handleInitiatePasswordReset} > {resetLoading ? Spinner : "Send Reset Code"} @@ -519,7 +525,7 @@ export function HomepageAuth({ type="button" className="w-full h-11 text-base font-semibold" disabled={resetLoading || resetCode.length !== 6} - onClick={verifyResetCode} + onClick={handleVerifyResetCode} > {resetLoading ? Spinner : "Verify Code"} @@ -598,7 +604,7 @@ export function HomepageAuth({ type="button" className="w-full h-11 text-base font-semibold" disabled={resetLoading || !newPassword || !confirmPassword} - onClick={completePasswordReset} + onClick={handleCompletePasswordReset} > {resetLoading ? Spinner : "Reset Password"} diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts index c1192d85..771cb493 100644 --- a/src/ui/main-axios.ts +++ b/src/ui/main-axios.ts @@ -105,35 +105,51 @@ export type ServerMetrics = { lastChecked: string; }; -const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'; +interface AuthResponse { + token: string; +} -const sshHostApi = axios.create({ - baseURL: isLocalhost ? 'http://localhost:8081' : '', - headers: { - 'Content-Type': 'application/json', - }, -}); +interface UserInfo { + id: string; + username: string; + is_admin: boolean; +} -const tunnelApi = axios.create({ - baseURL: isLocalhost ? 'http://localhost:8083' : '', - headers: { - 'Content-Type': 'application/json', - }, -}); +interface RegistrationResponse { + allowed: boolean; +} -const fileManagerApi = axios.create({ - baseURL: isLocalhost ? 'http://localhost:8084' : '', - headers: { - 'Content-Type': 'application/json', - } -}) +interface OIDCConfig { + configured: boolean; +} -const statsApi = axios.create({ - baseURL: isLocalhost ? 'http://localhost:8085' : '', - headers: { - 'Content-Type': 'application/json', - } -}) +interface UserCount { + count: number; +} + +interface PasswordResetInitiate { + username: string; +} + +interface PasswordResetVerify { + username: string; + resetCode: string; +} + +interface PasswordResetComplete { + username: string; + tempToken: string; + newPassword: string; +} + +interface OIDCAuthorize { + auth_url: string; +} + +function setCookie(name: string, value: string, days = 7) { + const expires = new Date(Date.now() + days * 864e5).toUTCString(); + document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/`; +} function getCookie(name: string): string | undefined { const value = `; ${document.cookie}`; @@ -141,41 +157,54 @@ function getCookie(name: string): string | undefined { if (parts.length === 2) return parts.pop()?.split(';').shift(); } -sshHostApi.interceptors.request.use((config) => { - const token = getCookie('jwt'); - if (token) { - config.headers.Authorization = `Bearer ${token}`; - } - return config; +const sshHostApi = axios.create({ + baseURL: import.meta.env.DEV ? 'http://localhost:8081/ssh' : '/ssh', + headers: { + 'Content-Type': 'application/json', + }, }); -statsApi.interceptors.request.use((config) => { - const token = getCookie('jwt'); - if (token) { - config.headers.Authorization = `Bearer ${token}`; - } - return config; +const tunnelApi = axios.create({ + baseURL: import.meta.env.DEV ? 'http://localhost:8083/ssh' : '/ssh', + headers: { + 'Content-Type': 'application/json', + }, }); -tunnelApi.interceptors.request.use((config) => { - const token = getCookie('jwt'); - if (token) { - config.headers.Authorization = `Bearer ${token}`; +const fileManagerApi = axios.create({ + baseURL: import.meta.env.DEV ? 'http://localhost:8084/ssh' : '/ssh', + headers: { + 'Content-Type': 'application/json', } - return config; }); -fileManagerApi.interceptors.request.use((config) => { - const token = getCookie('jwt'); - if (token) { - config.headers.Authorization = `Bearer ${token}`; +const statsApi = axios.create({ + baseURL: import.meta.env.DEV ? 'http://localhost:8085' : '', + headers: { + 'Content-Type': 'application/json', } - return config; +}); + +const authApi = axios.create({ + baseURL: import.meta.env.DEV ? 'http://localhost:8081/users' : '/users', + headers: { + 'Content-Type': 'application/json', + } +}); + +[sshHostApi, tunnelApi, fileManagerApi, statsApi, authApi].forEach(api => { + api.interceptors.request.use((config) => { + const token = getCookie('jwt'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }); }); export async function getSSHHosts(): Promise { try { - const response = await sshHostApi.get('/ssh/db/host'); + const response = await sshHostApi.get('/db/host'); return response.data; } catch (error) { throw error; @@ -220,7 +249,7 @@ export async function createSSHHost(hostData: SSHHostData): Promise { delete dataWithoutFile.key; formData.append('data', JSON.stringify(dataWithoutFile)); - const response = await sshHostApi.post('/ssh/db/host', formData, { + const response = await sshHostApi.post('/db/host', formData, { headers: { 'Content-Type': 'multipart/form-data', }, @@ -228,7 +257,7 @@ export async function createSSHHost(hostData: SSHHostData): Promise { return response.data; } else { - const response = await sshHostApi.post('/ssh/db/host', submitData); + const response = await sshHostApi.post('/db/host', submitData); return response.data; } } catch (error) { @@ -273,7 +302,7 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom delete dataWithoutFile.key; formData.append('data', JSON.stringify(dataWithoutFile)); - const response = await sshHostApi.put(`/ssh/db/host/${hostId}`, formData, { + const response = await sshHostApi.put(`/db/host/${hostId}`, formData, { headers: { 'Content-Type': 'multipart/form-data', }, @@ -281,7 +310,7 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom return response.data; } else { - const response = await sshHostApi.put(`/ssh/db/host/${hostId}`, submitData); + const response = await sshHostApi.put(`/db/host/${hostId}`, submitData); return response.data; } } catch (error) { @@ -296,7 +325,7 @@ export async function bulkImportSSHHosts(hosts: SSHHostData[]): Promise<{ errors: string[]; }> { try { - const response = await sshHostApi.post('/ssh/bulk-import', {hosts}); + const response = await sshHostApi.post('/bulk-import', {hosts}); return response.data; } catch (error) { throw error; @@ -305,7 +334,7 @@ export async function bulkImportSSHHosts(hosts: SSHHostData[]): Promise<{ export async function deleteSSHHost(hostId: number): Promise { try { - const response = await sshHostApi.delete(`/ssh/db/host/${hostId}`); + const response = await sshHostApi.delete(`/db/host/${hostId}`); return response.data; } catch (error) { throw error; @@ -314,7 +343,7 @@ export async function deleteSSHHost(hostId: number): Promise { export async function getSSHHostById(hostId: number): Promise { try { - const response = await sshHostApi.get(`/ssh/db/host/${hostId}`); + const response = await sshHostApi.get(`/db/host/${hostId}`); return response.data; } catch (error) { throw error; @@ -323,7 +352,7 @@ export async function getSSHHostById(hostId: number): Promise { export async function getTunnelStatuses(): Promise> { try { - const response = await tunnelApi.get('/ssh/tunnel/status'); + const response = await tunnelApi.get('/tunnel/status'); return response.data || {}; } catch (error) { throw error; @@ -337,7 +366,7 @@ export async function getTunnelStatusByName(tunnelName: string): Promise { try { - const response = await tunnelApi.post('/ssh/tunnel/connect', tunnelConfig); + const response = await tunnelApi.post('/tunnel/connect', tunnelConfig); return response.data; } catch (error) { throw error; @@ -346,7 +375,7 @@ export async function connectTunnel(tunnelConfig: TunnelConfig): Promise { export async function disconnectTunnel(tunnelName: string): Promise { try { - const response = await tunnelApi.post('/ssh/tunnel/disconnect', {tunnelName}); + const response = await tunnelApi.post('/tunnel/disconnect', {tunnelName}); return response.data; } catch (error) { throw error; @@ -355,7 +384,7 @@ export async function disconnectTunnel(tunnelName: string): Promise { export async function cancelTunnel(tunnelName: string): Promise { try { - const response = await tunnelApi.post('/ssh/tunnel/cancel', {tunnelName}); + const response = await tunnelApi.post('/tunnel/cancel', {tunnelName}); return response.data; } catch (error) { throw error; @@ -364,7 +393,7 @@ export async function cancelTunnel(tunnelName: string): Promise { export async function getFileManagerRecent(hostId: number): Promise { try { - const response = await sshHostApi.get(`/ssh/file_manager/recent?hostId=${hostId}`); + const response = await sshHostApi.get(`/file_manager/recent?hostId=${hostId}`); return response.data || []; } catch (error) { return []; @@ -379,7 +408,7 @@ export async function addFileManagerRecent(file: { hostId: number }): Promise { try { - const response = await sshHostApi.post('/ssh/file_manager/recent', file); + const response = await sshHostApi.post('/file_manager/recent', file); return response.data; } catch (error) { throw error; @@ -394,7 +423,7 @@ export async function removeFileManagerRecent(file: { hostId: number }): Promise { try { - const response = await sshHostApi.delete('/ssh/file_manager/recent', {data: file}); + const response = await sshHostApi.delete('/file_manager/recent', {data: file}); return response.data; } catch (error) { throw error; @@ -403,7 +432,7 @@ export async function removeFileManagerRecent(file: { export async function getFileManagerPinned(hostId: number): Promise { try { - const response = await sshHostApi.get(`/ssh/file_manager/pinned?hostId=${hostId}`); + const response = await sshHostApi.get(`/file_manager/pinned?hostId=${hostId}`); return response.data || []; } catch (error) { return []; @@ -418,7 +447,7 @@ export async function addFileManagerPinned(file: { hostId: number }): Promise { try { - const response = await sshHostApi.post('/ssh/file_manager/pinned', file); + const response = await sshHostApi.post('/file_manager/pinned', file); return response.data; } catch (error) { throw error; @@ -433,7 +462,7 @@ export async function removeFileManagerPinned(file: { hostId: number }): Promise { try { - const response = await sshHostApi.delete('/ssh/file_manager/pinned', {data: file}); + const response = await sshHostApi.delete('/file_manager/pinned', {data: file}); return response.data; } catch (error) { throw error; @@ -442,7 +471,7 @@ export async function removeFileManagerPinned(file: { export async function getFileManagerShortcuts(hostId: number): Promise { try { - const response = await sshHostApi.get(`/ssh/file_manager/shortcuts?hostId=${hostId}`); + const response = await sshHostApi.get(`/file_manager/shortcuts?hostId=${hostId}`); return response.data || []; } catch (error) { return []; @@ -457,7 +486,7 @@ export async function addFileManagerShortcut(shortcut: { hostId: number }): Promise { try { - const response = await sshHostApi.post('/ssh/file_manager/shortcuts', shortcut); + const response = await sshHostApi.post('/file_manager/shortcuts', shortcut); return response.data; } catch (error) { throw error; @@ -472,7 +501,7 @@ export async function removeFileManagerShortcut(shortcut: { hostId: number }): Promise { try { - const response = await sshHostApi.delete('/ssh/file_manager/shortcuts', {data: shortcut}); + const response = await sshHostApi.delete('/file_manager/shortcuts', {data: shortcut}); return response.data; } catch (error) { throw error; @@ -488,7 +517,7 @@ export async function connectSSH(sessionId: string, config: { keyPassword?: string; }): Promise { try { - const response = await fileManagerApi.post('/ssh/file_manager/ssh/connect', { + const response = await fileManagerApi.post('/ssh/connect', { sessionId, ...config }); @@ -500,7 +529,7 @@ export async function connectSSH(sessionId: string, config: { export async function disconnectSSH(sessionId: string): Promise { try { - const response = await fileManagerApi.post('/ssh/file_manager/ssh/disconnect', {sessionId}); + const response = await fileManagerApi.post('/ssh/disconnect', {sessionId}); return response.data; } catch (error) { throw error; @@ -509,7 +538,7 @@ export async function disconnectSSH(sessionId: string): Promise { export async function getSSHStatus(sessionId: string): Promise<{ connected: boolean }> { try { - const response = await fileManagerApi.get('/ssh/file_manager/ssh/status', { + const response = await fileManagerApi.get('/ssh/status', { params: {sessionId} }); return response.data; @@ -520,7 +549,7 @@ export async function getSSHStatus(sessionId: string): Promise<{ connected: bool export async function listSSHFiles(sessionId: string, path: string): Promise { try { - const response = await fileManagerApi.get('/ssh/file_manager/ssh/listFiles', { + const response = await fileManagerApi.get('/ssh/listFiles', { params: {sessionId, path} }); return response.data || []; @@ -531,7 +560,7 @@ export async function listSSHFiles(sessionId: string, path: string): Promise { try { - const response = await fileManagerApi.get('/ssh/file_manager/ssh/readFile', { + const response = await fileManagerApi.get('/ssh/readFile', { params: {sessionId, path} }); return response.data; @@ -542,7 +571,7 @@ export async function readSSHFile(sessionId: string, path: string): Promise<{ co export async function writeSSHFile(sessionId: string, path: string, content: string): Promise { try { - const response = await fileManagerApi.post('/ssh/file_manager/ssh/writeFile', { + const response = await fileManagerApi.post('/ssh/writeFile', { sessionId, path, content @@ -560,7 +589,7 @@ export async function writeSSHFile(sessionId: string, path: string, content: str export async function uploadSSHFile(sessionId: string, path: string, fileName: string, content: string): Promise { try { - const response = await fileManagerApi.post('/ssh/file_manager/ssh/uploadFile', { + const response = await fileManagerApi.post('/ssh/uploadFile', { sessionId, path, fileName, @@ -574,7 +603,7 @@ export async function uploadSSHFile(sessionId: string, path: string, fileName: s export async function createSSHFile(sessionId: string, path: string, fileName: string, content: string = ''): Promise { try { - const response = await fileManagerApi.post('/ssh/file_manager/ssh/createFile', { + const response = await fileManagerApi.post('/ssh/createFile', { sessionId, path, fileName, @@ -588,7 +617,7 @@ export async function createSSHFile(sessionId: string, path: string, fileName: s export async function createSSHFolder(sessionId: string, path: string, folderName: string): Promise { try { - const response = await fileManagerApi.post('/ssh/file_manager/ssh/createFolder', { + const response = await fileManagerApi.post('/ssh/createFolder', { sessionId, path, folderName @@ -601,7 +630,7 @@ export async function createSSHFolder(sessionId: string, path: string, folderNam export async function deleteSSHItem(sessionId: string, path: string, isDirectory: boolean): Promise { try { - const response = await fileManagerApi.delete('/ssh/file_manager/ssh/deleteItem', { + const response = await fileManagerApi.delete('/ssh/deleteItem', { data: { sessionId, path, @@ -616,7 +645,7 @@ export async function deleteSSHItem(sessionId: string, path: string, isDirectory export async function renameSSHItem(sessionId: string, oldPath: string, newName: string): Promise { try { - const response = await fileManagerApi.put('/ssh/file_manager/ssh/renameItem', { + const response = await fileManagerApi.put('/ssh/renameItem', { sessionId, oldPath, newName @@ -627,7 +656,7 @@ export async function renameSSHItem(sessionId: string, oldPath: string, newName: } } -export {sshHostApi, tunnelApi, fileManagerApi}; + export async function getAllServerStatuses(): Promise> { try { @@ -654,4 +683,97 @@ export async function getServerMetricsById(id: number): Promise { } catch (error) { throw error; } -} \ No newline at end of file +} + +// Auth-related functions +export async function registerUser(username: string, password: string): Promise { + try { + const response = await authApi.post('/create', { username, password }); + return response.data; + } catch (error) { + throw error; + } +} + +export async function loginUser(username: string, password: string): Promise { + try { + const response = await authApi.post('/login', { username, password }); + return response.data; + } catch (error) { + throw error; + } +} + +export async function getUserInfo(): Promise { + try { + const response = await authApi.get('/me'); + return response.data; + } catch (error) { + throw error; + } +} + +export async function getRegistrationAllowed(): Promise<{ allowed: boolean }> { + try { + const response = await authApi.get('/registration-allowed'); + return response.data; + } catch (error) { + throw error; + } +} + +export async function getOIDCConfig(): Promise { + try { + const response = await authApi.get('/oidc-config'); + return response.data; + } catch (error) { + throw error; + } +} + +export async function getUserCount(): Promise { + try { + const response = await authApi.get('/count'); + return response.data; + } catch (error) { + throw error; + } +} + +export async function initiatePasswordReset(username: string): Promise { + try { + const response = await authApi.post('/initiate-reset', { username }); + return response.data; + } catch (error) { + throw error; + } +} + +export async function verifyPasswordResetCode(username: string, resetCode: string): Promise { + try { + const response = await authApi.post('/verify-reset-code', { username, resetCode }); + return response.data; + } catch (error) { + throw error; + } +} + +export async function completePasswordReset(username: string, tempToken: string, newPassword: string): Promise { + try { + const response = await authApi.post('/complete-reset', { username, tempToken, newPassword }); + return response.data; + } catch (error) { + throw error; + } +} + +export async function getOIDCAuthorizeUrl(): Promise { + try { + const response = await authApi.get('/oidc/authorize'); + return response.data; + } catch (error) { + throw error; + } +} + +export {sshHostApi, tunnelApi, fileManagerApi, authApi}; \ No newline at end of file From 23e72aedfd6b81a795ee835793482d337a202dbd Mon Sep 17 00:00:00 2001 From: LukeGus Date: Sun, 24 Aug 2025 00:59:39 -0500 Subject: [PATCH 02/27] Migrate to new websocket link for locahlost --- src/ui/apps/Terminal/TerminalComponent.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/apps/Terminal/TerminalComponent.tsx b/src/ui/apps/Terminal/TerminalComponent.tsx index d77ac606..322afa41 100644 --- a/src/ui/apps/Terminal/TerminalComponent.tsx +++ b/src/ui/apps/Terminal/TerminalComponent.tsx @@ -226,7 +226,9 @@ export const TerminalComponent = forwardRef(function SSHT const cols = terminal.cols; const rows = terminal.rows; - const wsUrl = window.location.hostname === 'localhost' ? 'ws://localhost:8082' : `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}/ssh/websocket/`; + const wsUrl = import.meta.env.DEV + ? 'ws://localhost:8082' + : `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}/ssh/websocket/`; const ws = new WebSocket(wsUrl); webSocketRef.current = ws; From 94f69cee3f1f001f6ea091a5c4a860c1a193a0ef Mon Sep 17 00:00:00 2001 From: LukeGus Date: Sun, 24 Aug 2025 01:10:59 -0500 Subject: [PATCH 03/27] Migrate to new websocket link for locahlost --- src/backend/database/routes/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/database/routes/users.ts b/src/backend/database/routes/users.ts index 790ac7be..aca004ab 100644 --- a/src/backend/database/routes/users.ts +++ b/src/backend/database/routes/users.ts @@ -79,7 +79,7 @@ async function verifyOIDCToken(idToken: string, issuerUrl: string, clientId: str const key = await importJWK(publicKey); const {payload} = await jwtVerify(idToken, key, { - issuer: issuerUrl, + issuer: [issuerUrl, issuerUrl.replace(/\/application\/o\/[^\/]+$/, '')], audience: clientId, }); From cef420d1d8d7c74595f2f48db370cf753b0d7fd0 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Sun, 24 Aug 2025 01:27:41 -0500 Subject: [PATCH 04/27] Clean up main-axios.ts --- src/ui/main-axios.ts | 390 ++++++++++++++++++++++++------------------- 1 file changed, 218 insertions(+), 172 deletions(-) diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts index 771cb493..4c3a5c99 100644 --- a/src/ui/main-axios.ts +++ b/src/ui/main-axios.ts @@ -1,4 +1,8 @@ -import axios from 'axios'; +import axios, { AxiosError, AxiosInstance } from 'axios'; + +// ============================================================================ +// TYPES & INTERFACES +// ============================================================================ interface SSHHostData { name?: string; @@ -93,15 +97,41 @@ interface FileManagerShortcut { path: string; } +interface FileManagerOperation { + name: string; + path: string; + isSSH: boolean; + sshSessionId?: string; + hostId: number; +} + export type ServerStatus = { status: 'online' | 'offline'; lastChecked: string; }; +interface CpuMetrics { + percent: number | null; + cores: number | null; + load: [number, number, number] | null; +} + +interface MemoryMetrics { + percent: number | null; + usedGiB: number | null; + totalGiB: number | null; +} + +interface DiskMetrics { + percent: number | null; + usedHuman: string | null; + totalHuman: string | null; +} + export type ServerMetrics = { - cpu: { percent: number | null; cores: number | null; load: [number, number, number] | null }; - memory: { percent: number | null; usedGiB: number | null; totalGiB: number | null }; - disk: { percent: number | null; usedHuman: string | null; totalHuman: string | null }; + cpu: CpuMetrics; + memory: MemoryMetrics; + disk: DiskMetrics; lastChecked: string; }; @@ -115,38 +145,19 @@ interface UserInfo { is_admin: boolean; } -interface RegistrationResponse { - allowed: boolean; -} - -interface OIDCConfig { - configured: boolean; -} - interface UserCount { count: number; } -interface PasswordResetInitiate { - username: string; -} - -interface PasswordResetVerify { - username: string; - resetCode: string; -} - -interface PasswordResetComplete { - username: string; - tempToken: string; - newPassword: string; -} - interface OIDCAuthorize { auth_url: string; } -function setCookie(name: string, value: string, days = 7) { +// ============================================================================ +// UTILITY FUNCTIONS +// ============================================================================ + +function setCookie(name: string, value: string, days = 7): void { const expires = new Date(Date.now() + days * 864e5).toUTCString(); document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/`; } @@ -157,57 +168,118 @@ function getCookie(name: string): string | undefined { if (parts.length === 2) return parts.pop()?.split(';').shift(); } -const sshHostApi = axios.create({ - baseURL: import.meta.env.DEV ? 'http://localhost:8081/ssh' : '/ssh', - headers: { - 'Content-Type': 'application/json', - }, -}); +function createApiInstance(baseURL: string): AxiosInstance { + const instance = axios.create({ + baseURL, + headers: { 'Content-Type': 'application/json' }, + timeout: 30000, + }); -const tunnelApi = axios.create({ - baseURL: import.meta.env.DEV ? 'http://localhost:8083/ssh' : '/ssh', - headers: { - 'Content-Type': 'application/json', - }, -}); - -const fileManagerApi = axios.create({ - baseURL: import.meta.env.DEV ? 'http://localhost:8084/ssh' : '/ssh', - headers: { - 'Content-Type': 'application/json', - } -}); - -const statsApi = axios.create({ - baseURL: import.meta.env.DEV ? 'http://localhost:8085' : '', - headers: { - 'Content-Type': 'application/json', - } -}); - -const authApi = axios.create({ - baseURL: import.meta.env.DEV ? 'http://localhost:8081/users' : '/users', - headers: { - 'Content-Type': 'application/json', - } -}); - -[sshHostApi, tunnelApi, fileManagerApi, statsApi, authApi].forEach(api => { - api.interceptors.request.use((config) => { + // Add JWT token to all requests + instance.interceptors.request.use((config) => { const token = getCookie('jwt'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); -}); + + // Global error handling + instance.interceptors.response.use( + (response) => response, + (error: AxiosError) => { + if (error.response?.status === 401) { + // Token expired or invalid - clear cookie + document.cookie = 'jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; + } + return Promise.reject(error); + } + ); + + return instance; +} + +// ============================================================================ +// API INSTANCES +// ============================================================================ + +const isDev = process.env.NODE_ENV === 'development' || window.location.hostname === 'localhost'; + +// SSH Host Management API (port 8081) +export const sshHostApi = createApiInstance( + isDev ? 'http://localhost:8081/ssh' : '/ssh' +); + +// Tunnel Management API (port 8083) +export const tunnelApi = createApiInstance( + isDev ? 'http://localhost:8083/ssh' : '/ssh' +); + +// File Manager Operations API (port 8084) - SSH file operations +export const fileManagerApi = createApiInstance( + isDev ? 'http://localhost:8084/ssh/file_manager' : '/ssh/file_manager' +); + +// Server Statistics API (port 8085) +export const statsApi = createApiInstance( + isDev ? 'http://localhost:8085' : '' +); + +// Authentication API (port 8081) +export const authApi = createApiInstance( + isDev ? 'http://localhost:8081/users' : '/users' +); + +// ============================================================================ +// ERROR HANDLING +// ============================================================================ + +class ApiError extends Error { + constructor( + message: string, + public status?: number, + public code?: string + ) { + super(message); + this.name = 'ApiError'; + } +} + +function handleApiError(error: unknown, operation: string): never { + if (axios.isAxiosError(error)) { + const status = error.response?.status; + const message = error.response?.data?.error || error.message; + + if (status === 401) { + throw new ApiError('Authentication required', 401); + } else if (status === 403) { + throw new ApiError('Access denied', 403); + } else if (status === 404) { + throw new ApiError('Resource not found', 404); + } else if (status && status >= 500) { + throw new ApiError('Server error occurred', status); + } else { + throw new ApiError(message || `Failed to ${operation}`, status); + } + } + + if (error instanceof ApiError) { + throw error; + } + + throw new ApiError(`Unexpected error during ${operation}: ${error instanceof Error ? error.message : 'Unknown error'}`); +} + +// ============================================================================ +// SSH HOST MANAGEMENT +// ============================================================================ export async function getSSHHosts(): Promise { try { const response = await sshHostApi.get('/db/host'); return response.data; } catch (error) { - throw error; + handleApiError(error, 'fetch SSH hosts'); } } @@ -245,23 +317,20 @@ export async function createSSHHost(hostData: SSHHostData): Promise { const formData = new FormData(); formData.append('key', hostData.key); - const dataWithoutFile = {...submitData}; + const dataWithoutFile = { ...submitData }; delete dataWithoutFile.key; formData.append('data', JSON.stringify(dataWithoutFile)); const response = await sshHostApi.post('/db/host', formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, + headers: { 'Content-Type': 'multipart/form-data' }, }); - return response.data; } else { const response = await sshHostApi.post('/db/host', submitData); return response.data; } } catch (error) { - throw error; + handleApiError(error, 'create SSH host'); } } @@ -298,23 +367,20 @@ export async function updateSSHHost(hostId: number, hostData: SSHHostData): Prom const formData = new FormData(); formData.append('key', hostData.key); - const dataWithoutFile = {...submitData}; + const dataWithoutFile = { ...submitData }; delete dataWithoutFile.key; formData.append('data', JSON.stringify(dataWithoutFile)); const response = await sshHostApi.put(`/db/host/${hostId}`, formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, + headers: { 'Content-Type': 'multipart/form-data' }, }); - return response.data; } else { const response = await sshHostApi.put(`/db/host/${hostId}`, submitData); return response.data; } } catch (error) { - throw error; + handleApiError(error, 'update SSH host'); } } @@ -325,10 +391,10 @@ export async function bulkImportSSHHosts(hosts: SSHHostData[]): Promise<{ errors: string[]; }> { try { - const response = await sshHostApi.post('/bulk-import', {hosts}); + const response = await sshHostApi.post('/bulk-import', { hosts }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'bulk import SSH hosts'); } } @@ -337,7 +403,7 @@ export async function deleteSSHHost(hostId: number): Promise { const response = await sshHostApi.delete(`/db/host/${hostId}`); return response.data; } catch (error) { - throw error; + handleApiError(error, 'delete SSH host'); } } @@ -346,16 +412,20 @@ export async function getSSHHostById(hostId: number): Promise { const response = await sshHostApi.get(`/db/host/${hostId}`); return response.data; } catch (error) { - throw error; + handleApiError(error, 'fetch SSH host'); } } +// ============================================================================ +// TUNNEL MANAGEMENT +// ============================================================================ + export async function getTunnelStatuses(): Promise> { try { const response = await tunnelApi.get('/tunnel/status'); return response.data || {}; } catch (error) { - throw error; + handleApiError(error, 'fetch tunnel statuses'); } } @@ -369,64 +439,57 @@ export async function connectTunnel(tunnelConfig: TunnelConfig): Promise { const response = await tunnelApi.post('/tunnel/connect', tunnelConfig); return response.data; } catch (error) { - throw error; + handleApiError(error, 'connect tunnel'); } } export async function disconnectTunnel(tunnelName: string): Promise { try { - const response = await tunnelApi.post('/tunnel/disconnect', {tunnelName}); + const response = await tunnelApi.post('/tunnel/disconnect', { tunnelName }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'disconnect tunnel'); } } export async function cancelTunnel(tunnelName: string): Promise { try { - const response = await tunnelApi.post('/tunnel/cancel', {tunnelName}); + const response = await tunnelApi.post('/tunnel/cancel', { tunnelName }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'cancel tunnel'); } } +// ============================================================================ +// FILE MANAGER METADATA (Recent, Pinned, Shortcuts) +// ============================================================================ + export async function getFileManagerRecent(hostId: number): Promise { try { const response = await sshHostApi.get(`/file_manager/recent?hostId=${hostId}`); return response.data || []; } catch (error) { + // Don't throw for file manager metadata - return empty array return []; } } -export async function addFileManagerRecent(file: { - name: string; - path: string; - isSSH: boolean; - sshSessionId?: string; - hostId: number -}): Promise { +export async function addFileManagerRecent(file: FileManagerOperation): Promise { try { const response = await sshHostApi.post('/file_manager/recent', file); return response.data; } catch (error) { - throw error; + handleApiError(error, 'add recent file'); } } -export async function removeFileManagerRecent(file: { - name: string; - path: string; - isSSH: boolean; - sshSessionId?: string; - hostId: number -}): Promise { +export async function removeFileManagerRecent(file: FileManagerOperation): Promise { try { - const response = await sshHostApi.delete('/file_manager/recent', {data: file}); + const response = await sshHostApi.delete('/file_manager/recent', { data: file }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'remove recent file'); } } @@ -439,33 +502,21 @@ export async function getFileManagerPinned(hostId: number): Promise { +export async function addFileManagerPinned(file: FileManagerOperation): Promise { try { const response = await sshHostApi.post('/file_manager/pinned', file); return response.data; } catch (error) { - throw error; + handleApiError(error, 'add pinned file'); } } -export async function removeFileManagerPinned(file: { - name: string; - path: string; - isSSH: boolean; - sshSessionId?: string; - hostId: number -}): Promise { +export async function removeFileManagerPinned(file: FileManagerOperation): Promise { try { - const response = await sshHostApi.delete('/file_manager/pinned', {data: file}); + const response = await sshHostApi.delete('/file_manager/pinned', { data: file }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'remove pinned file'); } } @@ -478,36 +529,28 @@ export async function getFileManagerShortcuts(hostId: number): Promise { +export async function addFileManagerShortcut(shortcut: FileManagerOperation): Promise { try { const response = await sshHostApi.post('/file_manager/shortcuts', shortcut); return response.data; } catch (error) { - throw error; + handleApiError(error, 'add shortcut'); } } -export async function removeFileManagerShortcut(shortcut: { - name: string; - path: string; - isSSH: boolean; - sshSessionId?: string; - hostId: number -}): Promise { +export async function removeFileManagerShortcut(shortcut: FileManagerOperation): Promise { try { - const response = await sshHostApi.delete('/file_manager/shortcuts', {data: shortcut}); + const response = await sshHostApi.delete('/file_manager/shortcuts', { data: shortcut }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'remove shortcut'); } } +// ============================================================================ +// SSH FILE OPERATIONS +// ============================================================================ + export async function connectSSH(sessionId: string, config: { ip: string; port: number; @@ -523,49 +566,49 @@ export async function connectSSH(sessionId: string, config: { }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'connect SSH'); } } export async function disconnectSSH(sessionId: string): Promise { try { - const response = await fileManagerApi.post('/ssh/disconnect', {sessionId}); + const response = await fileManagerApi.post('/ssh/disconnect', { sessionId }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'disconnect SSH'); } } export async function getSSHStatus(sessionId: string): Promise<{ connected: boolean }> { try { const response = await fileManagerApi.get('/ssh/status', { - params: {sessionId} + params: { sessionId } }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'get SSH status'); } } export async function listSSHFiles(sessionId: string, path: string): Promise { try { const response = await fileManagerApi.get('/ssh/listFiles', { - params: {sessionId, path} + params: { sessionId, path } }); return response.data || []; } catch (error) { - throw error; + handleApiError(error, 'list SSH files'); } } export async function readSSHFile(sessionId: string, path: string): Promise<{ content: string; path: string }> { try { const response = await fileManagerApi.get('/ssh/readFile', { - params: {sessionId, path} + params: { sessionId, path } }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'read SSH file'); } } @@ -583,7 +626,7 @@ export async function writeSSHFile(sessionId: string, path: string, content: str throw new Error('File write operation did not return success status'); } } catch (error) { - throw error; + handleApiError(error, 'write SSH file'); } } @@ -597,7 +640,7 @@ export async function uploadSSHFile(sessionId: string, path: string, fileName: s }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'upload SSH file'); } } @@ -611,7 +654,7 @@ export async function createSSHFile(sessionId: string, path: string, fileName: s }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'create SSH file'); } } @@ -624,7 +667,7 @@ export async function createSSHFolder(sessionId: string, path: string, folderNam }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'create SSH folder'); } } @@ -639,7 +682,7 @@ export async function deleteSSHItem(sessionId: string, path: string, isDirectory }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'delete SSH item'); } } @@ -652,18 +695,20 @@ export async function renameSSHItem(sessionId: string, oldPath: string, newName: }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'rename SSH item'); } } - +// ============================================================================ +// SERVER STATISTICS +// ============================================================================ export async function getAllServerStatuses(): Promise> { try { const response = await statsApi.get('/status'); return response.data || {}; } catch (error) { - throw error; + handleApiError(error, 'fetch server statuses'); } } @@ -672,7 +717,7 @@ export async function getServerStatusById(id: number): Promise { const response = await statsApi.get(`/status/${id}`); return response.data; } catch (error) { - throw error; + handleApiError(error, 'fetch server status'); } } @@ -681,17 +726,20 @@ export async function getServerMetricsById(id: number): Promise { const response = await statsApi.get(`/metrics/${id}`); return response.data; } catch (error) { - throw error; + handleApiError(error, 'fetch server metrics'); } } -// Auth-related functions +// ============================================================================ +// AUTHENTICATION +// ============================================================================ + export async function registerUser(username: string, password: string): Promise { try { const response = await authApi.post('/create', { username, password }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'register user'); } } @@ -700,7 +748,7 @@ export async function loginUser(username: string, password: string): Promise { const response = await authApi.get('/me'); return response.data; } catch (error) { - throw error; + handleApiError(error, 'fetch user info'); } } @@ -718,7 +766,7 @@ export async function getRegistrationAllowed(): Promise<{ allowed: boolean }> { const response = await authApi.get('/registration-allowed'); return response.data; } catch (error) { - throw error; + handleApiError(error, 'check registration status'); } } @@ -727,7 +775,7 @@ export async function getOIDCConfig(): Promise { const response = await authApi.get('/oidc-config'); return response.data; } catch (error) { - throw error; + handleApiError(error, 'fetch OIDC config'); } } @@ -736,7 +784,7 @@ export async function getUserCount(): Promise { const response = await authApi.get('/count'); return response.data; } catch (error) { - throw error; + handleApiError(error, 'fetch user count'); } } @@ -745,7 +793,7 @@ export async function initiatePasswordReset(username: string): Promise { const response = await authApi.post('/initiate-reset', { username }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'initiate password reset'); } } @@ -754,7 +802,7 @@ export async function verifyPasswordResetCode(username: string, resetCode: strin const response = await authApi.post('/verify-reset-code', { username, resetCode }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'verify reset code'); } } @@ -763,7 +811,7 @@ export async function completePasswordReset(username: string, tempToken: string, const response = await authApi.post('/complete-reset', { username, tempToken, newPassword }); return response.data; } catch (error) { - throw error; + handleApiError(error, 'complete password reset'); } } @@ -772,8 +820,6 @@ export async function getOIDCAuthorizeUrl(): Promise { const response = await authApi.get('/oidc/authorize'); return response.data; } catch (error) { - throw error; + handleApiError(error, 'get OIDC authorize URL'); } -} - -export {sshHostApi, tunnelApi, fileManagerApi, authApi}; \ No newline at end of file +} \ No newline at end of file From f2fb938e5f3781c1bed3723837c36ddedb197b83 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Sun, 24 Aug 2025 01:28:18 -0500 Subject: [PATCH 05/27] Clean up main-axios.ts --- src/ui/main-axios.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts index 4c3a5c99..85766006 100644 --- a/src/ui/main-axios.ts +++ b/src/ui/main-axios.ts @@ -175,7 +175,6 @@ function createApiInstance(baseURL: string): AxiosInstance { timeout: 30000, }); - // Add JWT token to all requests instance.interceptors.request.use((config) => { const token = getCookie('jwt'); if (token) { @@ -184,7 +183,6 @@ function createApiInstance(baseURL: string): AxiosInstance { return config; }); - // Global error handling instance.interceptors.response.use( (response) => response, (error: AxiosError) => { From 5ac25e2a2676b4b750a9328d3e77895c7303066f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:08:49 +0000 Subject: [PATCH 06/27] Bump docker/build-push-action from 5 to 6 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5...v6) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 2d473e10..f88215d6 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -78,7 +78,7 @@ jobs: echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV - name: Build and Push Multi-Arch Docker Image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile From 402f9fa909b298037c4d2ab026aa1b236c3d33cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:08:51 +0000 Subject: [PATCH 07/27] Bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 2d473e10..897fe7de 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1 From e364e9b38eb9842be3a116f4848759647f826ccd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:08:53 +0000 Subject: [PATCH 08/27] Bump actions/cache from 3 to 4 Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 2d473e10..1e583af5 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -37,7 +37,7 @@ jobs: network=host - name: Cache npm dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.npm @@ -48,7 +48,7 @@ jobs: ${{ runner.os }}-node- - name: Cache Docker layers - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.ref_name }}-${{ hashFiles('docker/Dockerfile') }} From 12418eb5b29b8faf3c3c5e7b814100b9dab1fa27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:08:59 +0000 Subject: [PATCH 09/27] Bump node from 22-alpine to 24-alpine in /docker Bumps node from 22-alpine to 24-alpine. --- updated-dependencies: - dependency-name: node dependency-version: 24-alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docker/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index c82aa3e6..15f3d81f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ # Stage 1: Install dependencies and build frontend -FROM node:22-alpine AS deps +FROM node:24-alpine AS deps WORKDIR /app RUN apk add --no-cache python3 make g++ @@ -26,7 +26,7 @@ COPY . . RUN npm run build:backend # Stage 4: Production dependencies -FROM node:22-alpine AS production-deps +FROM node:24-alpine AS production-deps WORKDIR /app COPY package*.json ./ @@ -35,7 +35,7 @@ RUN npm ci --only=production --ignore-scripts --force && \ npm cache clean --force # Stage 5: Build native modules -FROM node:22-alpine AS native-builder +FROM node:24-alpine AS native-builder WORKDIR /app RUN apk add --no-cache python3 make g++ @@ -46,7 +46,7 @@ RUN npm ci --only=production bcryptjs better-sqlite3 --force && \ npm cache clean --force # Stage 6: Final image -FROM node:22-alpine +FROM node:24-alpine ENV DATA_DIR=/app/data \ PORT=8080 \ NODE_ENV=production From 6ac536ebad44ab8b70fd7059982256c96c63cda3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:09:29 +0000 Subject: [PATCH 10/27] Bump tw-animate-css from 1.3.5 to 1.3.7 in the dev-patch-updates group Bumps the dev-patch-updates group with 1 update: [tw-animate-css](https://github.com/Wombosvideo/tw-animate-css). Updates `tw-animate-css` from 1.3.5 to 1.3.7 - [Release notes](https://github.com/Wombosvideo/tw-animate-css/releases) - [Commits](https://github.com/Wombosvideo/tw-animate-css/compare/v1.3.5...v1.3.7) --- updated-dependencies: - dependency-name: tw-animate-css dependency-version: 1.3.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 62 ++++++++++++++++++++++++++++++++++++++++++++--- package.json | 2 +- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc2db2ae..b12d4f42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,7 +89,7 @@ "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", "ts-node": "^10.9.2", - "tw-animate-css": "^1.3.5", + "tw-animate-css": "^1.3.7", "typescript": "~5.8.3", "typescript-eslint": "^8.35.1", "vite": "^7.0.4" @@ -3525,6 +3525,60 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", @@ -8198,9 +8252,9 @@ } }, "node_modules/tw-animate-css": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.5.tgz", - "integrity": "sha512-t3u+0YNoloIhj1mMXs779P6MO9q3p3mvGn4k1n3nJPqJw/glZcuijG2qTSN4z4mgNRfW5ZC3aXJFLwDtiipZXA==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.7.tgz", + "integrity": "sha512-lvLb3hTIpB5oGsk8JmLoAjeCHV58nKa2zHYn8yWOoG5JJusH3bhJlF2DLAZ/5NmJ+jyH3ssiAx/2KmbhavJy/A==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index e53653c2..d602d11b 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", "ts-node": "^10.9.2", - "tw-animate-css": "^1.3.5", + "tw-animate-css": "^1.3.7", "typescript": "~5.8.3", "typescript-eslint": "^8.35.1", "vite": "^7.0.4" From 59a38dad0ac8f6bc586a8a2e8108b72cfffc6145 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:10:31 +0000 Subject: [PATCH 11/27] Bump the dev-minor-updates group with 6 updates Bumps the dev-minor-updates group with 6 updates: | Package | From | To | | --- | --- | --- | | [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.31.0` | `9.34.0` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `24.0.13` | `24.3.0` | | [eslint](https://github.com/eslint/eslint) | `9.31.0` | `9.34.0` | | [typescript](https://github.com/microsoft/TypeScript) | `5.8.3` | `5.9.2` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.37.0` | `8.40.0` | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `7.0.4` | `7.1.3` | Updates `@eslint/js` from 9.31.0 to 9.34.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/commits/v9.34.0/packages/js) Updates `@types/node` from 24.0.13 to 24.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `eslint` from 9.31.0 to 9.34.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v9.31.0...v9.34.0) Updates `typescript` from 5.8.3 to 5.9.2 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.8.3...v5.9.2) Updates `typescript-eslint` from 8.37.0 to 8.40.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.40.0/packages/typescript-eslint) Updates `vite` from 7.0.4 to 7.1.3 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v7.1.3/packages/vite) --- updated-dependencies: - dependency-name: "@eslint/js" dependency-version: 9.34.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates - dependency-name: "@types/node" dependency-version: 24.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates - dependency-name: eslint dependency-version: 9.34.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates - dependency-name: typescript dependency-version: 5.9.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates - dependency-name: typescript-eslint dependency-version: 8.40.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates - dependency-name: vite dependency-version: 7.1.3 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 291 +++++++++++++++++++++++++++------------------- package.json | 12 +- 2 files changed, 180 insertions(+), 123 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc2db2ae..23dc8262 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,27 +72,27 @@ "zod": "^4.0.5" }, "devDependencies": { - "@eslint/js": "^9.30.1", + "@eslint/js": "^9.34.0", "@types/better-sqlite3": "^7.6.13", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "^24.0.13", + "@types/node": "^24.3.0", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@types/ssh2": "^1.15.5", "@types/ws": "^8.18.1", "@vitejs/plugin-react-swc": "^3.10.2", "autoprefixer": "^10.4.21", - "eslint": "^9.30.1", + "eslint": "^9.34.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", "ts-node": "^10.9.2", "tw-animate-css": "^1.3.5", - "typescript": "~5.8.3", - "typescript-eslint": "^8.35.1", - "vite": "^7.0.4" + "typescript": "~5.9.2", + "typescript-eslint": "^8.40.0", + "vite": "^7.1.3" } }, "node_modules/@ampproject/remapping": { @@ -1023,9 +1023,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1033,9 +1033,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1083,9 +1083,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", "dev": true, "license": "MIT", "engines": { @@ -1106,13 +1106,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -3525,6 +3525,60 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", @@ -3720,12 +3774,12 @@ } }, "node_modules/@types/node": { - "version": "24.0.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz", - "integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "license": "MIT", "dependencies": { - "undici-types": "~7.8.0" + "undici-types": "~7.10.0" } }, "node_modules/@types/qs": { @@ -3819,17 +3873,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz", + "integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/type-utils": "8.40.0", + "@typescript-eslint/utils": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3843,9 +3897,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.40.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -3859,16 +3913,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz", + "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4" }, "engines": { @@ -3880,18 +3934,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz", + "integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.40.0", + "@typescript-eslint/types": "^8.40.0", "debug": "^4.3.4" }, "engines": { @@ -3902,18 +3956,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz", + "integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3924,9 +3978,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz", + "integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==", "dev": true, "license": "MIT", "engines": { @@ -3937,19 +3991,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz", + "integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3962,13 +4016,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz", + "integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==", "dev": true, "license": "MIT", "engines": { @@ -3980,16 +4034,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz", + "integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.40.0", + "@typescript-eslint/tsconfig-utils": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4005,7 +4059,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { @@ -4035,16 +4089,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz", + "integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4055,17 +4109,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz", + "integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.40.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5402,20 +5456,20 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -8247,9 +8301,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", "bin": { @@ -8261,16 +8315,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.40.0.tgz", + "integrity": "sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.40.0", + "@typescript-eslint/parser": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8281,13 +8335,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "license": "MIT" }, "node_modules/unpipe": { @@ -8424,16 +8478,16 @@ } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", + "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", "postcss": "^8.5.6", - "rollup": "^4.40.0", + "rollup": "^4.43.0", "tinyglobby": "^0.2.14" }, "bin": { @@ -8498,10 +8552,13 @@ } }, "node_modules/vite/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -8512,9 +8569,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" diff --git a/package.json b/package.json index e53653c2..b016dcb0 100644 --- a/package.json +++ b/package.json @@ -76,26 +76,26 @@ "zod": "^4.0.5" }, "devDependencies": { - "@eslint/js": "^9.30.1", + "@eslint/js": "^9.34.0", "@types/better-sqlite3": "^7.6.13", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/jsonwebtoken": "^9.0.10", - "@types/node": "^24.0.13", + "@types/node": "^24.3.0", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@types/ssh2": "^1.15.5", "@types/ws": "^8.18.1", "@vitejs/plugin-react-swc": "^3.10.2", "autoprefixer": "^10.4.21", - "eslint": "^9.30.1", + "eslint": "^9.34.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", "ts-node": "^10.9.2", "tw-animate-css": "^1.3.5", - "typescript": "~5.8.3", - "typescript-eslint": "^8.35.1", - "vite": "^7.0.4" + "typescript": "~5.9.2", + "typescript-eslint": "^8.40.0", + "vite": "^7.1.3" } } From 0c2ac176a768072d3ac84b563ec2104a55d177fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:13:27 +0000 Subject: [PATCH 12/27] Bump the prod-patch-updates group with 19 updates Bumps the prod-patch-updates group with 19 updates: | Package | From | To | | --- | --- | --- | | [@radix-ui/react-accordion](https://github.com/radix-ui/primitives) | `1.2.11` | `1.2.12` | | [@radix-ui/react-checkbox](https://github.com/radix-ui/primitives) | `1.3.2` | `1.3.3` | | [@radix-ui/react-collapsible](https://github.com/radix-ui/primitives) | `1.1.11` | `1.1.12` | | [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives) | `2.1.15` | `2.1.16` | | [@radix-ui/react-popover](https://github.com/radix-ui/primitives) | `1.1.14` | `1.1.15` | | [@radix-ui/react-scroll-area](https://github.com/radix-ui/primitives) | `1.2.9` | `1.2.10` | | [@radix-ui/react-select](https://github.com/radix-ui/primitives) | `2.2.5` | `2.2.6` | | [@radix-ui/react-slider](https://github.com/radix-ui/primitives) | `1.3.5` | `1.3.6` | | [@radix-ui/react-switch](https://github.com/radix-ui/primitives) | `1.2.5` | `1.2.6` | | [@radix-ui/react-tabs](https://github.com/radix-ui/primitives) | `1.1.12` | `1.1.13` | | [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.1.11` | `4.1.12` | | [dotenv](https://github.com/motdotla/dotenv) | `17.2.0` | `17.2.1` | | [drizzle-orm](https://github.com/drizzle-team/drizzle-orm) | `0.44.3` | `0.44.4` | | [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.1.0` | `19.1.1` | | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `19.1.8` | `19.1.11` | | [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `19.1.0` | `19.1.1` | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `19.1.6` | `19.1.7` | | [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels) | `3.0.3` | `3.0.5` | | [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.1.11` | `4.1.12` | Updates `@radix-ui/react-accordion` from 1.2.11 to 1.2.12 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@radix-ui/react-checkbox` from 1.3.2 to 1.3.3 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@radix-ui/react-collapsible` from 1.1.11 to 1.1.12 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@radix-ui/react-dropdown-menu` from 2.1.15 to 2.1.16 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@radix-ui/react-popover` from 1.1.14 to 1.1.15 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@radix-ui/react-scroll-area` from 1.2.9 to 1.2.10 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@radix-ui/react-select` from 2.2.5 to 2.2.6 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@radix-ui/react-slider` from 1.3.5 to 1.3.6 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@radix-ui/react-switch` from 1.2.5 to 1.2.6 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@radix-ui/react-tabs` from 1.1.12 to 1.1.13 - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) Updates `@tailwindcss/vite` from 4.1.11 to 4.1.12 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.1.12/packages/@tailwindcss-vite) Updates `dotenv` from 17.2.0 to 17.2.1 - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v17.2.0...v17.2.1) Updates `drizzle-orm` from 0.44.3 to 0.44.4 - [Release notes](https://github.com/drizzle-team/drizzle-orm/releases) - [Commits](https://github.com/drizzle-team/drizzle-orm/compare/0.44.3...0.44.4) Updates `react` from 19.1.0 to 19.1.1 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.1.1/packages/react) Updates `@types/react` from 19.1.8 to 19.1.11 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `react-dom` from 19.1.0 to 19.1.1 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.1.1/packages/react-dom) Updates `@types/react-dom` from 19.1.6 to 19.1.7 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `react-resizable-panels` from 3.0.3 to 3.0.5 - [Release notes](https://github.com/bvaughn/react-resizable-panels/releases) - [Commits](https://github.com/bvaughn/react-resizable-panels/compare/3.0.3...3.0.5) Updates `tailwindcss` from 4.1.11 to 4.1.12 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.1.12/packages/tailwindcss) --- updated-dependencies: - dependency-name: "@radix-ui/react-accordion" dependency-version: 1.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@radix-ui/react-checkbox" dependency-version: 1.3.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@radix-ui/react-collapsible" dependency-version: 1.1.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@radix-ui/react-dropdown-menu" dependency-version: 2.1.16 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@radix-ui/react-popover" dependency-version: 1.1.15 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@radix-ui/react-scroll-area" dependency-version: 1.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@radix-ui/react-select" dependency-version: 2.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@radix-ui/react-slider" dependency-version: 1.3.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@radix-ui/react-switch" dependency-version: 1.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@radix-ui/react-tabs" dependency-version: 1.1.13 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@tailwindcss/vite" dependency-version: 4.1.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: dotenv dependency-version: 17.2.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: drizzle-orm dependency-version: 0.44.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: react dependency-version: 19.1.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@types/react" dependency-version: 19.1.11 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: react-dom dependency-version: 19.1.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: "@types/react-dom" dependency-version: 19.1.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: react-resizable-panels dependency-version: 3.0.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: tailwindcss dependency-version: 4.1.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 696 ++++++++++++++++++++-------------------------- package.json | 34 +-- 2 files changed, 312 insertions(+), 418 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc2db2ae..7fd1447b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,24 +9,24 @@ "version": "0.0.0", "dependencies": { "@hookform/resolvers": "^5.1.1", - "@radix-ui/react-accordion": "^1.2.11", + "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-avatar": "^1.1.10", - "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.7", - "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-progress": "^1.1.7", - "@radix-ui/react-scroll-area": "^1.2.9", - "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", - "@radix-ui/react-slider": "^1.3.5", + "@radix-ui/react-slider": "^1.3.6", "@radix-ui/react-slot": "^1.2.3", - "@radix-ui/react-switch": "^1.2.5", - "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", - "@tailwindcss/vite": "^4.1.11", + "@tailwindcss/vite": "^4.1.12", "@types/bcryptjs": "^2.4.6", "@types/multer": "^2.0.0", "@uiw/codemirror-extensions-hyper-link": "^4.24.1", @@ -48,8 +48,8 @@ "clsx": "^2.1.1", "cookie-parser": "^1.4.7", "cors": "^2.8.5", - "dotenv": "^17.2.0", - "drizzle-orm": "^0.44.3", + "dotenv": "^17.2.1", + "drizzle-orm": "^0.44.4", "express": "^5.1.0", "jose": "^5.2.3", "jsonwebtoken": "^9.0.2", @@ -58,10 +58,10 @@ "nanoid": "^5.1.5", "next-themes": "^0.4.6", "node-fetch": "^3.3.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", "react-hook-form": "^7.60.0", - "react-resizable-panels": "^3.0.3", + "react-resizable-panels": "^3.0.5", "react-xtermjs": "^1.0.10", "sonner": "^2.0.7", "ssh2": "^1.16.0", @@ -78,8 +78,8 @@ "@types/express": "^5.0.3", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^24.0.13", - "@types/react": "^19.1.8", - "@types/react-dom": "^19.1.6", + "@types/react": "^19.1.11", + "@types/react-dom": "^19.1.7", "@types/ssh2": "^1.15.5", "@types/ws": "^8.18.1", "@vitejs/plugin-react-swc": "^3.10.2", @@ -95,19 +95,6 @@ "vite": "^7.0.4" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/runtime": { "version": "7.27.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", @@ -1248,15 +1235,25 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1267,15 +1264,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1539,19 +1536,19 @@ "license": "MIT" }, "node_modules/@radix-ui/primitive": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", "license": "MIT" }, "node_modules/@radix-ui/react-accordion": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.11.tgz", - "integrity": "sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", + "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collapsible": "1.1.11", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", @@ -1626,15 +1623,15 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz", - "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", @@ -1656,16 +1653,16 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz", - "integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -1777,13 +1774,22 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "node_modules/@radix-ui/react-dismissable-layer": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", @@ -1810,98 +1816,17 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", - "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz", - "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, @@ -1921,9 +1846,9 @@ } }, "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", - "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2002,25 +1927,25 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz", - "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.10", - "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", @@ -2042,21 +1967,21 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", - "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.10", - "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", @@ -2079,9 +2004,9 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", - "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", @@ -2135,9 +2060,9 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", - "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", @@ -2206,12 +2131,12 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", - "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", @@ -2237,17 +2162,17 @@ } }, "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.9.tgz", - "integrity": "sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", + "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -2268,22 +2193,22 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz", - "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.10", - "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", @@ -2334,13 +2259,13 @@ } }, "node_modules/@radix-ui/react-slider": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.5.tgz", - "integrity": "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.6.tgz", + "integrity": "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", @@ -2385,12 +2310,12 @@ } }, "node_modules/@radix-ui/react-switch": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.5.tgz", - "integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", + "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", @@ -2414,18 +2339,18 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz", - "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2477,95 +2402,6 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", @@ -3310,24 +3146,24 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", - "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz", + "integrity": "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==", "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.5.1", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.11" + "tailwindcss": "4.1.12" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", - "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.12.tgz", + "integrity": "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -3338,24 +3174,24 @@ "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-x64": "4.1.11", - "@tailwindcss/oxide-freebsd-x64": "4.1.11", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-x64-musl": "4.1.11", - "@tailwindcss/oxide-wasm32-wasi": "4.1.11", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + "@tailwindcss/oxide-android-arm64": "4.1.12", + "@tailwindcss/oxide-darwin-arm64": "4.1.12", + "@tailwindcss/oxide-darwin-x64": "4.1.12", + "@tailwindcss/oxide-freebsd-x64": "4.1.12", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.12", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.12", + "@tailwindcss/oxide-linux-x64-musl": "4.1.12", + "@tailwindcss/oxide-wasm32-wasi": "4.1.12", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.12" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", - "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.12.tgz", + "integrity": "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==", "cpu": [ "arm64" ], @@ -3369,9 +3205,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", - "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.12.tgz", + "integrity": "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==", "cpu": [ "arm64" ], @@ -3385,9 +3221,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", - "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.12.tgz", + "integrity": "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==", "cpu": [ "x64" ], @@ -3401,9 +3237,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", - "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.12.tgz", + "integrity": "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==", "cpu": [ "x64" ], @@ -3417,9 +3253,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", - "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.12.tgz", + "integrity": "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==", "cpu": [ "arm" ], @@ -3433,9 +3269,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", - "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.12.tgz", + "integrity": "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==", "cpu": [ "arm64" ], @@ -3449,9 +3285,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", - "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.12.tgz", + "integrity": "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==", "cpu": [ "arm64" ], @@ -3465,9 +3301,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", - "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.12.tgz", + "integrity": "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==", "cpu": [ "x64" ], @@ -3481,9 +3317,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", - "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.12.tgz", + "integrity": "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==", "cpu": [ "x64" ], @@ -3497,9 +3333,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", - "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.12.tgz", + "integrity": "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -3514,21 +3350,75 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.11", - "@tybys/wasm-util": "^0.9.0", + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@emnapi/wasi-threads": "^1.0.4", + "@napi-rs/wasm-runtime": "^0.2.12", + "@tybys/wasm-util": "^0.10.0", "tslib": "^2.8.0" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.5", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.5", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", - "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz", + "integrity": "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==", "cpu": [ "arm64" ], @@ -3542,9 +3432,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", - "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz", + "integrity": "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==", "cpu": [ "x64" ], @@ -3558,14 +3448,14 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", - "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.12.tgz", + "integrity": "sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.11", - "@tailwindcss/oxide": "4.1.11", - "tailwindcss": "4.1.11" + "@tailwindcss/node": "4.1.12", + "@tailwindcss/oxide": "4.1.12", + "tailwindcss": "4.1.12" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" @@ -3741,9 +3631,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", - "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "version": "19.1.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.11.tgz", + "integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -3751,9 +3641,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", - "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz", + "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==", "devOptional": true, "license": "MIT", "peerDependencies": { @@ -5083,9 +4973,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz", - "integrity": "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", + "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -5095,9 +4985,9 @@ } }, "node_modules/drizzle-orm": { - "version": "0.44.3", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.3.tgz", - "integrity": "sha512-8nIiYQxOpgUicEL04YFojJmvC4DNO4KoyXsEIqN44+g6gNBr6hmVpWk3uyAt4CaTiRGDwoU+alfqNNeonLAFOQ==", + "version": "0.44.4", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.4.tgz", + "integrity": "sha512-ZyzKFpTC/Ut3fIqc2c0dPZ6nhchQXriTsqTNs4ayRgl6sZcFlMs9QZKPSHXK4bdOf41GHGWf+FrpcDDYwW+W6Q==", "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", @@ -5274,9 +5164,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", - "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -6253,9 +6143,9 @@ "license": "ISC" }, "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -6680,12 +6570,12 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/make-error": { @@ -7424,24 +7314,24 @@ } }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", "license": "MIT", "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.1.1" } }, "node_modules/react-hook-form": { @@ -7508,9 +7398,9 @@ } }, "node_modules/react-resizable-panels": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.3.tgz", - "integrity": "sha512-7HA8THVBHTzhDK4ON0tvlGXyMAJN1zBeRpuyyremSikgYh2ku6ltD7tsGQOcXx4NKPrZtYCm/5CBr+dkruTGQw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.5.tgz", + "integrity": "sha512-3z1yN25DMTXLg2wfyFrW32r5k4WEcUa3F7cJ2EgtNK07lnOs4mpM8yWLGunCpkhcQRwJX4fqoLcIh/pHPxzlmQ==", "license": "MIT", "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", @@ -7993,18 +7883,22 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", - "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz", + "integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==", "license": "MIT" }, "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/tar": { diff --git a/package.json b/package.json index e53653c2..b36acf2c 100644 --- a/package.json +++ b/package.json @@ -13,24 +13,24 @@ }, "dependencies": { "@hookform/resolvers": "^5.1.1", - "@radix-ui/react-accordion": "^1.2.11", + "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-avatar": "^1.1.10", - "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.7", - "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-progress": "^1.1.7", - "@radix-ui/react-scroll-area": "^1.2.9", - "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", - "@radix-ui/react-slider": "^1.3.5", + "@radix-ui/react-slider": "^1.3.6", "@radix-ui/react-slot": "^1.2.3", - "@radix-ui/react-switch": "^1.2.5", - "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", - "@tailwindcss/vite": "^4.1.11", + "@tailwindcss/vite": "^4.1.12", "@types/bcryptjs": "^2.4.6", "@types/multer": "^2.0.0", "@uiw/codemirror-extensions-hyper-link": "^4.24.1", @@ -52,8 +52,8 @@ "clsx": "^2.1.1", "cookie-parser": "^1.4.7", "cors": "^2.8.5", - "dotenv": "^17.2.0", - "drizzle-orm": "^0.44.3", + "dotenv": "^17.2.1", + "drizzle-orm": "^0.44.4", "express": "^5.1.0", "jose": "^5.2.3", "jsonwebtoken": "^9.0.2", @@ -62,10 +62,10 @@ "nanoid": "^5.1.5", "next-themes": "^0.4.6", "node-fetch": "^3.3.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", "react-hook-form": "^7.60.0", - "react-resizable-panels": "^3.0.3", + "react-resizable-panels": "^3.0.5", "react-xtermjs": "^1.0.10", "sonner": "^2.0.7", "ssh2": "^1.16.0", @@ -82,8 +82,8 @@ "@types/express": "^5.0.3", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^24.0.13", - "@types/react": "^19.1.8", - "@types/react-dom": "^19.1.6", + "@types/react": "^19.1.11", + "@types/react-dom": "^19.1.7", "@types/ssh2": "^1.15.5", "@types/ws": "^8.18.1", "@vitejs/plugin-react-swc": "^3.10.2", From c7fba8dbb18044b91afb8c2dd77d51430cecf456 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:14:46 +0000 Subject: [PATCH 13/27] Bump the prod-minor-updates group with 10 updates Bumps the prod-minor-updates group with 10 updates: | Package | From | To | | --- | --- | --- | | [@hookform/resolvers](https://github.com/react-hook-form/resolvers) | `5.1.1` | `5.2.1` | | [@uiw/codemirror-extensions-hyper-link](https://github.com/uiwjs/react-codemirror) | `4.24.1` | `4.25.1` | | [@uiw/codemirror-extensions-langs](https://github.com/uiwjs/react-codemirror) | `4.24.1` | `4.25.1` | | [@uiw/codemirror-themes](https://github.com/uiwjs/react-codemirror) | `4.24.1` | `4.25.1` | | [@uiw/react-codemirror](https://github.com/uiwjs/react-codemirror) | `4.24.1` | `4.25.1` | | [axios](https://github.com/axios/axios) | `1.10.0` | `1.11.0` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `0.525.0` | `0.541.0` | | [react-hook-form](https://github.com/react-hook-form/react-hook-form) | `7.60.0` | `7.62.0` | | [ssh2](https://github.com/mscdex/ssh2) | `1.16.0` | `1.17.0` | | [zod](https://github.com/colinhacks/zod) | `4.0.5` | `4.1.1` | Updates `@hookform/resolvers` from 5.1.1 to 5.2.1 - [Release notes](https://github.com/react-hook-form/resolvers/releases) - [Commits](https://github.com/react-hook-form/resolvers/compare/v5.1.1...v5.2.1) Updates `@uiw/codemirror-extensions-hyper-link` from 4.24.1 to 4.25.1 - [Release notes](https://github.com/uiwjs/react-codemirror/releases) - [Commits](https://github.com/uiwjs/react-codemirror/compare/v4.24.1...v4.25.1) Updates `@uiw/codemirror-extensions-langs` from 4.24.1 to 4.25.1 - [Release notes](https://github.com/uiwjs/react-codemirror/releases) - [Commits](https://github.com/uiwjs/react-codemirror/compare/v4.24.1...v4.25.1) Updates `@uiw/codemirror-themes` from 4.24.1 to 4.25.1 - [Release notes](https://github.com/uiwjs/react-codemirror/releases) - [Commits](https://github.com/uiwjs/react-codemirror/compare/v4.24.1...v4.25.1) Updates `@uiw/react-codemirror` from 4.24.1 to 4.25.1 - [Release notes](https://github.com/uiwjs/react-codemirror/releases) - [Commits](https://github.com/uiwjs/react-codemirror/compare/v4.24.1...v4.25.1) Updates `axios` from 1.10.0 to 1.11.0 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.10.0...v1.11.0) Updates `lucide-react` from 0.525.0 to 0.541.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/0.541.0/packages/lucide-react) Updates `react-hook-form` from 7.60.0 to 7.62.0 - [Release notes](https://github.com/react-hook-form/react-hook-form/releases) - [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md) - [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.60.0...v7.62.0) Updates `ssh2` from 1.16.0 to 1.17.0 - [Commits](https://github.com/mscdex/ssh2/compare/v1.16.0...v1.17.0) Updates `zod` from 4.0.5 to 4.1.1 - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](https://github.com/colinhacks/zod/compare/v4.0.5...v4.1.1) --- updated-dependencies: - dependency-name: "@hookform/resolvers" dependency-version: 5.2.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: "@uiw/codemirror-extensions-hyper-link" dependency-version: 4.25.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: "@uiw/codemirror-extensions-langs" dependency-version: 4.25.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: "@uiw/codemirror-themes" dependency-version: 4.25.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: "@uiw/react-codemirror" dependency-version: 4.25.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: axios dependency-version: 1.11.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: lucide-react dependency-version: 0.541.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: react-hook-form dependency-version: 7.62.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: ssh2 dependency-version: 1.17.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: zod dependency-version: 4.1.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates ... Signed-off-by: dependabot[bot] --- package-lock.json | 235 +++++++++++++++++++++------------------------- package.json | 20 ++-- 2 files changed, 116 insertions(+), 139 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc2db2ae..ce31b6ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "termix", "version": "0.0.0", "dependencies": { - "@hookform/resolvers": "^5.1.1", + "@hookform/resolvers": "^5.2.1", "@radix-ui/react-accordion": "^1.2.11", "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.3.2", @@ -29,10 +29,10 @@ "@tailwindcss/vite": "^4.1.11", "@types/bcryptjs": "^2.4.6", "@types/multer": "^2.0.0", - "@uiw/codemirror-extensions-hyper-link": "^4.24.1", - "@uiw/codemirror-extensions-langs": "^4.24.1", - "@uiw/codemirror-themes": "^4.24.1", - "@uiw/react-codemirror": "^4.24.1", + "@uiw/codemirror-extensions-hyper-link": "^4.25.1", + "@uiw/codemirror-extensions-langs": "^4.25.1", + "@uiw/codemirror-themes": "^4.25.1", + "@uiw/react-codemirror": "^4.25.1", "@xterm/addon-attach": "^0.11.0", "@xterm/addon-clipboard": "^0.1.0", "@xterm/addon-fit": "^0.10.0", @@ -40,7 +40,7 @@ "@xterm/addon-unicode11": "^0.8.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", - "axios": "^1.10.0", + "axios": "^1.11.0", "bcryptjs": "^3.0.2", "better-sqlite3": "^12.2.0", "chalk": "^4.1.2", @@ -53,23 +53,23 @@ "express": "^5.1.0", "jose": "^5.2.3", "jsonwebtoken": "^9.0.2", - "lucide-react": "^0.525.0", + "lucide-react": "^0.541.0", "multer": "^2.0.2", "nanoid": "^5.1.5", "next-themes": "^0.4.6", "node-fetch": "^3.3.2", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-hook-form": "^7.60.0", + "react-hook-form": "^7.62.0", "react-resizable-panels": "^3.0.3", "react-xtermjs": "^1.0.10", "sonner": "^2.0.7", - "ssh2": "^1.16.0", + "ssh2": "^1.17.0", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.11", "validator": "^13.15.15", "ws": "^8.18.3", - "zod": "^4.0.5" + "zod": "^4.1.1" }, "devDependencies": { "@eslint/js": "^9.30.1", @@ -256,18 +256,6 @@ "@lezer/lr": "^1.0.0" } }, - "node_modules/@codemirror/lang-lezer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-lezer/-/lang-lezer-6.0.2.tgz", - "integrity": "sha512-mcVAf8lw+sCfSlr2ivMqV8JtNmOQjSXdA1vHKRtoW0OZsz1k6qhF+DX0K2TbWlAThqiGgRkRSZyYzIoEtKB2uQ==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/lezer": "^1.0.0" - } - }, "node_modules/@codemirror/lang-liquid": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/@codemirror/lang-liquid/-/lang-liquid-6.2.3.tgz", @@ -1158,9 +1146,9 @@ "license": "MIT" }, "node_modules/@hookform/resolvers": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.1.1.tgz", - "integrity": "sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.1.tgz", + "integrity": "sha512-u0+6X58gkjMcxur1wRWokA7XsiiBJ6aK17aPZxhkoYiK5J+HcTx0Vhu9ovXe6H+dVpO6cjrn2FkJTryXEMlryQ==", "license": "MIT", "dependencies": { "@standard-schema/utils": "^0.3.0" @@ -1374,16 +1362,6 @@ "@lezer/lr": "^1.0.0" } }, - "node_modules/@lezer/lezer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@lezer/lezer/-/lezer-1.1.2.tgz", - "integrity": "sha512-O8yw3CxPhzYHB1hvwbdozjnAslhhR8A5BH7vfEMof0xk3p+/DFDfZkA9Tde6J+88WgtwaHy4Sy6ThZSkaI0Evw==", - "license": "MIT", - "dependencies": { - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, "node_modules/@lezer/lr": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", @@ -1475,25 +1453,6 @@ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "license": "MIT" }, - "node_modules/@nextjournal/lang-clojure": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@nextjournal/lang-clojure/-/lang-clojure-1.0.0.tgz", - "integrity": "sha512-gOCV71XrYD0DhwGoPMWZmZ0r92/lIHsqQu9QWdpZYYBwiChNwMO4sbVMP7eTuAqffFB2BTtCSC+1skSH9d3bNg==", - "license": "ISC", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@nextjournal/lezer-clojure": "1.0.0" - } - }, - "node_modules/@nextjournal/lezer-clojure": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@nextjournal/lezer-clojure/-/lezer-clojure-1.0.0.tgz", - "integrity": "sha512-VZyuGu4zw5mkTOwQBTaGVNWmsOZAPw5ZRxu1/Knk/Xfs7EDBIogwIs5UXTYkuECX5ZQB8eOB+wKA2pc7VyqaZQ==", - "license": "ISC", - "dependencies": { - "@lezer/lr": "^1.0.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2749,21 +2708,6 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, - "node_modules/@replit/codemirror-lang-csharp": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@replit/codemirror-lang-csharp/-/codemirror-lang-csharp-6.2.0.tgz", - "integrity": "sha512-6utbaWkoymhoAXj051mkRp+VIJlpwUgCX9Toevz3YatiZsz512fw3OVCedXQx+WcR0wb6zVHjChnuxqfCLtFVQ==", - "license": "MIT", - "peerDependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, "node_modules/@replit/codemirror-lang-nix": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@replit/codemirror-lang-nix/-/codemirror-lang-nix-6.0.1.tgz", @@ -3525,6 +3469,60 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", @@ -4077,9 +4075,9 @@ } }, "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.24.1.tgz", - "integrity": "sha512-o1m1a8eUS3fWERMbDFvN8t8sZUFPgDKNemmlQ5Ot2vKm+Ax84lKP1dhEFgkiOaZ1bDHk4T5h6SjHuTghrJHKww==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.1.tgz", + "integrity": "sha512-zxgA2QkvP3ZDKxTBc9UltNFTrSeFezGXcZtZj6qcsBxiMzowoEMP5mVwXcKjpzldpZVRuY+JCC+RsekEgid4vg==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -4104,9 +4102,9 @@ } }, "node_modules/@uiw/codemirror-extensions-hyper-link": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-hyper-link/-/codemirror-extensions-hyper-link-4.24.1.tgz", - "integrity": "sha512-qf3docpmsHHM0OKLO5m2Fc8t4G+pr1+k9QwrhlM2iolku/INbz+B1JzbRcSU0ow1EcxKtHRtCFE4Lnu6DwP7CQ==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-hyper-link/-/codemirror-extensions-hyper-link-4.25.1.tgz", + "integrity": "sha512-BVp+bnPI0LtqYXAPFWBqpLLLICoD8QsTAC/KQVRf7l+MO8FXCP0F/4WoM724eU4/2bcLefBkK1gBgCB1+Ug1CQ==", "license": "MIT", "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -4117,34 +4115,13 @@ } }, "node_modules/@uiw/codemirror-extensions-langs": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-langs/-/codemirror-extensions-langs-4.24.1.tgz", - "integrity": "sha512-8Q33k/UhNni2u5VvAHD+2mxe4hNIqZTNySSUcnJ7urV2lXXau+0fimsQlI+GQLF7gy5F1BUzIi+yvOMrEPK9Ig==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-langs/-/codemirror-extensions-langs-4.25.1.tgz", + "integrity": "sha512-P9Sxk0w8WgxxoOK4hC2yNV2f3shE0CH8gmk8lG5rDrAYYyuUrTsTmJANXh30TuQWCPCkEXwXZZVy+dbTYAgvMQ==", "license": "MIT", "dependencies": { - "@codemirror/lang-angular": "^0.1.0", - "@codemirror/lang-cpp": "^6.0.0", - "@codemirror/lang-css": "^6.2.0", - "@codemirror/lang-html": "^6.4.0", - "@codemirror/lang-java": "^6.0.0", - "@codemirror/lang-javascript": "^6.1.0", - "@codemirror/lang-json": "^6.0.0", - "@codemirror/lang-less": "^6.0.1", - "@codemirror/lang-lezer": "^6.0.0", - "@codemirror/lang-liquid": "^6.0.1", - "@codemirror/lang-markdown": "^6.1.0", - "@codemirror/lang-php": "^6.0.0", - "@codemirror/lang-python": "^6.1.0", - "@codemirror/lang-rust": "^6.0.0", - "@codemirror/lang-sass": "^6.0.1", - "@codemirror/lang-sql": "^6.4.0", - "@codemirror/lang-vue": "^0.1.1", - "@codemirror/lang-wast": "^6.0.0", - "@codemirror/lang-xml": "^6.0.0", - "@codemirror/language-data": ">=6.0.0", - "@codemirror/legacy-modes": ">=6.0.0", - "@nextjournal/lang-clojure": "^1.0.0", - "@replit/codemirror-lang-csharp": "^6.1.0", + "@codemirror/language": "^6.0.0", + "@codemirror/language-data": "^6.5.1", "@replit/codemirror-lang-nix": "^6.0.1", "@replit/codemirror-lang-solidity": "^6.0.1", "@replit/codemirror-lang-svelte": "^6.0.0", @@ -4154,14 +4131,14 @@ "url": "https://jaywcjlove.github.io/#/sponsor" }, "peerDependencies": { - "@codemirror/language-data": ">=6.0.0", - "@codemirror/legacy-modes": ">=6.0.0" + "@codemirror/language": ">=6.0.0", + "@codemirror/language-data": ">=6.0.0" } }, "node_modules/@uiw/codemirror-themes": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.24.1.tgz", - "integrity": "sha512-hduBbFNiWNW6nYa2/giKQ9YpzhWNw87BGpCjC+cXYMZ7bCD6q5DC6Hw+7z7ZwSzEaOQvV91lmirOjJ8hn9+pkg==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.1.tgz", + "integrity": "sha512-6o8tQ8bdq14RuVFpZ7l9u8KnuPq824uG3U1VV933Uhv8mfaxaoaOQSjv6T2bQUPhjH6ZlEu5+tAMkOfIL21eIQ==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -4178,16 +4155,16 @@ } }, "node_modules/@uiw/react-codemirror": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.24.1.tgz", - "integrity": "sha512-BivF4NLqbuBQK5gPVhSkOARi9nPXw8X5r25EnInPeY+I9l1dfEX8O9V6+0xHTlGHyUo0cNfGEF9t1KHEicUfJw==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.1.tgz", + "integrity": "sha512-eESBKHndoYkaEGlKCwRO4KrnTw1HkWBxVpEeqntoWTpoFEUYxdLWUYmkPBVk4/u8YzVy9g91nFfIRpqe5LjApg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.6", "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.24.1", + "@uiw/codemirror-extensions-basic-setup": "4.25.1", "codemirror": "^6.0.0" }, "funding": { @@ -4199,8 +4176,8 @@ "@codemirror/theme-one-dark": ">=6.0.0", "@codemirror/view": ">=6.0.0", "codemirror": ">=6.0.0", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "react": ">=17.0.0", + "react-dom": ">=17.0.0" } }, "node_modules/@vitejs/plugin-react-swc": { @@ -4447,13 +4424,13 @@ } }, "node_modules/axios": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", - "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, @@ -6671,9 +6648,9 @@ "license": "MIT" }, "node_modules/lucide-react": { - "version": "0.525.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.525.0.tgz", - "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", + "version": "0.541.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.541.0.tgz", + "integrity": "sha512-s0Vircsu5WaGv2KoJZ5+SoxiAJ3UXV5KqEM3eIFDHaHkcLIFdIWgXtZ412+Gh02UsdS7Was+jvEpBvPCWQISlg==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -7445,9 +7422,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.60.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.60.0.tgz", - "integrity": "sha512-SBrYOvMbDB7cV8ZfNpaiLcgjH/a1c7aK0lK+aNigpf4xWLO8q+o4tcvVurv3c4EOyzn/3dCsYt4GKD42VvJ/+A==", + "version": "7.62.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", + "integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -7909,9 +7886,9 @@ } }, "node_modules/ssh2": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", - "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", + "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", "hasInstallScript": true, "dependencies": { "asn1": "^0.2.6", @@ -7922,7 +7899,7 @@ }, "optionalDependencies": { "cpu-features": "~0.0.10", - "nan": "^2.20.0" + "nan": "^2.23.0" } }, "node_modules/statuses": { @@ -8633,9 +8610,9 @@ } }, "node_modules/zod": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.5.tgz", - "integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.1.tgz", + "integrity": "sha512-SgMZK/h8Tigt9nnKkfJMvB/mKjiJXaX26xegP4sa+0wHIFVFWVlsQGdhklDmuargBD3Hsi3rsQRIzwJIhTPJHA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index e53653c2..352a3e49 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "preview": "vite preview" }, "dependencies": { - "@hookform/resolvers": "^5.1.1", + "@hookform/resolvers": "^5.2.1", "@radix-ui/react-accordion": "^1.2.11", "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.3.2", @@ -33,10 +33,10 @@ "@tailwindcss/vite": "^4.1.11", "@types/bcryptjs": "^2.4.6", "@types/multer": "^2.0.0", - "@uiw/codemirror-extensions-hyper-link": "^4.24.1", - "@uiw/codemirror-extensions-langs": "^4.24.1", - "@uiw/codemirror-themes": "^4.24.1", - "@uiw/react-codemirror": "^4.24.1", + "@uiw/codemirror-extensions-hyper-link": "^4.25.1", + "@uiw/codemirror-extensions-langs": "^4.25.1", + "@uiw/codemirror-themes": "^4.25.1", + "@uiw/react-codemirror": "^4.25.1", "@xterm/addon-attach": "^0.11.0", "@xterm/addon-clipboard": "^0.1.0", "@xterm/addon-fit": "^0.10.0", @@ -44,7 +44,7 @@ "@xterm/addon-unicode11": "^0.8.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", - "axios": "^1.10.0", + "axios": "^1.11.0", "bcryptjs": "^3.0.2", "better-sqlite3": "^12.2.0", "chalk": "^4.1.2", @@ -57,23 +57,23 @@ "express": "^5.1.0", "jose": "^5.2.3", "jsonwebtoken": "^9.0.2", - "lucide-react": "^0.525.0", + "lucide-react": "^0.541.0", "multer": "^2.0.2", "nanoid": "^5.1.5", "next-themes": "^0.4.6", "node-fetch": "^3.3.2", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-hook-form": "^7.60.0", + "react-hook-form": "^7.62.0", "react-resizable-panels": "^3.0.3", "react-xtermjs": "^1.0.10", "sonner": "^2.0.7", - "ssh2": "^1.16.0", + "ssh2": "^1.17.0", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.11", "validator": "^13.15.15", "ws": "^8.18.3", - "zod": "^4.0.5" + "zod": "^4.1.1" }, "devDependencies": { "@eslint/js": "^9.30.1", From 6e175e2b3615a033830445558f3d32ab2faafb90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 21:14:55 +0000 Subject: [PATCH 14/27] Bump @vitejs/plugin-react-swc from 3.10.2 to 4.0.1 Bumps [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react-swc) from 3.10.2 to 4.0.1. - [Release notes](https://github.com/vitejs/vite-plugin-react/releases) - [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@4.0.1/packages/plugin-react-swc) --- updated-dependencies: - dependency-name: "@vitejs/plugin-react-swc" dependency-version: 4.0.1 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 171 ++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 115 insertions(+), 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc2db2ae..5ea143ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,7 +82,7 @@ "@types/react-dom": "^19.1.6", "@types/ssh2": "^1.15.5", "@types/ws": "^8.18.1", - "@vitejs/plugin-react-swc": "^3.10.2", + "@vitejs/plugin-react-swc": "^4.0.1", "autoprefixer": "^10.4.21", "eslint": "^9.30.1", "eslint-plugin-react-hooks": "^5.2.0", @@ -2811,9 +2811,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.11", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.11.tgz", - "integrity": "sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==", + "version": "1.0.0-beta.32", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz", + "integrity": "sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==", "dev": true, "license": "MIT" }, @@ -3084,15 +3084,15 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.14.tgz", - "integrity": "sha512-CJSn2vstd17ddWIHBsjuD4OQnn9krQfaq6EO+w9YfId5DKznyPmzxAARlOXG99cC8/3Kli8ysKy6phL43bSr0w==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", + "integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.23" + "@swc/types": "^0.1.24" }, "engines": { "node": ">=10" @@ -3102,16 +3102,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.12.14", - "@swc/core-darwin-x64": "1.12.14", - "@swc/core-linux-arm-gnueabihf": "1.12.14", - "@swc/core-linux-arm64-gnu": "1.12.14", - "@swc/core-linux-arm64-musl": "1.12.14", - "@swc/core-linux-x64-gnu": "1.12.14", - "@swc/core-linux-x64-musl": "1.12.14", - "@swc/core-win32-arm64-msvc": "1.12.14", - "@swc/core-win32-ia32-msvc": "1.12.14", - "@swc/core-win32-x64-msvc": "1.12.14" + "@swc/core-darwin-arm64": "1.13.5", + "@swc/core-darwin-x64": "1.13.5", + "@swc/core-linux-arm-gnueabihf": "1.13.5", + "@swc/core-linux-arm64-gnu": "1.13.5", + "@swc/core-linux-arm64-musl": "1.13.5", + "@swc/core-linux-x64-gnu": "1.13.5", + "@swc/core-linux-x64-musl": "1.13.5", + "@swc/core-win32-arm64-msvc": "1.13.5", + "@swc/core-win32-ia32-msvc": "1.13.5", + "@swc/core-win32-x64-msvc": "1.13.5" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -3123,9 +3123,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.14.tgz", - "integrity": "sha512-HNukQoOKgMsHSETj8vgGGKK3SEcH7Cz6k4bpntCxBKNkO3sH7RcBTDulWGGHJfZaDNix7Rw2ExUVWtLZlzkzXg==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.5.tgz", + "integrity": "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==", "cpu": [ "arm64" ], @@ -3140,9 +3140,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.14.tgz", - "integrity": "sha512-4Ttf3Obtk3MvFrR0e04qr6HfXh4L1Z+K3dRej63TAFuYpo+cPXeOZdPUddAW73lSUGkj+61IHnGPoXD3OQYy4Q==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.5.tgz", + "integrity": "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==", "cpu": [ "x64" ], @@ -3157,9 +3157,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.14.tgz", - "integrity": "sha512-zhJOH2KWjtQpzJ27Xjw/RKLVOa1aiEJC2b70xbCwEX6ZTVAl8tKbhkZ3GMphhfVmLJ9gf/2UQR58oxVnsXqX5Q==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.5.tgz", + "integrity": "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==", "cpu": [ "arm" ], @@ -3174,9 +3174,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.14.tgz", - "integrity": "sha512-akUAe1YrBqZf1EDdUxahQ8QZnJi8Ts6Ya0jf6GBIMvnXL4Y6QIuvKTRwfNxy7rJ+x9zpzP1Vlh14ZZkSKZ1EGA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.5.tgz", + "integrity": "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==", "cpu": [ "arm64" ], @@ -3191,9 +3191,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.14.tgz", - "integrity": "sha512-ZkOOIpSMXuPAjfOXEIAEQcrPOgLi6CaXvA5W+GYnpIpFG21Nd0qb0WbwFRv4K8BRtl993Q21v0gPpOaFHU+wdA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.5.tgz", + "integrity": "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==", "cpu": [ "arm64" ], @@ -3208,9 +3208,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.14.tgz", - "integrity": "sha512-71EPPccwJiJUxd2aMwNlTfom2mqWEWYGdbeTju01tzSHsEuD7E6ePlgC3P3ngBqB3urj41qKs87z7zPOswT5Iw==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.5.tgz", + "integrity": "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==", "cpu": [ "x64" ], @@ -3225,9 +3225,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.14.tgz", - "integrity": "sha512-nImF1hZJqKTcl0WWjHqlelOhvuB9rU9kHIw/CmISBUZXogjLIvGyop1TtJNz0ULcz2Oxr3Q2YpwfrzsgvgbGkA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.5.tgz", + "integrity": "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==", "cpu": [ "x64" ], @@ -3242,9 +3242,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.14.tgz", - "integrity": "sha512-sABFQFxSuStFoxvEWZUHWYldtB1B4A9eDNFd4Ty50q7cemxp7uoscFoaCqfXSGNBwwBwpS5EiPB6YN4y6hqmLQ==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.5.tgz", + "integrity": "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==", "cpu": [ "arm64" ], @@ -3259,9 +3259,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.14.tgz", - "integrity": "sha512-KBznRB02NASkpepRdWIK4f1AvmaJCDipKWdW1M1xV9QL2tE4aySJFojVuG1+t0tVDkjRfwcZjycQfRoJ4RjD7Q==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.5.tgz", + "integrity": "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==", "cpu": [ "ia32" ], @@ -3276,9 +3276,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.12.14", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.14.tgz", - "integrity": "sha512-SymoP2CJHzrYaFKjWvuQljcF7BkTpzaS1vpywv7K9EzdTb5N8qPDvNd+PhWUqBz9JHBhbJxpaeTDQBXF/WWPmw==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.5.tgz", + "integrity": "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==", "cpu": [ "x64" ], @@ -3300,9 +3300,9 @@ "license": "Apache-2.0" }, "node_modules/@swc/types": { - "version": "0.1.23", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz", - "integrity": "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==", + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.24.tgz", + "integrity": "sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3525,6 +3525,60 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", @@ -4204,17 +4258,20 @@ } }, "node_modules/@vitejs/plugin-react-swc": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.10.2.tgz", - "integrity": "sha512-xD3Rdvrt5LgANug7WekBn1KhcvLn1H3jNBfJRL3reeOIua/WnZOEV5qi5qIBq5T8R0jUDmRtxuvk4bPhzGHDWw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-4.0.1.tgz", + "integrity": "sha512-NQhPjysi5duItyrMd5JWZFf2vNOuSMyw+EoZyTBDzk+DkfYD8WNrsUs09sELV2cr1P15nufsN25hsUBt4CKF9Q==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-beta.11", - "@swc/core": "^1.11.31" + "@rolldown/pluginutils": "1.0.0-beta.32", + "@swc/core": "^1.13.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4 || ^5 || ^6 || ^7.0.0-beta.0" + "vite": "^4 || ^5 || ^6 || ^7" } }, "node_modules/@xterm/addon-attach": { diff --git a/package.json b/package.json index e53653c2..eec23c0c 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "@types/react-dom": "^19.1.6", "@types/ssh2": "^1.15.5", "@types/ws": "^8.18.1", - "@vitejs/plugin-react-swc": "^3.10.2", + "@vitejs/plugin-react-swc": "^4.0.1", "autoprefixer": "^10.4.21", "eslint": "^9.30.1", "eslint-plugin-react-hooks": "^5.2.0", From b9bd00f86e140bb40eb75f197cd8f7cf22997895 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Mon, 25 Aug 2025 18:00:34 -0500 Subject: [PATCH 15/27] Migrate everytihng into the main-axios and update the routing to fix localhost issues. --- src/App.tsx | 11 +- src/ui/Admin/AdminSettings.tsx | 38 ++++--- src/ui/Homepage/Homepage.tsx | 18 ++-- src/ui/Homepage/HomepageAlertManager.tsx | 17 +-- src/ui/Homepage/HompageUpdateLog.tsx | 16 +-- src/ui/Navigation/LeftSidebar.tsx | 53 ++++----- src/ui/main-axios.ts | 131 ++++++++++++++++++++++- 7 files changed, 190 insertions(+), 94 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index cb2881da..223e6246 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,13 +4,10 @@ import {Homepage} from "@/ui/Homepage/Homepage.tsx" import {AppView} from "@/ui/Navigation/AppView.tsx" import {HostManager} from "@/ui/apps/Host Manager/HostManager.tsx" import {TabProvider, useTabs} from "@/ui/Navigation/Tabs/TabContext.tsx" -import axios from "axios" import {TopNavbar} from "@/ui/Navigation/TopNavbar.tsx"; import { AdminSettings } from "@/ui/Admin/AdminSettings"; import { Toaster } from "@/components/ui/sonner"; - -const apiBase = import.meta.env.DEV ? "http://localhost:8081/users" : "/users"; -const API = axios.create({baseURL: apiBase}); +import { getUserInfo } from "@/ui/main-axios.ts"; function getCookie(name: string) { return document.cookie.split('; ').reduce((r, v) => { @@ -39,11 +36,11 @@ function AppContent() { const jwt = getCookie("jwt"); if (jwt) { setAuthLoading(true); - API.get("/me", {headers: {Authorization: `Bearer ${jwt}`}}) + getUserInfo() .then((meRes) => { setIsAuthenticated(true); - setIsAdmin(!!meRes.data.is_admin); - setUsername(meRes.data.username || null); + setIsAdmin(!!meRes.is_admin); + setUsername(meRes.username || null); }) .catch((err) => { setIsAuthenticated(false); diff --git a/src/ui/Admin/AdminSettings.tsx b/src/ui/Admin/AdminSettings.tsx index 1f18501c..bece9c0a 100644 --- a/src/ui/Admin/AdminSettings.tsx +++ b/src/ui/Admin/AdminSettings.tsx @@ -16,10 +16,16 @@ import { TableRow, } from "@/components/ui/table.tsx"; import {Shield, Trash2, Users} from "lucide-react"; -import axios from "axios"; - -const apiBase = import.meta.env.DEV ? "http://localhost:8081/users" : "/users"; -const API = axios.create({baseURL: apiBase}); +import { + getOIDCConfig, + getRegistrationAllowed, + getUserList, + updateRegistrationAllowed, + updateOIDCConfig, + makeUserAdmin, + removeAdminStatus, + deleteUser +} from "@/ui/main-axios.ts"; function getCookie(name: string) { return document.cookie.split('; ').reduce((r, v) => { @@ -67,9 +73,9 @@ export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React. React.useEffect(() => { const jwt = getCookie("jwt"); if (!jwt) return; - API.get("/oidc-config", {headers: {Authorization: `Bearer ${jwt}`}}) + getOIDCConfig() .then(res => { - if (res.data) setOidcConfig(res.data); + if (res) setOidcConfig(res); }) .catch(() => { }); @@ -77,10 +83,10 @@ export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React. }, []); React.useEffect(() => { - API.get("/registration-allowed") + getRegistrationAllowed() .then(res => { - if (typeof res?.data?.allowed === 'boolean') { - setAllowRegistration(res.data.allowed); + if (typeof res?.allowed === 'boolean') { + setAllowRegistration(res.allowed); } }) .catch(() => { @@ -92,8 +98,8 @@ export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React. if (!jwt) return; setUsersLoading(true); try { - const response = await API.get("/list", {headers: {Authorization: `Bearer ${jwt}`}}); - setUsers(response.data.users); + const response = await getUserList(); + setUsers(response.users); } finally { setUsersLoading(false); } @@ -103,7 +109,7 @@ export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React. setRegLoading(true); const jwt = getCookie("jwt"); try { - await API.patch("/registration-allowed", {allowed: checked}, {headers: {Authorization: `Bearer ${jwt}`}}); + await updateRegistrationAllowed(checked); setAllowRegistration(checked); } finally { setRegLoading(false); @@ -126,7 +132,7 @@ export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React. const jwt = getCookie("jwt"); try { - await API.post("/oidc-config", oidcConfig, {headers: {Authorization: `Bearer ${jwt}`}}); + await updateOIDCConfig(oidcConfig); setOidcSuccess("OIDC configuration updated successfully!"); } catch (err: any) { setOidcError(err?.response?.data?.error || "Failed to update OIDC configuration"); @@ -147,7 +153,7 @@ export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React. setMakeAdminSuccess(null); const jwt = getCookie("jwt"); try { - await API.post("/make-admin", {username: newAdminUsername.trim()}, {headers: {Authorization: `Bearer ${jwt}`}}); + await makeUserAdmin(newAdminUsername.trim()); setMakeAdminSuccess(`User ${newAdminUsername} is now an admin`); setNewAdminUsername(""); fetchUsers(); @@ -162,7 +168,7 @@ export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React. if (!confirm(`Remove admin status from ${username}?`)) return; const jwt = getCookie("jwt"); try { - await API.post("/remove-admin", {username}, {headers: {Authorization: `Bearer ${jwt}`}}); + await removeAdminStatus(username); fetchUsers(); } catch { } @@ -172,7 +178,7 @@ export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React. if (!confirm(`Delete user ${username}? This cannot be undone.`)) return; const jwt = getCookie("jwt"); try { - await API.delete("/delete-user", {headers: {Authorization: `Bearer ${jwt}`}, data: {username}}); + await deleteUser(username); fetchUsers(); } catch { } diff --git a/src/ui/Homepage/Homepage.tsx b/src/ui/Homepage/Homepage.tsx index 38740e9d..3349115b 100644 --- a/src/ui/Homepage/Homepage.tsx +++ b/src/ui/Homepage/Homepage.tsx @@ -1,9 +1,9 @@ import React, {useEffect, useState} from "react"; import {HomepageAuth} from "@/ui/Homepage/HomepageAuth.tsx"; -import axios from "axios"; import {HomepageUpdateLog} from "@/ui/Homepage/HompageUpdateLog.tsx"; import {HomepageAlertManager} from "@/ui/Homepage/HomepageAlertManager.tsx"; import {Button} from "@/components/ui/button.tsx"; +import { getUserInfo, getDatabaseHealth } from "@/ui/main-axios.ts"; interface HomepageProps { onSelectView: (view: string) => void; @@ -25,12 +25,6 @@ function setCookie(name: string, value: string, days = 7) { document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/`; } -const apiBase = import.meta.env.DEV ? "http://localhost:8081/users" : "/users"; - -const API = axios.create({ - baseURL: apiBase, -}); - export function Homepage({ onSelectView, isAuthenticated, @@ -53,13 +47,13 @@ export function Homepage({ const jwt = getCookie("jwt"); if (jwt) { Promise.all([ - API.get("/me", {headers: {Authorization: `Bearer ${jwt}`}}), - API.get("/db-health") + getUserInfo(), + getDatabaseHealth() ]) .then(([meRes]) => { - setIsAdmin(!!meRes.data.is_admin); - setUsername(meRes.data.username || null); - setUserId(meRes.data.userId || null); + setIsAdmin(!!meRes.is_admin); + setUsername(meRes.username || null); + setUserId(meRes.userId || null); setDbError(null); }) .catch((err) => { diff --git a/src/ui/Homepage/HomepageAlertManager.tsx b/src/ui/Homepage/HomepageAlertManager.tsx index e0a8c252..4aa8dd70 100644 --- a/src/ui/Homepage/HomepageAlertManager.tsx +++ b/src/ui/Homepage/HomepageAlertManager.tsx @@ -1,7 +1,7 @@ import React, {useEffect, useState} from "react"; import {HomepageAlertCard} from "./HomepageAlertCard.tsx"; import {Button} from "@/components/ui/button.tsx"; -import axios from "axios"; +import { getUserAlerts, dismissAlert } from "@/ui/main-axios.ts"; interface TermixAlert { id: string; @@ -19,12 +19,6 @@ interface AlertManagerProps { loggedIn: boolean; } -const apiBase = import.meta.env.DEV ? "http://localhost:8081/alerts" : "/alerts"; - -const API = axios.create({ - baseURL: apiBase, -}); - export function HomepageAlertManager({userId, loggedIn}: AlertManagerProps): React.ReactElement { const [alerts, setAlerts] = useState([]); const [currentAlertIndex, setCurrentAlertIndex] = useState(0); @@ -44,9 +38,9 @@ export function HomepageAlertManager({userId, loggedIn}: AlertManagerProps): Rea setError(null); try { - const response = await API.get(`/user/${userId}`); + const response = await getUserAlerts(userId); - const userAlerts = response.data.alerts || []; + const userAlerts = response.alerts || []; const sortedAlerts = userAlerts.sort((a: TermixAlert, b: TermixAlert) => { const priorityOrder = {critical: 4, high: 3, medium: 2, low: 1}; @@ -73,10 +67,7 @@ export function HomepageAlertManager({userId, loggedIn}: AlertManagerProps): Rea if (!userId) return; try { - const response = await API.post('/dismiss', { - userId, - alertId - }); + await dismissAlert(userId, alertId); setAlerts(prev => { const newAlerts = prev.filter(alert => alert.id !== alertId); diff --git a/src/ui/Homepage/HompageUpdateLog.tsx b/src/ui/Homepage/HompageUpdateLog.tsx index 203327e2..908b1ae6 100644 --- a/src/ui/Homepage/HompageUpdateLog.tsx +++ b/src/ui/Homepage/HompageUpdateLog.tsx @@ -2,7 +2,7 @@ import React, {useEffect, useState} from "react"; import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert.tsx"; import {Button} from "@/components/ui/button.tsx"; import {Separator} from "@/components/ui/separator.tsx"; -import axios from "axios"; +import { getReleasesRSS, getVersionInfo } from "@/ui/main-axios.ts"; interface HomepageUpdateLogProps extends React.ComponentProps<"div"> { loggedIn: boolean; @@ -50,12 +50,6 @@ interface VersionResponse { cache_age?: number; } -const apiBase = import.meta.env.DEV ? "http://localhost:8081" : ""; - -const API = axios.create({ - baseURL: apiBase, -}); - export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) { const [releases, setReleases] = useState(null); const [versionInfo, setVersionInfo] = useState(null); @@ -66,12 +60,12 @@ export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) { if (loggedIn) { setLoading(true); Promise.all([ - API.get('/releases/rss?per_page=100'), - API.get('/version/') + getReleasesRSS(100), + getVersionInfo() ]) .then(([releasesRes, versionRes]) => { - setReleases(releasesRes.data); - setVersionInfo(versionRes.data); + setReleases(releasesRes); + setVersionInfo(versionRes); setError(null); }) .catch(err => { diff --git a/src/ui/Navigation/LeftSidebar.tsx b/src/ui/Navigation/LeftSidebar.tsx index 53c50b01..d75f182a 100644 --- a/src/ui/Navigation/LeftSidebar.tsx +++ b/src/ui/Navigation/LeftSidebar.tsx @@ -45,11 +45,18 @@ import { TableHeader, TableRow, } from "@/components/ui/table.tsx"; -import axios from "axios"; import {Card} from "@/components/ui/card.tsx"; import {FolderCard} from "@/ui/Navigation/Hosts/FolderCard.tsx"; import {getSSHHosts} from "@/ui/main-axios.ts"; import {useTabs} from "@/ui/Navigation/Tabs/TabContext.tsx"; +import { + getOIDCConfig, + getUserList, + makeUserAdmin, + removeAdminStatus, + deleteUser, + deleteAccount +} from "@/ui/main-axios.ts"; interface SSHHost { id: number; @@ -95,11 +102,7 @@ function getCookie(name: string) { }, ""); } -const apiBase = import.meta.env.DEV ? "http://localhost:8081/users" : "/users"; -const API = axios.create({ - baseURL: apiBase, -}); export function LeftSidebar({ onSelectView, @@ -162,9 +165,9 @@ export function LeftSidebar({ if (adminSheetOpen) { const jwt = getCookie("jwt"); if (jwt && isAdmin) { - API.get("/oidc-config").then(res => { - if (res.data) { - setOidcConfig(res.data); + getOIDCConfig().then(res => { + if (res) { + setOidcConfig(res); } }).catch((error) => { }); @@ -308,10 +311,7 @@ export function LeftSidebar({ const jwt = getCookie("jwt"); try { - await API.delete("/delete-account", { - headers: {Authorization: `Bearer ${jwt}`}, - data: {password: deletePassword} - }); + await deleteAccount(deletePassword); handleLogout(); } catch (err: any) { @@ -329,12 +329,10 @@ export function LeftSidebar({ setUsersLoading(true); try { - const response = await API.get("/list", { - headers: {Authorization: `Bearer ${jwt}`} - }); - setUsers(response.data.users); + const response = await getUserList(); + setUsers(response.users); - const adminUsers = response.data.users.filter((user: any) => user.is_admin); + const adminUsers = response.users.filter((user: any) => user.is_admin); setAdminCount(adminUsers.length); } catch (err: any) { } finally { @@ -350,10 +348,8 @@ export function LeftSidebar({ } try { - const response = await API.get("/list", { - headers: {Authorization: `Bearer ${jwt}`} - }); - const adminUsers = response.data.users.filter((user: any) => user.is_admin); + const response = await getUserList(); + const adminUsers = response.users.filter((user: any) => user.is_admin); setAdminCount(adminUsers.length); } catch (err: any) { } @@ -373,10 +369,7 @@ export function LeftSidebar({ const jwt = getCookie("jwt"); try { - await API.post("/make-admin", - {username: newAdminUsername.trim()}, - {headers: {Authorization: `Bearer ${jwt}`}} - ); + await makeUserAdmin(newAdminUsername.trim()); setMakeAdminSuccess(`User ${newAdminUsername} is now an admin`); setNewAdminUsername(""); fetchUsers(); @@ -396,10 +389,7 @@ export function LeftSidebar({ const jwt = getCookie("jwt"); try { - await API.post("/remove-admin", - {username}, - {headers: {Authorization: `Bearer ${jwt}`}} - ); + await removeAdminStatus(username); fetchUsers(); } catch (err: any) { } @@ -414,10 +404,7 @@ export function LeftSidebar({ const jwt = getCookie("jwt"); try { - await API.delete("/delete-user", { - headers: {Authorization: `Bearer ${jwt}`}, - data: {username} - }); + await deleteUser(username); fetchUsers(); } catch (err: any) { } diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts index 85766006..3342b7c1 100644 --- a/src/ui/main-axios.ts +++ b/src/ui/main-axios.ts @@ -1,4 +1,4 @@ -import axios, { AxiosError, AxiosInstance } from 'axios'; +import axios, { AxiosError, type AxiosInstance } from 'axios'; // ============================================================================ // TYPES & INTERFACES @@ -201,7 +201,10 @@ function createApiInstance(baseURL: string): AxiosInstance { // API INSTANCES // ============================================================================ -const isDev = process.env.NODE_ENV === 'development' || window.location.hostname === 'localhost'; +// Check if we're in development mode (Vite dev server) or if we're accessing via a specific port +// that indicates we're using nginx proxy (not localhost:3000 or similar dev ports) +const isDev = process.env.NODE_ENV === 'development' && + (window.location.port === '3000' || window.location.port === '5173' || window.location.port === ''); // SSH Host Management API (port 8081) export const sshHostApi = createApiInstance( @@ -820,4 +823,128 @@ export async function getOIDCAuthorizeUrl(): Promise { } catch (error) { handleApiError(error, 'get OIDC authorize URL'); } +} + +// ============================================================================ +// USER MANAGEMENT +// ============================================================================ + +export async function getUserList(): Promise<{ users: UserInfo[] }> { + try { + const response = await authApi.get('/list'); + return response.data; + } catch (error) { + handleApiError(error, 'fetch user list'); + } +} + +export async function makeUserAdmin(username: string): Promise { + try { + const response = await authApi.post('/make-admin', { username }); + return response.data; + } catch (error) { + handleApiError(error, 'make user admin'); + } +} + +export async function removeAdminStatus(username: string): Promise { + try { + const response = await authApi.post('/remove-admin', { username }); + return response.data; + } catch (error) { + handleApiError(error, 'remove admin status'); + } +} + +export async function deleteUser(username: string): Promise { + try { + const response = await authApi.delete('/delete-user', { data: { username } }); + return response.data; + } catch (error) { + handleApiError(error, 'delete user'); + } +} + +export async function deleteAccount(password: string): Promise { + try { + const response = await authApi.delete('/delete-account', { data: { password } }); + return response.data; + } catch (error) { + handleApiError(error, 'delete account'); + } +} + +export async function updateRegistrationAllowed(allowed: boolean): Promise { + try { + const response = await authApi.patch('/registration-allowed', { allowed }); + return response.data; + } catch (error) { + handleApiError(error, 'update registration allowed'); + } +} + +export async function updateOIDCConfig(config: any): Promise { + try { + const response = await authApi.post('/oidc-config', config); + return response.data; + } catch (error) { + handleApiError(error, 'update OIDC config'); + } +} + +// ============================================================================ +// ALERTS +// ============================================================================ + +export async function getUserAlerts(userId: string): Promise<{ alerts: any[] }> { + try { + const response = await authApi.get(`/alerts/user/${userId}`); + return response.data; + } catch (error) { + handleApiError(error, 'fetch user alerts'); + } +} + +export async function dismissAlert(userId: string, alertId: string): Promise { + try { + const response = await authApi.post('/alerts/dismiss', { userId, alertId }); + return response.data; + } catch (error) { + handleApiError(error, 'dismiss alert'); + } +} + +// ============================================================================ +// UPDATES & RELEASES +// ============================================================================ + +export async function getReleasesRSS(perPage: number = 100): Promise { + try { + const response = await authApi.get(`/releases/rss?per_page=${perPage}`); + return response.data; + } catch (error) { + handleApiError(error, 'fetch releases RSS'); + } +} + +export async function getVersionInfo(): Promise { + try { + const response = await authApi.get('/version/'); + return response.data; + } catch (error) { + handleApiError(error, 'fetch version info'); + } +} + +// ============================================================================ +// DATABASE HEALTH +// ============================================================================ + +export async function getDatabaseHealth(): Promise { + try { + const response = await authApi.get('/db-health'); + return response.data; + } catch (error) { + handleApiError(error, 'check database health'); + } } \ No newline at end of file From a34c60947da8d674ea37b1384e50e27b7efe4ca6 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Wed, 27 Aug 2025 11:24:17 -0500 Subject: [PATCH 16/27] Finish migration into main-axios --- src/ui/main-axios.ts | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts index 3342b7c1..efa78e6b 100644 --- a/src/ui/main-axios.ts +++ b/src/ui/main-axios.ts @@ -187,7 +187,6 @@ function createApiInstance(baseURL: string): AxiosInstance { (response) => response, (error: AxiosError) => { if (error.response?.status === 401) { - // Token expired or invalid - clear cookie document.cookie = 'jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; } return Promise.reject(error); @@ -201,8 +200,6 @@ function createApiInstance(baseURL: string): AxiosInstance { // API INSTANCES // ============================================================================ -// Check if we're in development mode (Vite dev server) or if we're accessing via a specific port -// that indicates we're using nginx proxy (not localhost:3000 or similar dev ports) const isDev = process.env.NODE_ENV === 'development' && (window.location.port === '3000' || window.location.port === '5173' || window.location.port === ''); @@ -226,9 +223,9 @@ export const statsApi = createApiInstance( isDev ? 'http://localhost:8085' : '' ); -// Authentication API (port 8081) +// Authentication API (port 8081) - includes users, alerts, version, releases export const authApi = createApiInstance( - isDev ? 'http://localhost:8081/users' : '/users' + isDev ? 'http://localhost:8081' : '' ); // ============================================================================ @@ -471,7 +468,6 @@ export async function getFileManagerRecent(hostId: number): Promise { export async function registerUser(username: string, password: string): Promise { try { - const response = await authApi.post('/create', { username, password }); + const response = await authApi.post('/users/create', { username, password }); return response.data; } catch (error) { handleApiError(error, 'register user'); @@ -746,7 +742,7 @@ export async function registerUser(username: string, password: string): Promise< export async function loginUser(username: string, password: string): Promise { try { - const response = await authApi.post('/login', { username, password }); + const response = await authApi.post('/users/login', { username, password }); return response.data; } catch (error) { handleApiError(error, 'login user'); @@ -755,7 +751,7 @@ export async function loginUser(username: string, password: string): Promise { try { - const response = await authApi.get('/me'); + const response = await authApi.get('/users/me'); return response.data; } catch (error) { handleApiError(error, 'fetch user info'); @@ -764,7 +760,7 @@ export async function getUserInfo(): Promise { export async function getRegistrationAllowed(): Promise<{ allowed: boolean }> { try { - const response = await authApi.get('/registration-allowed'); + const response = await authApi.get('/users/registration-allowed'); return response.data; } catch (error) { handleApiError(error, 'check registration status'); @@ -773,7 +769,7 @@ export async function getRegistrationAllowed(): Promise<{ allowed: boolean }> { export async function getOIDCConfig(): Promise { try { - const response = await authApi.get('/oidc-config'); + const response = await authApi.get('/users/oidc-config'); return response.data; } catch (error) { handleApiError(error, 'fetch OIDC config'); @@ -782,7 +778,7 @@ export async function getOIDCConfig(): Promise { export async function getUserCount(): Promise { try { - const response = await authApi.get('/count'); + const response = await authApi.get('/users/count'); return response.data; } catch (error) { handleApiError(error, 'fetch user count'); @@ -791,7 +787,7 @@ export async function getUserCount(): Promise { export async function initiatePasswordReset(username: string): Promise { try { - const response = await authApi.post('/initiate-reset', { username }); + const response = await authApi.post('/users/initiate-reset', { username }); return response.data; } catch (error) { handleApiError(error, 'initiate password reset'); @@ -800,7 +796,7 @@ export async function initiatePasswordReset(username: string): Promise { export async function verifyPasswordResetCode(username: string, resetCode: string): Promise { try { - const response = await authApi.post('/verify-reset-code', { username, resetCode }); + const response = await authApi.post('/users/verify-reset-code', { username, resetCode }); return response.data; } catch (error) { handleApiError(error, 'verify reset code'); @@ -809,7 +805,7 @@ export async function verifyPasswordResetCode(username: string, resetCode: strin export async function completePasswordReset(username: string, tempToken: string, newPassword: string): Promise { try { - const response = await authApi.post('/complete-reset', { username, tempToken, newPassword }); + const response = await authApi.post('/users/complete-reset', { username, tempToken, newPassword }); return response.data; } catch (error) { handleApiError(error, 'complete password reset'); @@ -818,7 +814,7 @@ export async function completePasswordReset(username: string, tempToken: string, export async function getOIDCAuthorizeUrl(): Promise { try { - const response = await authApi.get('/oidc/authorize'); + const response = await authApi.get('/users/oidc/authorize'); return response.data; } catch (error) { handleApiError(error, 'get OIDC authorize URL'); @@ -831,7 +827,7 @@ export async function getOIDCAuthorizeUrl(): Promise { export async function getUserList(): Promise<{ users: UserInfo[] }> { try { - const response = await authApi.get('/list'); + const response = await authApi.get('/users/list'); return response.data; } catch (error) { handleApiError(error, 'fetch user list'); @@ -840,7 +836,7 @@ export async function getUserList(): Promise<{ users: UserInfo[] }> { export async function makeUserAdmin(username: string): Promise { try { - const response = await authApi.post('/make-admin', { username }); + const response = await authApi.post('/users/make-admin', { username }); return response.data; } catch (error) { handleApiError(error, 'make user admin'); @@ -849,7 +845,7 @@ export async function makeUserAdmin(username: string): Promise { export async function removeAdminStatus(username: string): Promise { try { - const response = await authApi.post('/remove-admin', { username }); + const response = await authApi.post('/users/remove-admin', { username }); return response.data; } catch (error) { handleApiError(error, 'remove admin status'); @@ -858,7 +854,7 @@ export async function removeAdminStatus(username: string): Promise { export async function deleteUser(username: string): Promise { try { - const response = await authApi.delete('/delete-user', { data: { username } }); + const response = await authApi.delete('/users/delete-user', { data: { username } }); return response.data; } catch (error) { handleApiError(error, 'delete user'); @@ -867,7 +863,7 @@ export async function deleteUser(username: string): Promise { export async function deleteAccount(password: string): Promise { try { - const response = await authApi.delete('/delete-account', { data: { password } }); + const response = await authApi.delete('/users/delete-account', { data: { password } }); return response.data; } catch (error) { handleApiError(error, 'delete account'); @@ -876,7 +872,7 @@ export async function deleteAccount(password: string): Promise { export async function updateRegistrationAllowed(allowed: boolean): Promise { try { - const response = await authApi.patch('/registration-allowed', { allowed }); + const response = await authApi.patch('/users/registration-allowed', { allowed }); return response.data; } catch (error) { handleApiError(error, 'update registration allowed'); @@ -885,7 +881,7 @@ export async function updateRegistrationAllowed(allowed: boolean): Promise export async function updateOIDCConfig(config: any): Promise { try { - const response = await authApi.post('/oidc-config', config); + const response = await authApi.post('/users/oidc-config', config); return response.data; } catch (error) { handleApiError(error, 'update OIDC config'); @@ -942,7 +938,7 @@ export async function getVersionInfo(): Promise { export async function getDatabaseHealth(): Promise { try { - const response = await authApi.get('/db-health'); + const response = await authApi.get('/users/db-health'); return response.data; } catch (error) { handleApiError(error, 'check database health'); From d88c890ba7d170571d1427d8bd75cf335c74e618 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Wed, 27 Aug 2025 11:34:38 -0500 Subject: [PATCH 17/27] Reduce automatic pinging --- src/backend/ssh/server-stats.ts | 7 ++++--- src/ui/Navigation/LeftSidebar.tsx | 2 +- src/ui/apps/Server/Server.tsx | 34 +++++++++++++++++++++++++------ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/backend/ssh/server-stats.ts b/src/backend/ssh/server-stats.ts index 2fd66bd1..7f79dee3 100644 --- a/src/backend/ssh/server-stats.ts +++ b/src/backend/ssh/server-stats.ts @@ -415,7 +415,8 @@ app.listen(PORT, async () => { } }); -setInterval(() => { - pollStatusesOnce().catch(err => logger.error('Background poll failed', err)); -}, 60_000); +// Disable automatic background polling to prevent log flooding +// setInterval(() => { +// pollStatusesOnce().catch(err => logger.error('Background poll failed', err)); +// }, 60_000); diff --git a/src/ui/Navigation/LeftSidebar.tsx b/src/ui/Navigation/LeftSidebar.tsx index d75f182a..f60383a9 100644 --- a/src/ui/Navigation/LeftSidebar.tsx +++ b/src/ui/Navigation/LeftSidebar.tsx @@ -238,7 +238,7 @@ export function LeftSidebar({ React.useEffect(() => { fetchHosts(); - const interval = setInterval(fetchHosts, 10000); + const interval = setInterval(fetchHosts, 300000); // 5 minutes instead of 10 seconds return () => clearInterval(interval); }, [fetchHosts]); diff --git a/src/ui/apps/Server/Server.tsx b/src/ui/apps/Server/Server.tsx index be6a4470..3af2a663 100644 --- a/src/ui/apps/Server/Server.tsx +++ b/src/ui/apps/Server/Server.tsx @@ -94,20 +94,23 @@ export function Server({ } }; - if (currentHostConfig?.id) { + if (currentHostConfig?.id && isVisible) { fetchStatus(); fetchMetrics(); + // Only poll when component is visible to reduce unnecessary connections intervalId = window.setInterval(() => { - fetchStatus(); - fetchMetrics(); - }, 10_000); + if (isVisible) { + fetchStatus(); + fetchMetrics(); + } + }, 300_000); // 5 minutes instead of 10 seconds } return () => { cancelled = true; if (intervalId) window.clearInterval(intervalId); }; - }, [currentHostConfig?.id]); + }, [currentHostConfig?.id, isVisible]); const topMarginPx = isTopbarOpen ? 74 : 16; const leftMarginPx = sidebarState === 'collapsed' ? 16 : 8; @@ -142,7 +145,26 @@ export function Server({ -
+
+ {currentHostConfig?.enableFileManager && ( -
+
-
+
-
+
- )} - + + - + )} {dbError && ( @@ -686,4 +686,4 @@ export function HomepageAuth({ )} ); -} +} \ No newline at end of file diff --git a/src/ui/Homepage/HompageUpdateLog.tsx b/src/ui/Homepage/HompageUpdateLog.tsx index 908b1ae6..ad85aafd 100644 --- a/src/ui/Homepage/HompageUpdateLog.tsx +++ b/src/ui/Homepage/HompageUpdateLog.tsx @@ -89,70 +89,63 @@ export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) { }; return ( -
+
-

Updates & Releases

+

Updates & Releases

- + {versionInfo && versionInfo.status === 'requires_update' && ( - - Update Available - + + Update Available + A new version ({versionInfo.version}) is available. - )}
{versionInfo && versionInfo.status === 'requires_update' && ( - + )} -
+
{loading && (
-
+
)} {error && ( - - Error - {error} + + Error + {error} )} {releases?.items.map((release) => (
window.open(release.link, '_blank')} >
-

+

{release.title}

{release.isPrerelease && ( + className="text-xs bg-yellow-600 text-yellow-100 px-2 py-1 rounded ml-2 flex-shrink-0 font-medium"> Pre-release )}
-

+

{formatDescription(release.description)}

-
+
{new Date(release.pubDate).toLocaleDateString()} {release.assets.length > 0 && ( <> @@ -165,9 +158,9 @@ export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) { ))} {releases && releases.items.length === 0 && !loading && ( - - No Releases - + + No Releases + No releases found. From 487919cedc0f00c98f59dd571f9170c9b26f5b2f Mon Sep 17 00:00:00 2001 From: LukeGus Date: Wed, 27 Aug 2025 22:17:28 -0500 Subject: [PATCH 21/27] Improve File Manger UI scaling, fix file manager disconnect, disable more than one file manager at a time. --- docker/nginx.conf | 2 - src/backend/ssh/file-manager.ts | 137 +++----- src/backend/ssh/server-stats.ts | 35 +- src/backend/ssh/terminal.ts | 28 +- src/backend/ssh/tunnel.ts | 323 ++---------------- src/ui/Navigation/AppView.tsx | 20 +- src/ui/Navigation/Tabs/TabContext.tsx | 2 +- src/ui/apps/File Manager/FileManager.tsx | 68 ++-- .../File Manager/FileManagerOperations.tsx | 229 ++++++++----- .../apps/File Manager/FileManagerTabList.tsx | 2 +- .../Host Manager/HostManagerHostViewer.tsx | 2 +- src/ui/apps/Server/Server.tsx | 15 +- .../{TerminalComponent.tsx => Terminal.tsx} | 2 +- src/ui/main-axios.ts | 15 +- 14 files changed, 323 insertions(+), 557 deletions(-) rename src/ui/apps/Terminal/{TerminalComponent.tsx => Terminal.tsx} (99%) diff --git a/docker/nginx.conf b/docker/nginx.conf index fe530ac4..1ffb8fa5 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -85,7 +85,6 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } - # File manager recent, pinned, shortcuts (handled by SSH service) location /ssh/file_manager/recent { proxy_pass http://127.0.0.1:8081; proxy_http_version 1.1; @@ -113,7 +112,6 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } - # SSH file manager operations (handled by file manager service) location /ssh/file_manager/ssh/ { proxy_pass http://127.0.0.1:8084; proxy_http_version 1.1; diff --git a/src/backend/ssh/file-manager.ts b/src/backend/ssh/file-manager.ts index 5f24f1f8..ee8bcc9e 100644 --- a/src/backend/ssh/file-manager.ts +++ b/src/backend/ssh/file-manager.ts @@ -48,7 +48,6 @@ interface SSHSession { } const sshSessions: Record = {}; -const SESSION_TIMEOUT_MS = 10 * 60 * 1000; function cleanupSession(sessionId: string) { const session = sshSessions[sessionId]; @@ -66,25 +65,26 @@ function scheduleSessionCleanup(sessionId: string) { const session = sshSessions[sessionId]; if (session) { if (session.timeout) clearTimeout(session.timeout); - session.timeout = setTimeout(() => cleanupSession(sessionId), SESSION_TIMEOUT_MS); } } -app.post('/ssh/file_manager/ssh/connect', (req, res) => { +app.post('/ssh/file_manager/ssh/connect', async (req, res) => { const {sessionId, ip, port, username, password, sshKey, keyPassword} = req.body; if (!sessionId || !ip || !username || !port) { return res.status(400).json({error: 'Missing SSH connection parameters'}); } - if (sshSessions[sessionId]?.isConnected) cleanupSession(sessionId); + if (sshSessions[sessionId]?.isConnected) { + cleanupSession(sessionId); + } const client = new SSHClient(); const config: any = { host: ip, port: port || 22, username, - readyTimeout: 20000, - keepaliveInterval: 10000, - keepaliveCountMax: 3, + readyTimeout: 0, + keepaliveInterval: 30000, + keepaliveCountMax: 0, algorithms: { kex: [ 'diffie-hellman-group14-sha256', @@ -122,8 +122,22 @@ app.post('/ssh/file_manager/ssh/connect', (req, res) => { }; if (sshKey && sshKey.trim()) { - config.privateKey = sshKey; - if (keyPassword) config.passphrase = keyPassword; + try { + if (!sshKey.includes('-----BEGIN') || !sshKey.includes('-----END')) { + throw new Error('Invalid private key format'); + } + + const cleanKey = sshKey.trim().replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + + config.privateKey = Buffer.from(cleanKey, 'utf8'); + + if (keyPassword) config.passphrase = keyPassword; + + logger.info('SSH key authentication configured successfully for file manager'); + } catch (keyError) { + logger.error('SSH key format error: ' + keyError.message); + return res.status(400).json({error: 'Invalid SSH key format'}); + } } else if (password && password.trim()) { config.password = password; } else { @@ -136,7 +150,6 @@ app.post('/ssh/file_manager/ssh/connect', (req, res) => { if (responseSent) return; responseSent = true; sshSessions[sessionId] = {client, isConnected: true, lastActive: Date.now()}; - scheduleSessionCleanup(sessionId); res.json({status: 'success', message: 'SSH connection established'}); }); @@ -181,7 +194,7 @@ app.get('/ssh/file_manager/ssh/listFiles', (req, res) => { } sshConn.lastActive = Date.now(); - scheduleSessionCleanup(sessionId); + const escapedPath = sshPath.replace(/'/g, "'\"'\"'"); sshConn.client.exec(`ls -la '${escapedPath}'`, (err, stream) => { @@ -251,7 +264,7 @@ app.get('/ssh/file_manager/ssh/readFile', (req, res) => { } sshConn.lastActive = Date.now(); - scheduleSessionCleanup(sessionId); + const escapedPath = filePath.replace(/'/g, "'\"'\"'"); sshConn.client.exec(`cat '${escapedPath}'`, (err, stream) => { @@ -303,14 +316,6 @@ app.post('/ssh/file_manager/ssh/writeFile', (req, res) => { } sshConn.lastActive = Date.now(); - scheduleSessionCleanup(sessionId); - - const commandTimeout = setTimeout(() => { - logger.error(`SSH writeFile command timed out for session: ${sessionId}`); - if (!res.headersSent) { - res.status(500).json({error: 'SSH command timed out'}); - } - }, 60000); const trySFTP = () => { try { @@ -331,7 +336,6 @@ app.post('/ssh/file_manager/ssh/writeFile', (req, res) => { fileBuffer = Buffer.from(content); } } catch (bufferErr) { - clearTimeout(commandTimeout); logger.error('Buffer conversion error:', bufferErr); if (!res.headersSent) { return res.status(500).json({error: 'Invalid file content format'}); @@ -354,7 +358,6 @@ app.post('/ssh/file_manager/ssh/writeFile', (req, res) => { writeStream.on('finish', () => { if (hasError || hasFinished) return; hasFinished = true; - clearTimeout(commandTimeout); logger.success(`File written successfully via SFTP: ${filePath}`); if (!res.headersSent) { res.json({message: 'File written successfully', path: filePath}); @@ -364,7 +367,6 @@ app.post('/ssh/file_manager/ssh/writeFile', (req, res) => { writeStream.on('close', () => { if (hasError || hasFinished) return; hasFinished = true; - clearTimeout(commandTimeout); logger.success(`File written successfully via SFTP: ${filePath}`); if (!res.headersSent) { res.json({message: 'File written successfully', path: filePath}); @@ -396,7 +398,7 @@ app.post('/ssh/file_manager/ssh/writeFile', (req, res) => { sshConn.client.exec(writeCommand, (err, stream) => { if (err) { - clearTimeout(commandTimeout); + logger.error('Fallback write command failed:', err); if (!res.headersSent) { return res.status(500).json({error: `Write failed: ${err.message}`}); @@ -416,7 +418,7 @@ app.post('/ssh/file_manager/ssh/writeFile', (req, res) => { }); stream.on('close', (code) => { - clearTimeout(commandTimeout); + if (outputData.includes('SUCCESS')) { logger.success(`File written successfully via fallback: ${filePath}`); @@ -432,7 +434,7 @@ app.post('/ssh/file_manager/ssh/writeFile', (req, res) => { }); stream.on('error', (streamErr) => { - clearTimeout(commandTimeout); + logger.error('Fallback write stream error:', streamErr); if (!res.headersSent) { res.status(500).json({error: `Write stream error: ${streamErr.message}`}); @@ -440,7 +442,7 @@ app.post('/ssh/file_manager/ssh/writeFile', (req, res) => { }); }); } catch (fallbackErr) { - clearTimeout(commandTimeout); + logger.error('Fallback method failed:', fallbackErr); if (!res.headersSent) { res.status(500).json({error: `All write methods failed: ${fallbackErr.message}`}); @@ -468,16 +470,11 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { } sshConn.lastActive = Date.now(); - scheduleSessionCleanup(sessionId); + const fullPath = filePath.endsWith('/') ? filePath + fileName : filePath + '/' + fileName; - const commandTimeout = setTimeout(() => { - logger.error(`SSH uploadFile command timed out for session: ${sessionId}`); - if (!res.headersSent) { - res.status(500).json({error: 'SSH command timed out'}); - } - }, 60000); + const trySFTP = () => { try { @@ -498,7 +495,7 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { fileBuffer = Buffer.from(content); } } catch (bufferErr) { - clearTimeout(commandTimeout); + logger.error('Buffer conversion error:', bufferErr); if (!res.headersSent) { return res.status(500).json({error: 'Invalid file content format'}); @@ -521,7 +518,7 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { writeStream.on('finish', () => { if (hasError || hasFinished) return; hasFinished = true; - clearTimeout(commandTimeout); + logger.success(`File uploaded successfully via SFTP: ${fullPath}`); if (!res.headersSent) { res.json({message: 'File uploaded successfully', path: fullPath}); @@ -531,7 +528,7 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { writeStream.on('close', () => { if (hasError || hasFinished) return; hasFinished = true; - clearTimeout(commandTimeout); + logger.success(`File uploaded successfully via SFTP: ${fullPath}`); if (!res.headersSent) { res.json({message: 'File uploaded successfully', path: fullPath}); @@ -573,7 +570,7 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { sshConn.client.exec(writeCommand, (err, stream) => { if (err) { - clearTimeout(commandTimeout); + logger.error('Fallback upload command failed:', err); if (!res.headersSent) { return res.status(500).json({error: `Upload failed: ${err.message}`}); @@ -593,7 +590,7 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { }); stream.on('close', (code) => { - clearTimeout(commandTimeout); + if (outputData.includes('SUCCESS')) { logger.success(`File uploaded successfully via fallback: ${fullPath}`); @@ -609,7 +606,7 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { }); stream.on('error', (streamErr) => { - clearTimeout(commandTimeout); + logger.error('Fallback upload stream error:', streamErr); if (!res.headersSent) { res.status(500).json({error: `Upload stream error: ${streamErr.message}`}); @@ -631,7 +628,7 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { sshConn.client.exec(writeCommand, (err, stream) => { if (err) { - clearTimeout(commandTimeout); + logger.error('Chunked fallback upload failed:', err); if (!res.headersSent) { return res.status(500).json({error: `Chunked upload failed: ${err.message}`}); @@ -651,7 +648,7 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { }); stream.on('close', (code) => { - clearTimeout(commandTimeout); + if (outputData.includes('SUCCESS')) { logger.success(`File uploaded successfully via chunked fallback: ${fullPath}`); @@ -667,7 +664,6 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { }); stream.on('error', (streamErr) => { - clearTimeout(commandTimeout); logger.error('Chunked fallback upload stream error:', streamErr); if (!res.headersSent) { res.status(500).json({error: `Chunked upload stream error: ${streamErr.message}`}); @@ -676,7 +672,6 @@ app.post('/ssh/file_manager/ssh/uploadFile', (req, res) => { }); } } catch (fallbackErr) { - clearTimeout(commandTimeout); logger.error('Fallback method failed:', fallbackErr); if (!res.headersSent) { res.status(500).json({error: `All upload methods failed: ${fallbackErr.message}`}); @@ -704,23 +699,14 @@ app.post('/ssh/file_manager/ssh/createFile', (req, res) => { } sshConn.lastActive = Date.now(); - scheduleSessionCleanup(sessionId); const fullPath = filePath.endsWith('/') ? filePath + fileName : filePath + '/' + fileName; const escapedPath = fullPath.replace(/'/g, "'\"'\"'"); - const commandTimeout = setTimeout(() => { - logger.error(`SSH createFile command timed out for session: ${sessionId}`); - if (!res.headersSent) { - res.status(500).json({error: 'SSH command timed out'}); - } - }, 15000); - const createCommand = `touch '${escapedPath}' && echo "SUCCESS" && exit 0`; sshConn.client.exec(createCommand, (err, stream) => { if (err) { - clearTimeout(commandTimeout); logger.error('SSH createFile error:', err); if (!res.headersSent) { return res.status(500).json({error: err.message}); @@ -739,7 +725,6 @@ app.post('/ssh/file_manager/ssh/createFile', (req, res) => { errorData += chunk.toString(); if (chunk.toString().includes('Permission denied')) { - clearTimeout(commandTimeout); logger.error(`Permission denied creating file: ${fullPath}`); if (!res.headersSent) { return res.status(403).json({ @@ -751,8 +736,6 @@ app.post('/ssh/file_manager/ssh/createFile', (req, res) => { }); stream.on('close', (code) => { - clearTimeout(commandTimeout); - if (outputData.includes('SUCCESS')) { if (!res.headersSent) { res.json({message: 'File created successfully', path: fullPath}); @@ -774,7 +757,6 @@ app.post('/ssh/file_manager/ssh/createFile', (req, res) => { }); stream.on('error', (streamErr) => { - clearTimeout(commandTimeout); logger.error('SSH createFile stream error:', streamErr); if (!res.headersSent) { res.status(500).json({error: `Stream error: ${streamErr.message}`}); @@ -800,23 +782,15 @@ app.post('/ssh/file_manager/ssh/createFolder', (req, res) => { } sshConn.lastActive = Date.now(); - scheduleSessionCleanup(sessionId); const fullPath = folderPath.endsWith('/') ? folderPath + folderName : folderPath + '/' + folderName; const escapedPath = fullPath.replace(/'/g, "'\"'\"'"); - const commandTimeout = setTimeout(() => { - logger.error(`SSH createFolder command timed out for session: ${sessionId}`); - if (!res.headersSent) { - res.status(500).json({error: 'SSH command timed out'}); - } - }, 15000); - const createCommand = `mkdir -p '${escapedPath}' && echo "SUCCESS" && exit 0`; sshConn.client.exec(createCommand, (err, stream) => { if (err) { - clearTimeout(commandTimeout); + logger.error('SSH createFolder error:', err); if (!res.headersSent) { return res.status(500).json({error: err.message}); @@ -835,7 +809,6 @@ app.post('/ssh/file_manager/ssh/createFolder', (req, res) => { errorData += chunk.toString(); if (chunk.toString().includes('Permission denied')) { - clearTimeout(commandTimeout); logger.error(`Permission denied creating folder: ${fullPath}`); if (!res.headersSent) { return res.status(403).json({ @@ -847,8 +820,6 @@ app.post('/ssh/file_manager/ssh/createFolder', (req, res) => { }); stream.on('close', (code) => { - clearTimeout(commandTimeout); - if (outputData.includes('SUCCESS')) { if (!res.headersSent) { res.json({message: 'Folder created successfully', path: fullPath}); @@ -870,7 +841,6 @@ app.post('/ssh/file_manager/ssh/createFolder', (req, res) => { }); stream.on('error', (streamErr) => { - clearTimeout(commandTimeout); logger.error('SSH createFolder stream error:', streamErr); if (!res.headersSent) { res.status(500).json({error: `Stream error: ${streamErr.message}`}); @@ -896,24 +866,14 @@ app.delete('/ssh/file_manager/ssh/deleteItem', (req, res) => { } sshConn.lastActive = Date.now(); - scheduleSessionCleanup(sessionId); - const escapedPath = itemPath.replace(/'/g, "'\"'\"'"); - const commandTimeout = setTimeout(() => { - logger.error(`SSH deleteItem command timed out for session: ${sessionId}`); - if (!res.headersSent) { - res.status(500).json({error: 'SSH command timed out'}); - } - }, 15000); - const deleteCommand = isDirectory ? `rm -rf '${escapedPath}' && echo "SUCCESS" && exit 0` : `rm -f '${escapedPath}' && echo "SUCCESS" && exit 0`; sshConn.client.exec(deleteCommand, (err, stream) => { if (err) { - clearTimeout(commandTimeout); logger.error('SSH deleteItem error:', err); if (!res.headersSent) { return res.status(500).json({error: err.message}); @@ -932,7 +892,6 @@ app.delete('/ssh/file_manager/ssh/deleteItem', (req, res) => { errorData += chunk.toString(); if (chunk.toString().includes('Permission denied')) { - clearTimeout(commandTimeout); logger.error(`Permission denied deleting: ${itemPath}`); if (!res.headersSent) { return res.status(403).json({ @@ -944,8 +903,6 @@ app.delete('/ssh/file_manager/ssh/deleteItem', (req, res) => { }); stream.on('close', (code) => { - clearTimeout(commandTimeout); - if (outputData.includes('SUCCESS')) { if (!res.headersSent) { res.json({message: 'Item deleted successfully', path: itemPath}); @@ -967,7 +924,6 @@ app.delete('/ssh/file_manager/ssh/deleteItem', (req, res) => { }); stream.on('error', (streamErr) => { - clearTimeout(commandTimeout); logger.error('SSH deleteItem stream error:', streamErr); if (!res.headersSent) { res.status(500).json({error: `Stream error: ${streamErr.message}`}); @@ -993,25 +949,16 @@ app.put('/ssh/file_manager/ssh/renameItem', (req, res) => { } sshConn.lastActive = Date.now(); - scheduleSessionCleanup(sessionId); const oldDir = oldPath.substring(0, oldPath.lastIndexOf('/') + 1); const newPath = oldDir + newName; const escapedOldPath = oldPath.replace(/'/g, "'\"'\"'"); const escapedNewPath = newPath.replace(/'/g, "'\"'\"'"); - const commandTimeout = setTimeout(() => { - logger.error(`SSH renameItem command timed out for session: ${sessionId}`); - if (!res.headersSent) { - res.status(500).json({error: 'SSH command timed out'}); - } - }, 15000); - const renameCommand = `mv '${escapedOldPath}' '${escapedNewPath}' && echo "SUCCESS" && exit 0`; sshConn.client.exec(renameCommand, (err, stream) => { if (err) { - clearTimeout(commandTimeout); logger.error('SSH renameItem error:', err); if (!res.headersSent) { return res.status(500).json({error: err.message}); @@ -1030,7 +977,6 @@ app.put('/ssh/file_manager/ssh/renameItem', (req, res) => { errorData += chunk.toString(); if (chunk.toString().includes('Permission denied')) { - clearTimeout(commandTimeout); logger.error(`Permission denied renaming: ${oldPath}`); if (!res.headersSent) { return res.status(403).json({ @@ -1042,8 +988,6 @@ app.put('/ssh/file_manager/ssh/renameItem', (req, res) => { }); stream.on('close', (code) => { - clearTimeout(commandTimeout); - if (outputData.includes('SUCCESS')) { if (!res.headersSent) { res.json({message: 'Item renamed successfully', oldPath, newPath}); @@ -1065,7 +1009,6 @@ app.put('/ssh/file_manager/ssh/renameItem', (req, res) => { }); stream.on('error', (streamErr) => { - clearTimeout(commandTimeout); logger.error('SSH renameItem stream error:', streamErr); if (!res.headersSent) { res.status(500).json({error: `Stream error: ${streamErr.message}`}); diff --git a/src/backend/ssh/server-stats.ts b/src/backend/ssh/server-stats.ts index 7f79dee3..e4cca4ad 100644 --- a/src/backend/ssh/server-stats.ts +++ b/src/backend/ssh/server-stats.ts @@ -115,10 +115,29 @@ function buildSshConfig(host: HostRecord): ConnectConfig { (base as any).password = host.password || ''; } else if (host.authType === 'key') { if (host.key) { - (base as any).privateKey = Buffer.from(host.key, 'utf8'); - } - if (host.keyPassword) { - (base as any).passphrase = host.keyPassword; + try { + if (!host.key.includes('-----BEGIN') || !host.key.includes('-----END')) { + throw new Error('Invalid private key format'); + } + + const cleanKey = host.key.trim().replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + + (base as any).privateKey = Buffer.from(cleanKey, 'utf8'); + + if (host.keyPassword) { + (base as any).passphrase = host.keyPassword; + } + + logger.info(`SSH key authentication configured for host ${host.ip}`); + } catch (keyError) { + logger.error(`SSH key format error for host ${host.ip}: ${keyError.message}`); + if (host.password) { + (base as any).password = host.password; + logger.info(`Falling back to password authentication for host ${host.ip}`); + } else { + throw new Error(`Invalid SSH key format for host ${host.ip}`); + } + } } } return base; @@ -413,10 +432,4 @@ app.listen(PORT, async () => { } catch (err) { logger.error('Initial poll failed', err); } -}); - -// Disable automatic background polling to prevent log flooding -// setInterval(() => { -// pollStatusesOnce().catch(err => logger.error('Background poll failed', err)); -// }, 60_000); - +}); \ No newline at end of file diff --git a/src/backend/ssh/terminal.ts b/src/backend/ssh/terminal.ts index 4646528c..e962df4e 100644 --- a/src/backend/ssh/terminal.ts +++ b/src/backend/ssh/terminal.ts @@ -294,12 +294,28 @@ wss.on('connection', (ws: WebSocket) => { } }; if (authType === 'key' && key) { - connectConfig.privateKey = key; - if (keyPassword) { - connectConfig.passphrase = keyPassword; - } - if (keyType && keyType !== 'auto') { - connectConfig.privateKeyType = keyType; + try { + if (!key.includes('-----BEGIN') || !key.includes('-----END')) { + throw new Error('Invalid private key format'); + } + + const cleanKey = key.trim().replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + + connectConfig.privateKey = Buffer.from(cleanKey, 'utf8'); + + if (keyPassword) { + connectConfig.passphrase = keyPassword; + } + + if (keyType && keyType !== 'auto') { + connectConfig.privateKeyType = keyType; + } + + logger.info('SSH key authentication configured successfully'); + } catch (keyError) { + logger.error('SSH key format error: ' + keyError.message); + ws.send(JSON.stringify({type: 'error', message: 'SSH key format error: Invalid private key format'})); + return; } } else if (authType === 'key') { logger.error('SSH key authentication requested but no key provided'); diff --git a/src/backend/ssh/tunnel.ts b/src/backend/ssh/tunnel.ts index 6530c921..d27e41b1 100644 --- a/src/backend/ssh/tunnel.ts +++ b/src/backend/ssh/tunnel.ts @@ -438,264 +438,13 @@ function handleDisconnect(tunnelName: string, tunnelConfig: TunnelConfig | null, } function verifyTunnelConnection(tunnelName: string, tunnelConfig: TunnelConfig, isPeriodic = false): void { - if (manualDisconnects.has(tunnelName) || !activeTunnels.has(tunnelName)) { - return; - } - - if (tunnelVerifications.has(tunnelName)) { - return; - } - - const conn = activeTunnels.get(tunnelName); - if (!conn) return; - broadcastTunnelStatus(tunnelName, { - connected: false, - status: CONNECTION_STATES.VERIFYING + connected: true, + status: CONNECTION_STATES.CONNECTED }); - - const verificationConn = new Client(); - tunnelVerifications.set(tunnelName, { - conn: verificationConn, - timeout: setTimeout(() => { - logger.error(`Verification timeout for '${tunnelName}'`); - cleanupVerification(false, "Verification timeout"); - }, 10000) - }); - - function cleanupVerification(isSuccessful: boolean, failureReason = "Unknown verification failure") { - const verification = tunnelVerifications.get(tunnelName); - if (verification) { - clearTimeout(verification.timeout); - try { - verification.conn.end(); - } catch (e) { - } - tunnelVerifications.delete(tunnelName); - } - - if (isSuccessful) { - broadcastTunnelStatus(tunnelName, { - connected: true, - status: CONNECTION_STATES.CONNECTED - }); - - if (!isPeriodic) { - setupPingInterval(tunnelName, tunnelConfig); - } - } else { - logger.warn(`Verification failed for '${tunnelName}': ${failureReason}`); - - if (failureReason.includes('command failed') || failureReason.includes('connection error') || failureReason.includes('timeout')) { - if (!manualDisconnects.has(tunnelName)) { - broadcastTunnelStatus(tunnelName, { - connected: false, - status: CONNECTION_STATES.FAILED, - reason: failureReason - }); - } - activeTunnels.delete(tunnelName); - handleDisconnect(tunnelName, tunnelConfig, !manualDisconnects.has(tunnelName)); - } else { - logger.info(`Assuming tunnel '${tunnelName}' is working despite verification warning: ${failureReason}`); - cleanupVerification(true); - } - } - } - - function attemptVerification() { - const testCmd = `timeout 3 bash -c 'nc -z ${tunnelConfig.endpointIP} ${tunnelConfig.endpointPort}'`; - - verificationConn.exec(testCmd, (err, stream) => { - if (err) { - logger.error(`Verification command failed for '${tunnelName}': ${err.message}`); - cleanupVerification(false, `Verification command failed: ${err.message}`); - return; - } - - let output = ''; - let errorOutput = ''; - - stream.on('data', (data: Buffer) => { - output += data.toString(); - }); - - stream.stderr?.on('data', (data: Buffer) => { - errorOutput += data.toString(); - }); - - stream.on('close', (code: number) => { - if (code === 0) { - cleanupVerification(true); - } else { - const isTimeout = errorOutput.includes('timeout') || errorOutput.includes('Connection timed out'); - const isConnectionRefused = errorOutput.includes('Connection refused') || errorOutput.includes('No route to host'); - - let failureReason = `Cannot connect to ${tunnelConfig.endpointIP}:${tunnelConfig.endpointPort}`; - if (isTimeout) { - failureReason = `Tunnel verification timeout - cannot reach ${tunnelConfig.endpointIP}:${tunnelConfig.endpointPort}`; - } else if (isConnectionRefused) { - failureReason = `Connection refused to ${tunnelConfig.endpointIP}:${tunnelConfig.endpointPort} - tunnel may not be established`; - } - - cleanupVerification(false, failureReason); - } - }); - - stream.on('error', (err: Error) => { - logger.error(`Verification stream error for '${tunnelName}': ${err.message}`); - cleanupVerification(false, `Verification stream error: ${err.message}`); - }); - }); - } - - verificationConn.on('ready', () => { - setTimeout(() => { - attemptVerification(); - }, 2000); - }); - - verificationConn.on('error', (err: Error) => { - cleanupVerification(false, `Verification connection error: ${err.message}`); - }); - - verificationConn.on('close', () => { - if (tunnelVerifications.has(tunnelName)) { - cleanupVerification(false, "Verification connection closed"); - } - }); - - const connOptions: any = { - host: tunnelConfig.sourceIP, - port: tunnelConfig.sourceSSHPort, - username: tunnelConfig.sourceUsername, - readyTimeout: 10000, - keepaliveInterval: 30000, - keepaliveCountMax: 3, - tcpKeepAlive: true, - tcpKeepAliveInitialDelay: 30000, - algorithms: { - kex: [ - 'diffie-hellman-group14-sha256', - 'diffie-hellman-group14-sha1', - 'diffie-hellman-group1-sha1', - 'diffie-hellman-group-exchange-sha256', - 'diffie-hellman-group-exchange-sha1', - 'ecdh-sha2-nistp256', - 'ecdh-sha2-nistp384', - 'ecdh-sha2-nistp521' - ], - cipher: [ - 'aes128-ctr', - 'aes192-ctr', - 'aes256-ctr', - 'aes128-gcm@openssh.com', - 'aes256-gcm@openssh.com', - 'aes128-cbc', - 'aes192-cbc', - 'aes256-cbc', - '3des-cbc' - ], - hmac: [ - 'hmac-sha2-256', - 'hmac-sha2-512', - 'hmac-sha1', - 'hmac-md5' - ], - compress: [ - 'none', - 'zlib@openssh.com', - 'zlib' - ] - } - }; - - if (tunnelConfig.sourceAuthMethod === "key" && tunnelConfig.sourceSSHKey) { - connOptions.privateKey = tunnelConfig.sourceSSHKey; - if (tunnelConfig.sourceKeyPassword) { - connOptions.passphrase = tunnelConfig.sourceKeyPassword; - } - if (tunnelConfig.sourceKeyType && tunnelConfig.sourceKeyType !== 'auto') { - connOptions.privateKeyType = tunnelConfig.sourceKeyType; - } - } else if (tunnelConfig.sourceAuthMethod === "key") { - logger.error(`SSH key authentication requested but no key provided for tunnel '${tunnelName}'`); - broadcastTunnelStatus(tunnelName, { - connected: false, - status: CONNECTION_STATES.FAILED, - reason: "SSH key authentication requested but no key provided" - }); - return; - } else { - connOptions.password = tunnelConfig.sourcePassword; - } - - verificationConn.connect(connOptions); } function setupPingInterval(tunnelName: string, tunnelConfig: TunnelConfig): void { - const pingInterval = setInterval(() => { - if (!activeTunnels.has(tunnelName) || manualDisconnects.has(tunnelName)) { - clearInterval(pingInterval); - return; - } - - const conn = activeTunnels.get(tunnelName); - if (!conn) { - clearInterval(pingInterval); - return; - } - - conn.exec('echo "ping"', (err, stream) => { - if (err) { - clearInterval(pingInterval); - - if (!manualDisconnects.has(tunnelName)) { - broadcastTunnelStatus(tunnelName, { - connected: false, - status: CONNECTION_STATES.UNSTABLE, - reason: "Ping failed" - }); - } - - activeTunnels.delete(tunnelName); - handleDisconnect(tunnelName, tunnelConfig, !manualDisconnects.has(tunnelName)); - return; - } - - stream.on('close', (code: number) => { - if (code !== 0) { - clearInterval(pingInterval); - - if (!manualDisconnects.has(tunnelName)) { - broadcastTunnelStatus(tunnelName, { - connected: false, - status: CONNECTION_STATES.UNSTABLE, - reason: "Ping command failed" - }); - } - - activeTunnels.delete(tunnelName); - handleDisconnect(tunnelName, tunnelConfig, !manualDisconnects.has(tunnelName)); - } - }); - - stream.on('error', (err: Error) => { - clearInterval(pingInterval); - - if (!manualDisconnects.has(tunnelName)) { - broadcastTunnelStatus(tunnelName, { - connected: false, - status: CONNECTION_STATES.UNSTABLE, - reason: "Ping stream error" - }); - } - - activeTunnels.delete(tunnelName); - handleDisconnect(tunnelName, tunnelConfig, !manualDisconnects.has(tunnelName)); - }); - }); - }, 60000); } function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void { @@ -751,7 +500,7 @@ function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void { handleDisconnect(tunnelName, tunnelConfig, !manualDisconnects.has(tunnelName)); } } - }, 15000); + }, 60000); conn.on("error", (err) => { clearTimeout(connectionTimeout); @@ -910,9 +659,9 @@ function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void { host: tunnelConfig.sourceIP, port: tunnelConfig.sourceSSHPort, username: tunnelConfig.sourceUsername, - keepaliveInterval: 30000, - keepaliveCountMax: 3, - readyTimeout: 10000, + keepaliveInterval: 60000, + keepaliveCountMax: 0, + readyTimeout: 60000, tcpKeepAlive: true, tcpKeepAliveInitialDelay: 30000, algorithms: { @@ -952,8 +701,8 @@ function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void { }; if (tunnelConfig.sourceAuthMethod === "key" && tunnelConfig.sourceSSHKey) { - if (!tunnelConfig.sourceSSHKey.includes('-----BEGIN')) { - logger.error(`Invalid SSH key format for tunnel '${tunnelName}'. Key should start with '-----BEGIN'`); + if (!tunnelConfig.sourceSSHKey.includes('-----BEGIN') || !tunnelConfig.sourceSSHKey.includes('-----END')) { + logger.error(`Invalid SSH key format for tunnel '${tunnelName}'. Key should contain both BEGIN and END markers`); broadcastTunnelStatus(tunnelName, { connected: false, status: CONNECTION_STATES.FAILED, @@ -962,7 +711,8 @@ function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void { return; } - connOptions.privateKey = tunnelConfig.sourceSSHKey; + const cleanKey = tunnelConfig.sourceSSHKey.trim().replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + connOptions.privateKey = Buffer.from(cleanKey, 'utf8'); if (tunnelConfig.sourceKeyPassword) { connOptions.passphrase = tunnelConfig.sourceKeyPassword; } @@ -981,43 +731,16 @@ function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void { connOptions.password = tunnelConfig.sourcePassword; } - const testSocket = new net.Socket(); - testSocket.setTimeout(5000); - - testSocket.on('connect', () => { - testSocket.destroy(); - - const currentStatus = connectionStatus.get(tunnelName); - if (!currentStatus || currentStatus.status !== CONNECTION_STATES.WAITING) { - broadcastTunnelStatus(tunnelName, { - connected: false, - status: CONNECTION_STATES.CONNECTING, - retryCount: retryAttempt > 0 ? retryAttempt : undefined - }); - } - - conn.connect(connOptions); - }); - - testSocket.on('timeout', () => { - testSocket.destroy(); + const currentStatus = connectionStatus.get(tunnelName); + if (!currentStatus || currentStatus.status !== CONNECTION_STATES.WAITING) { broadcastTunnelStatus(tunnelName, { connected: false, - status: CONNECTION_STATES.FAILED, - reason: "Network connectivity test failed - server not reachable" + status: CONNECTION_STATES.CONNECTING, + retryCount: retryAttempt > 0 ? retryAttempt : undefined }); - }); + } - testSocket.on('error', (err: any) => { - testSocket.destroy(); - broadcastTunnelStatus(tunnelName, { - connected: false, - status: CONNECTION_STATES.FAILED, - reason: `Network connectivity test failed - ${err.message}` - }); - }); - - testSocket.connect(tunnelConfig.sourceSSHPort, tunnelConfig.sourceIP); + conn.connect(connOptions); } function killRemoteTunnelByMarker(tunnelConfig: TunnelConfig, tunnelName: string, callback: (err?: Error) => void) { @@ -1027,9 +750,9 @@ function killRemoteTunnelByMarker(tunnelConfig: TunnelConfig, tunnelName: string host: tunnelConfig.sourceIP, port: tunnelConfig.sourceSSHPort, username: tunnelConfig.sourceUsername, - keepaliveInterval: 30000, - keepaliveCountMax: 3, - readyTimeout: 10000, + keepaliveInterval: 60000, + keepaliveCountMax: 0, + readyTimeout: 60000, tcpKeepAlive: true, tcpKeepAliveInitialDelay: 30000, algorithms: { @@ -1068,7 +791,13 @@ function killRemoteTunnelByMarker(tunnelConfig: TunnelConfig, tunnelName: string } }; if (tunnelConfig.sourceAuthMethod === "key" && tunnelConfig.sourceSSHKey) { - connOptions.privateKey = tunnelConfig.sourceSSHKey; + if (!tunnelConfig.sourceSSHKey.includes('-----BEGIN') || !tunnelConfig.sourceSSHKey.includes('-----END')) { + callback(new Error('Invalid SSH key format')); + return; + } + + const cleanKey = tunnelConfig.sourceSSHKey.trim().replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + connOptions.privateKey = Buffer.from(cleanKey, 'utf8'); if (tunnelConfig.sourceKeyPassword) { connOptions.passphrase = tunnelConfig.sourceKeyPassword; } diff --git a/src/ui/Navigation/AppView.tsx b/src/ui/Navigation/AppView.tsx index 006bc269..bd5dc865 100644 --- a/src/ui/Navigation/AppView.tsx +++ b/src/ui/Navigation/AppView.tsx @@ -1,5 +1,5 @@ import React, {useEffect, useRef, useState} from "react"; -import {TerminalComponent} from "@/ui/apps/Terminal/TerminalComponent.tsx"; +import {Terminal} from "@/ui/apps/Terminal/Terminal.tsx"; import {Server as ServerView} from "@/ui/apps/Server/Server.tsx"; import {FileManager} from "@/ui/apps/File Manager/FileManager.tsx"; import {useTabs} from "@/ui/Navigation/Tabs/TabContext.tsx"; @@ -108,12 +108,13 @@ export function AppView({isTopbarOpen = true}: TerminalViewProps): React.ReactEl const layoutTabs = [mainTab, ...splitTabs.filter((t: any) => t && t.id !== (mainTab && (mainTab as any).id))].filter(Boolean) as any[]; if (allSplitScreenTab.length === 0 && mainTab) { + const isFileManagerTab = mainTab.type === 'file_manager'; styles[mainTab.id] = { position: 'absolute', - top: 2, - left: 2, - right: 2, - bottom: 2, + top: isFileManagerTab ? 0 : 2, + left: isFileManagerTab ? 0 : 2, + right: isFileManagerTab ? 0 : 2, + bottom: isFileManagerTab ? 0 : 2, zIndex: 20, display: 'block', pointerEvents: 'auto', @@ -154,9 +155,9 @@ export function AppView({isTopbarOpen = true}: TerminalViewProps): React.ReactEl const effectiveVisible = isVisible && ready; return (
-
+
{t.type === 'terminal' ? ( - tab.id === currentTab); + const isFileManager = currentTabData?.type === 'file_manager'; + const topMarginPx = isTopbarOpen ? 74 : 26; const leftMarginPx = sidebarState === 'collapsed' ? 26 : 8; const bottomMarginPx = 8; @@ -533,7 +537,7 @@ export function AppView({isTopbarOpen = true}: TerminalViewProps): React.ReactEl className="border-2 border-[#303032] rounded-lg overflow-hidden overflow-x-hidden" style={{ position: 'relative', - background: '#18181b', + background: isFileManager ? '#09090b' : '#18181b', marginLeft: leftMarginPx, marginRight: 17, marginTop: topMarginPx, diff --git a/src/ui/Navigation/Tabs/TabContext.tsx b/src/ui/Navigation/Tabs/TabContext.tsx index 22a568ba..5d8104bc 100644 --- a/src/ui/Navigation/Tabs/TabContext.tsx +++ b/src/ui/Navigation/Tabs/TabContext.tsx @@ -50,7 +50,7 @@ export function TabProvider({children}: TabProviderProps) { const usedNumbers = new Set(); let rootUsed = false; tabs.forEach(t => { - if (t.type !== tabType || !t.title) return; + if (!t.title) return; if (t.title === root) { rootUsed = true; return; diff --git a/src/ui/apps/File Manager/FileManager.tsx b/src/ui/apps/File Manager/FileManager.tsx index 94ce24a8..ddb397b9 100644 --- a/src/ui/apps/File Manager/FileManager.tsx +++ b/src/ui/apps/File Manager/FileManager.tsx @@ -8,6 +8,7 @@ import {Button} from '@/components/ui/button.tsx'; import {FIleManagerTopNavbar} from "@/ui/apps/File Manager/FIleManagerTopNavbar.tsx"; import {cn} from '@/lib/utils.ts'; import {Save, RefreshCw, Settings, Trash2} from 'lucide-react'; +import {Separator} from '@/components/ui/separator.tsx'; import {toast} from 'sonner'; import { getFileManagerRecent, @@ -489,7 +490,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null} if (!currentHost) { return ( -
+
{ @@ -525,7 +526,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null} } return ( -
+
{ @@ -570,6 +571,7 @@ export function FileManager({onSelectView, embedded = false, initialHost = null} > +
-
-
- - Current Path: - {currentPath} +
+
+ +
+ Current Path: + {currentPath} +
{showUpload && ( - -
-
-

- - Upload File + +
+
+

+ + Upload File

-

- Maximum file size: 100MB (JSON) / 200MB (Binary) +

+ Max: 100MB (JSON) / 200MB (Binary)

-
-
+
+
{uploadFile ? ( -
- -

{uploadFile.name}

-

+

+ +

{uploadFile.name}

+

{(uploadFile.size / 1024).toFixed(2)} KB

) : ( -
- -

Click to select a file

+
+ +

Click to select a file

@@ -308,11 +338,11 @@ export function FileManagerOperations({ accept="*/*" /> -
+
@@ -320,6 +350,7 @@ export function FileManagerOperations({ variant="outline" onClick={() => setShowUpload(false)} disabled={isLoading} + className="w-full text-sm h-9" > Cancel @@ -329,23 +360,25 @@ export function FileManagerOperations({ )} {showCreateFile && ( - -
-

- - Create New File -

+ +
+
+

+ + Create New File +

+
-
+
-
+
@@ -371,6 +404,7 @@ export function FileManagerOperations({ variant="outline" onClick={() => setShowCreateFile(false)} disabled={isLoading} + className="w-full text-sm h-9" > Cancel @@ -380,23 +414,25 @@ export function FileManagerOperations({ )} {showCreateFolder && ( - -
-

- - Create New Folder -

+ +
+
+

+ + Create New Folder +

+
-
+
-
+
@@ -422,6 +458,7 @@ export function FileManagerOperations({ variant="outline" onClick={() => setShowCreateFolder(false)} disabled={isLoading} + className="w-full text-sm h-9" > Cancel @@ -431,27 +468,29 @@ export function FileManagerOperations({ )} {showDelete && ( - -
-

- - Delete Item -

+ +
+
+

+ + Delete Item +

+
-
+
-
- - Warning: This action cannot be undone +
+ + Warning: This action cannot be undone
@@ -462,30 +501,30 @@ export function FileManagerOperations({ setDeletePath(e.target.value)} - placeholder="Enter full path to item (e.g., /path/to/file.txt)" - className="bg-[#23232a] border-2 border-[#434345] text-white" + placeholder="Enter full path to item" + className="bg-[#23232a] border-2 border-[#434345] text-white text-sm" />
-
+
setDeleteIsDirectory(e.target.checked)} - className="rounded border-[#434345] bg-[#23232a]" + className="rounded border-[#434345] bg-[#23232a] mt-0.5 flex-shrink-0" /> -
-
+
@@ -493,6 +532,7 @@ export function FileManagerOperations({ variant="outline" onClick={() => setShowDelete(false)} disabled={isLoading} + className="w-full text-sm h-9" > Cancel @@ -502,23 +542,25 @@ export function FileManagerOperations({ )} {showRename && ( - -
-

- - Rename Item -

+ +
+
+

+ + Rename Item +

+
-
+
@@ -539,29 +581,29 @@ export function FileManagerOperations({ value={newName} onChange={(e) => setNewName(e.target.value)} placeholder="Enter new name" - className="bg-[#23232a] border-2 border-[#434345] text-white" + className="bg-[#23232a] border-2 border-[#434345] text-white text-sm" onKeyDown={(e) => e.key === 'Enter' && handleRename()} />
-
+
setRenameIsDirectory(e.target.checked)} - className="rounded border-[#434345] bg-[#23232a]" + className="rounded border-[#434345] bg-[#23232a] mt-0.5 flex-shrink-0" /> -
-
+
@@ -569,6 +611,7 @@ export function FileManagerOperations({ variant="outline" onClick={() => setShowRename(false)} disabled={isLoading} + className="w-full text-sm h-9" > Cancel diff --git a/src/ui/apps/File Manager/FileManagerTabList.tsx b/src/ui/apps/File Manager/FileManagerTabList.tsx index b43c1cdc..e46a7e22 100644 --- a/src/ui/apps/File Manager/FileManagerTabList.tsx +++ b/src/ui/apps/File Manager/FileManagerTabList.tsx @@ -21,7 +21,7 @@ export function FileManagerTabList({tabs, activeTab, setActiveTab, closeTab, onH diff --git a/src/ui/apps/Host Manager/HostManagerHostViewer.tsx b/src/ui/apps/Host Manager/HostManagerHostViewer.tsx index 93ee0341..aa4e65ca 100644 --- a/src/ui/apps/Host Manager/HostManagerHostViewer.tsx +++ b/src/ui/apps/Host Manager/HostManagerHostViewer.tsx @@ -677,7 +677,7 @@ EXAMPLE STRUCTURE: {host.tags && host.tags.length > 0 && (
{host.tags.slice(0, 6).map((tag, index) => ( - {tag} diff --git a/src/ui/apps/Server/Server.tsx b/src/ui/apps/Server/Server.tsx index 3af2a663..4235eb2f 100644 --- a/src/ui/apps/Server/Server.tsx +++ b/src/ui/apps/Server/Server.tsx @@ -25,7 +25,7 @@ export function Server({ embedded = false }: ServerProps): React.ReactElement { const {state: sidebarState} = useSidebar(); - const {addTab} = useTabs() as any; + const {addTab, tabs} = useTabs() as any; const [serverStatus, setServerStatus] = React.useState<'online' | 'offline'>('offline'); const [metrics, setMetrics] = React.useState(null); const [currentHostConfig, setCurrentHostConfig] = React.useState(hostConfig); @@ -116,6 +116,15 @@ export function Server({ const leftMarginPx = sidebarState === 'collapsed' ? 16 : 8; const bottomMarginPx = 8; + // Check if a file manager tab for this host is already open + const isFileManagerAlreadyOpen = React.useMemo(() => { + if (!currentHostConfig) return false; + return tabs.some((tab: any) => + tab.type === 'file_manager' && + tab.hostConfig?.id === currentHostConfig.id + ); + }, [tabs, currentHostConfig]); + const wrapperStyle: React.CSSProperties = embedded ? {opacity: isVisible ? 1 : 0, height: '100%', width: '100%'} : { @@ -169,8 +178,10 @@ export function Server({