diff --git a/admin/src/Layout/Dashboard.tsx b/admin/src/Layout/Dashboard.tsx index fd37bb6..b0f830d 100644 --- a/admin/src/Layout/Dashboard.tsx +++ b/admin/src/Layout/Dashboard.tsx @@ -7,6 +7,7 @@ import UserTable from "../components/UserTable"; import ItemTable from "../components/ItemTable"; import LoanTable from "../components/LoanTable"; import APIKeyTable from "@/components/APIKeyTable"; +import ServerConfig from "@/components/ServerConfig"; import { MoveLeft } from "lucide-react"; type DashboardProps = { @@ -44,6 +45,7 @@ const Dashboard: React.FC = ({ onLogout }) => { viewSchliessfaecher={() => setActiveView("Schließfächer")} viewUser={() => setActiveView("User")} viewAPI={() => setActiveView("API")} + viewConfig={() => setActiveView("Server Konfiguration")} /> = ({ onLogout }) => { {activeView === "Ausleihen" && } {activeView === "Gegenstände" && } {activeView === "API" && } + {activeView === "Server Konfiguration" && } diff --git a/admin/src/Layout/Sidebar.tsx b/admin/src/Layout/Sidebar.tsx index 029ba27..05ee64f 100644 --- a/admin/src/Layout/Sidebar.tsx +++ b/admin/src/Layout/Sidebar.tsx @@ -9,6 +9,7 @@ type SidebarProps = { viewSchliessfaecher: () => void; viewUser: () => void; viewAPI: () => void; + viewConfig: () => void; }; const Sidebar: React.FC = ({ @@ -16,6 +17,7 @@ const Sidebar: React.FC = ({ viewGegenstaende, viewUser, viewAPI, + viewConfig }) => { const [info, setInfo] = useState(null); @@ -83,6 +85,15 @@ const Sidebar: React.FC = ({ > API Keys + + Server Konfiguration + diff --git a/admin/src/components/ServerConfig.tsx b/admin/src/components/ServerConfig.tsx new file mode 100644 index 0000000..d4d8b67 --- /dev/null +++ b/admin/src/components/ServerConfig.tsx @@ -0,0 +1,175 @@ +import React from "react"; +import { + Table, + Spinner, + Text, + VStack, + Heading, + Switch, +} from "@chakra-ui/react"; +import MyAlert from "./myChakra/MyAlert"; +import Cookies from "js-cookie"; +import { useState, useEffect } from "react"; +import { formatDateTime } from "@/utils/userFuncs"; +import { API_BASE } from "@/config/api.config"; + +type Items = { + id: number; + function_name: string; + active: boolean; + entry_created_at: string; + updated_at: string | null; +}; + +const ServerConfig: React.FC = () => { + const [items, setItems] = useState([]); + const [errorStatus, setErrorStatus] = useState<"error" | "success">("error"); + const [errorMessage, setErrorMessage] = useState(""); + const [errorDsc, setErrorDsc] = useState(""); + const [isError, setIsError] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [reload, setReload] = useState(false); + + const handleSwitchChange = async (id: number, newState: boolean) => { + try { + const response = await fetch( + `${API_BASE}/api/admin/server-config/update?functionName=${encodeURIComponent( + items.find((item) => item.id === id)?.function_name || "", + )}&active=${newState}`, + { + method: "POST", + headers: { + Authorization: `Bearer ${Cookies.get("token")}`, + }, + }, + ); + if (response.ok) { + setReload((prev) => !prev); + setError( + "success", + "Status updated", + "The function status was updated successfully.", + ); + } else { + setError( + "error", + "Failed to update status", + "There is an error updating the function status.", + ); + } + } catch (error) { + setError( + "error", + "Failed to update status", + "There is an error updating the function status.", + ); + } + }; + + const setError = ( + status: "error" | "success", + message: string, + description: string, + ) => { + setIsError(false); + setErrorStatus(status); + setErrorMessage(message); + setErrorDsc(description); + setIsError(true); + }; + + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + try { + const response = await fetch( + `${API_BASE}/api/admin/server-config/all`, + { + method: "GET", + headers: { + Authorization: `Bearer ${Cookies.get("token")}`, + }, + }, + ); + const data = await response.json(); + return data.data; + } catch (error) { + setError("error", "Failed to fetch items", "There is an error"); + } finally { + setIsLoading(false); + } + }; + fetchData().then((data) => { + if (Array.isArray(data)) { + setItems(data); + } + }); + }, [reload]); + + return ( + <> + + Server Konfiguration + + {isError && ( + + )} + {isLoading && ( + + + Loading... + + )} + + + + + + # + + + Service Name + + + Toggle + + + Eintrag erstellt am + + + + + {items.map((item) => ( + + {item.id} + {item.function_name} + + + handleSwitchChange(item.id, !item.active) + } + > + + + + + + + + + {formatDateTime(item.entry_created_at)} + + + ))} + + + + ); +}; + +export default ServerConfig; diff --git a/backendV2/routes/admin/database/serverConfMgmt.database.js b/backendV2/routes/admin/database/serverConfMgmt.database.js new file mode 100644 index 0000000..33185a5 --- /dev/null +++ b/backendV2/routes/admin/database/serverConfMgmt.database.js @@ -0,0 +1,26 @@ +import mysql from "mysql2"; +import dotenv from "dotenv"; +dotenv.config(); + +const pool = mysql + .createPool({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, + }) + .promise(); + +export const getAllFunctions = async () => { + const [rows] = await pool.query("SELECT * FROM functions"); + return { success: true, data: rows }; +}; + +export const updateFunctionStatus = async (functionName, active) => { + const [result] = await pool.query( + "UPDATE functions SET active = ? WHERE function_name = ?", + [active, functionName], + ); + if (result.affectedRows > 0) return { success: true }; + return { success: false }; +}; diff --git a/backendV2/routes/admin/serverConfMgmt.route.js b/backendV2/routes/admin/serverConfMgmt.route.js new file mode 100644 index 0000000..3def349 --- /dev/null +++ b/backendV2/routes/admin/serverConfMgmt.route.js @@ -0,0 +1,50 @@ +import express from "express"; +import { authenticateAdmin } from "../../services/authentication.js"; +const router = express.Router(); +import dotenv from "dotenv"; +dotenv.config(); + +// database funcs import +import { + getAllFunctions, + updateFunctionStatus, +} from "./database/serverConfMgmt.database.js"; + +// Route to get all functions and their statuses +router.get("/all", async (req, res) => { + try { + const result = await getAllFunctions(); + if (result.success) { + res.status(200).json({ data: result.data }); + } else { + res.status(500).json({ message: "Failed to fetch functions" }); + } + } catch (error) { + res + .status(500) + .json({ message: "An error occurred", error: error.message }); + } +}); + +// Route to update the status of a function +router.post("/update", async (req, res) => { + const functionName = req.query.functionName; + let active = req.query.active; + + if (active === "false") { + active = 0; + } else if (active === "true") { + active = 1; + } else { + res.status(406).json({ message: "Got unexpected format" }); + } + + const result = await updateFunctionStatus(functionName, active); + if (result.success) { + res.status(200).json({ message: "Function status updated successfully" }); + } else { + res.status(500).json({ message: "Failed to update function status" }); + } +}); + +export default router; diff --git a/backendV2/routes/api/api.route.js b/backendV2/routes/api/api.route.js index a79ee3e..17b6edc 100644 --- a/backendV2/routes/api/api.route.js +++ b/backendV2/routes/api/api.route.js @@ -5,7 +5,7 @@ const router = express.Router(); import dotenv from "dotenv"; dotenv.config(); -const loan_service = loan_service; +const loan_service = "Loan Service"; import { getItemsFromDatabaseV2, diff --git a/backendV2/server.js b/backendV2/server.js index 61855d8..c268042 100644 --- a/backendV2/server.js +++ b/backendV2/server.js @@ -14,6 +14,7 @@ import loanDataMgmtRouter from "./routes/admin/loanDataMgmt.route.js"; import itemDataMgmtRouter from "./routes/admin/itemDataMgmt.route.js"; import apiDataMgmtRouter from "./routes/admin/apiDataMgmt.route.js"; import userMgmtRouterADMIN from "./routes/admin/userMgmt.route.js"; +import serverConfMgmtRouter from "./routes/admin/serverConfMgmt.route.js"; // API routes import apiRouter from "./routes/api/api.route.js"; @@ -38,6 +39,7 @@ app.use("/api/admin/user-data", userDataMgmtRouter); app.use("/api/admin/item-data", itemDataMgmtRouter); app.use("/api/admin/api-data", apiDataMgmtRouter); app.use("/api/admin/user-mgmt", userMgmtRouterADMIN); +app.use("/api/admin/server-config", serverConfMgmtRouter); // API routes app.use("/api", apiRouter);