From b9bd00f86e140bb40eb75f197cd8f7cf22997895 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Mon, 25 Aug 2025 18:00:34 -0500 Subject: [PATCH] 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