diff --git a/package-lock.json b/package-lock.json index 8638befd..ee2bb56c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2220,9 +2220,9 @@ ] }, "node_modules/@remix-run/router": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", - "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz", + "integrity": "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw==", "license": "MIT", "engines": { "node": ">=14.0.0" @@ -5561,12 +5561,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -9316,9 +9310,9 @@ "license": "MIT" }, "node_modules/mobx": { - "version": "6.13.5", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.5.tgz", - "integrity": "sha512-/HTWzW2s8J1Gqt+WmUj5Y0mddZk+LInejADc79NJadrWla3rHzmRHki/mnEUH1AvOmbNTZ1BRbKxr8DSgfdjMA==", + "version": "6.13.6", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.6.tgz", + "integrity": "sha512-r19KNV0uBN4b+ER8Z0gA4y+MzDYIQ2SvOmn3fUrqPnWXdQfakd9yfbPBDBF/p5I+bd3N5Rk1fHONIvMay+bJGA==", "license": "MIT", "funding": { "type": "opencollective", @@ -10475,9 +10469,9 @@ "license": "MIT" }, "node_modules/react-markdown": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", - "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.3.tgz", + "integrity": "sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -10501,12 +10495,12 @@ } }, "node_modules/react-router": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", - "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", + "version": "6.29.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.29.0.tgz", + "integrity": "sha512-DXZJoE0q+KyeVw75Ck6GkPxFak63C4fGqZGNijnWgzB/HzSP1ZfTlBj5COaGWwhrMQ/R8bXiq5Ooy4KG+ReyjQ==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.21.0" + "@remix-run/router": "1.22.0" }, "engines": { "node": ">=14.0.0" @@ -10516,13 +10510,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", - "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", + "version": "6.29.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.29.0.tgz", + "integrity": "sha512-pkEbJPATRJ2iotK+wUwHfy0xs2T59YPEN8BQxVCPeBZvK7kfPESRc/nyxzdcxR17hXgUPYx2whMwl+eo9cUdnQ==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.21.0", - "react-router": "6.28.0" + "@remix-run/router": "1.22.0", + "react-router": "6.29.0" }, "engines": { "node": ">=14.0.0" @@ -11493,16 +11487,16 @@ "license": "MIT" }, "node_modules/swr": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.5.tgz", - "integrity": "sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.2.tgz", + "integrity": "sha512-RosxFpiabojs75IwQ316DGoDRmOqtiAj0tg8wCcbEu4CiLZBs/a9QNtHV7TUfDXmmlgqij/NqzKq/eLelyv9xA==", "license": "MIT", "dependencies": { - "client-only": "^0.0.1", - "use-sync-external-store": "^1.2.0" + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" }, "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/symbol-tree": { @@ -14038,13 +14032,13 @@ "@antv/g2": "4.1.23", "cronstrue": "2.5.0", "lodash.clonedeep": "4.5.0", - "mobx": "6.13.5", + "mobx": "6.13.6", "mobx-react": "9.2.0", "react-helmet-async": "2.0.5", - "react-markdown": "9.0.1", - "react-router": "6.28.0", - "react-router-dom": "6.28.0", - "swr": "2.2.5" + "react-markdown": "9.0.3", + "react-router": "6.29.0", + "react-router-dom": "6.29.0", + "swr": "2.3.2" }, "devDependencies": { "@types/lodash.clonedeep": "4.5.9" diff --git a/packages/console/package.json b/packages/console/package.json index 2ca3a244..9c5893af 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -8,7 +8,7 @@ "dev": "vite", "watch": "vite", "build": "vite build", - "build-watch": "vite build --watch", + "build-watch": "vite build --watch --emptyOutDir", "test": "vitest", "check-types": "tsc --noEmit", "lint": "biome check .", @@ -19,13 +19,13 @@ "@antv/g2": "4.1.23", "cronstrue": "2.5.0", "lodash.clonedeep": "4.5.0", - "mobx": "6.13.5", + "mobx": "6.13.6", "mobx-react": "9.2.0", "react-helmet-async": "2.0.5", - "react-markdown": "9.0.1", - "react-router": "6.28.0", - "react-router-dom": "6.28.0", - "swr": "2.2.5" + "react-markdown": "9.0.3", + "react-router": "6.29.0", + "react-router-dom": "6.29.0", + "swr": "2.3.2" }, "devDependencies": { "@types/lodash.clonedeep": "4.5.9" diff --git a/packages/console/src/App.tsx b/packages/console/src/App.tsx index 3434ae66..3b75421a 100644 --- a/packages/console/src/App.tsx +++ b/packages/console/src/App.tsx @@ -1,18 +1,22 @@ import type { IPluginList } from "@tago-io/tcore-sdk/types"; +import axios, { type Method } from "axios"; import { runInAction } from "mobx"; -import { observer } from "mobx-react"; -import { useEffect, useState } from "react"; -import ReactDOM from "react-dom"; +import type { PropsWithChildren } from "react"; +import { createRoot } from "react-dom/client"; import { Helmet, HelmetProvider } from "react-helmet-async"; -import { useNavigate } from "react-router"; -import { BrowserRouter, Route, Routes } from "react-router-dom"; +import { Outlet, redirect, useLoaderData } from "react-router"; +import { + Route, + RouterProvider, + Routes, + createBrowserRouter, +} from "react-router-dom"; import { ThemeProvider } from "styled-components"; import imgFavicon from "../assets/images/favicon.png"; import ActionEdit from "./Components/Action/Edit/ActionEdit.tsx"; import ActionList from "./Components/Action/List/ActionList.tsx"; import AnalysisEdit from "./Components/Analysis/Edit/AnalysisEdit.tsx"; import AnalysisList from "./Components/Analysis/List/AnalysisList.tsx"; -import BucketEdit from "./Components/Bucket/Edit/BucketEdit.tsx"; import DeviceEdit from "./Components/Device/Edit/DeviceEdit.tsx"; import DeviceList from "./Components/Device/List/DeviceList.tsx"; import Home from "./Components/Home/Home.tsx"; @@ -22,24 +26,55 @@ import MainScreen from "./Components/MainScreen/MainScreen.tsx"; import PageIFrame from "./Components/PageIframe/PageIFrame.tsx"; import PluginEdit from "./Components/Plugins/Edit/PluginEdit.tsx"; import Settings from "./Components/Settings/Edit/Settings.tsx"; -import Setup from "./Components/Setup/Setup.tsx"; +import Setup, { getSetupSteps } from "./Components/Setup/Setup.tsx"; import StepDatabaseError from "./Components/Setup/StepDatabaseError/StepDatabaseError.tsx"; import PluginDetails from "./Components/Store/Details/PluginDetails.tsx"; import PluginStore from "./Components/Store/List/PluginStore.tsx"; import GlobalStyles from "./Components/Styles/GlobalStyles.ts"; import { getLocalStorage, setLocalStorage } from "./Helpers/localStorage.ts"; -import useApiRequest from "./Helpers/useApiRequest.ts"; import getAccountByToken from "./Requests/getAccountByToken.ts"; import { startSocket } from "./System/Socket.ts"; import store from "./System/Store.ts"; import { lightTheme } from "./theme.ts"; -/** - * Main component of the application. - */ -function App() { - const themeObject = lightTheme; +async function apiFetch( + url: string, + options?: { method?: Method; data?: unknown }, +) { + const token = getLocalStorage("token", "") as string; + const masterPassword = store.masterPassword; + const headers = { token, masterPassword }; + const method = options?.method || "get"; + try { + const { data: response } = await axios({ + method, + url, + data: options?.data, + headers, + }); + + if (response?.result !== undefined) { + return { data: response.result as ResponseData, error: null }; + } + + return { data: response as ResponseData, error: null }; + } catch (error) { + return { error, data: null }; + } +} + +export type TStatusResponse = { + version: string; + account: boolean; + database?: { + error?: string; + configured?: boolean; + }; + master_password: boolean; +}; + +function ConsoleWrapper() { return ( <> @@ -47,152 +82,231 @@ function App() { - + - - - + + + ); } -/** - * Wrapper that does all the authentication logic. - */ -const WrappedStoreRoutes = observer(() => { - const { data: status } = useApiRequest("/status"); - const { data: plugins } = useApiRequest("/plugin", { - skip: !store.token, - }); - const [readyToRender, setReadyToRender] = useState(false); - const [stateToken] = useState(() => { - return store.token || getLocalStorage("token", ""); - }); - const navigate = useNavigate(); - - const validateAuth = async () => { - const token = stateToken || getLocalStorage("token", ""); - - if (token) { - // has token, but maybe it's expired - store.token = token; - getAccountByToken(token) - .then((account) => { - store.account = account; - setReadyToRender(true); - }) - .catch(() => { - store.token = ""; - setLocalStorage("token", ""); - setReadyToRender(true); - navigate("/console/login"); - }); - } else { - // not logged in - store.token = ""; - setReadyToRender(true); - navigate("/console/login"); - } +function PageModuleRoutes() { + const loaderData = useLoaderData() as { + pageModules: + | { + pluginID: string; + setup: { + name: string; + route: string; + }; + }[] + | null; }; - useEffect(() => { - if (plugins) { - runInAction(() => { - store.plugins = plugins; - }); - } - }, [plugins]); - - // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect state machine - useEffect(() => { - if (status) { - if (status.database?.error) { - // database has error - setReadyToRender(true); - navigate("/console/database/error"); - } else if ( + const { pageModules } = loaderData; + + return ( + + {pageModules?.map((module) => ( + } + /> + ))} + + ); +} + +function SimpleWrapper(props: PropsWithChildren) { + return ( + <> + + + + + + + + + {props.children} + + + + ); +} + +const router = createBrowserRouter([ + { + path: "/console", + loader: async () => { + const { data: status } = await apiFetch("/status"); + + if (!status || status.database?.error) { + return redirect("/console/database/error"); + } + + if ( !status.account || !status.database?.configured || !status.master_password ) { - // not configured, go to setup - setReadyToRender(true); - navigate("/console/setup"); - } else { - // configured, validate token - validateAuth(); + return redirect("/console/setup"); + } + + const token = getLocalStorage("token"); + if (!token) { + runInAction(() => { + store.token = ""; + }); + + return redirect("/console/login"); } + try { + const account = await getAccountByToken(token); + store.token = token; + store.account = account; + } catch { + store.token = ""; + setLocalStorage("token", ""); + + return redirect("/console/login"); + } + + const { data: plugins } = await apiFetch("/plugin"); + runInAction(() => { store.version = status.version; - store.databaseConfigured = status.database.configured; - store.databaseError = status.database.error; + store.databaseConfigured = status.database?.configured ?? false; + store.databaseError = !!status.database?.error; store.masterPasswordConfigured = status.master_password; store.accountConfigured = status.account; + + if (plugins) { + store.plugins = plugins; + } }); - } - }, [status, navigate]); - - /** - * Starts the socket connection. - */ - // biome-ignore lint/correctness/useExhaustiveDependencies: mobx observers - useEffect(() => { - if (store.masterPassword || store.token) { - startSocket(); - } - }, [store.masterPassword, store.token]); - if (!readyToRender) { - return null; - } + if (store.masterPassword || store.token) { + startSocket(); + } - return ( - - } /> - } /> - } /> - } /> - - ); -}); + return null; + }, -/** - * Renders the main admin screen, which is the one that has the sidebar and navbar. - */ -function MainScreenWrapper() { - const { data: pageModules } = useApiRequest("/module?type=page"); + element: , + children: [ + { + index: true, + element: , + }, + { + path: "actions", + element: , + }, + { + path: "actions/:id", + element: , + }, + { + path: "analysis", + element: , + }, + { + path: "analysis/:id", + element: , + }, + { + path: "devices", + element: , + }, + { + path: "devices/:id", + element: , + }, + { + path: "logs", + element: , + }, + { + path: "plugin/:id", + element: , + }, + { + path: "settings", + element: , + }, + { + path: "pluginstore", + element: , + }, + { + path: "pluginstore/detail/:id", + element: , + }, + { + path: "*", + loader: async () => { + const { data: pageModules = [] } = + await apiFetch("/module?type=page"); - return ( - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - {pageModules?.map((module) => ( - } - /> - ))} - - - ); -} + return { pageModules }; + }, + element: , + }, + ], + }, + + { + path: "/console/login", + element: ( + + + + ), + }, + + { + path: "/console/setup", + loader: async (a) => { + const { data: status } = await apiFetch("/status"); -ReactDOM.render(, document.getElementById("root")); + if (!status) { + throw new Error("Error getting status from TagoCore API"); + } + + const stepList = getSetupSteps(status); + + return { stepList }; + }, + + element: ( + + + + ), + }, + + { + path: "/console/database/error", + element: ( + + + + ), + }, +]); + +const rootNode = document.getElementById("root"); +if (rootNode) { + const root = createRoot(rootNode); + + root.render(); +} else { + console.error("Could not find root node to render."); +} diff --git a/packages/console/src/Components/RelativeDate/RelativeDate.test.tsx b/packages/console/src/Components/RelativeDate/RelativeDate.test.tsx index 9a429ff6..4597055d 100644 --- a/packages/console/src/Components/RelativeDate/RelativeDate.test.tsx +++ b/packages/console/src/Components/RelativeDate/RelativeDate.test.tsx @@ -20,34 +20,34 @@ test("renders correct output for `undefined`", () => { expect(screen.getByText("Never")).toBeInTheDocument(); }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("renders correct output for right now", () => { // render(); // expect(screen.getByText("a few seconds ago")).toBeInTheDocument(); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("renders correct output for 10 seconds ago", () => { // const someTimeAgo = Date.now() - 10 * 1000; // render(); // expect(screen.getByText("10 seconds ago")).toBeInTheDocument(); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("renders correct output for 5 minutes ago", () => { // const someTimeAgo = Date.now() - 5 * 1000 * 60; // render(); // expect(screen.getByText("5 minutes ago")).toBeInTheDocument(); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("text shows same string with `useInputStyle` prop", () => { // const someTimeAgo = Date.now() - 5 * 1000 * 60; // render(); // expect(screen.getByText("5 minutes ago")).toBeInTheDocument(); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("hovering over text opens tooltip", async () => { // const someTimeAgo = Date.now() - 5 * 1000 * 60; // const { user } = renderWithEvents(); @@ -55,7 +55,7 @@ test("renders correct output for `undefined`", () => { // expect(screen.queryByTestId("tooltip")).toBeInTheDocument(); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("hovering over text with `useInputStyle` opens tooltip", async () => { // const { user } = renderWithEvents( // , diff --git a/packages/console/src/Components/Setup/Setup.tsx b/packages/console/src/Components/Setup/Setup.tsx index 65be215a..cfbeb491 100644 --- a/packages/console/src/Components/Setup/Setup.tsx +++ b/packages/console/src/Components/Setup/Setup.tsx @@ -1,12 +1,13 @@ import { observer } from "mobx-react"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { useNavigate } from "react-router"; -import setDocumentTitle from "../../Helpers/setDocumentTitle.ts"; -import store from "../../System/Store.ts"; +import { useCallback, useMemo, useState } from "react"; +import { useLoaderData, useNavigate } from "react-router"; + +import type { TStatusResponse } from "src/App.tsx"; + import SetupBackground from "./SetupBackground/SetupBackground.tsx"; import StepDatabaseWrapper from "./StepDatabaseWrapper/StepDatabaseWrapper.tsx"; import StepMasterPassword from "./StepMasterPassword/StepMasterPassword.tsx"; -import { StepPluginConfigByID } from "./StepPluginConfig/StepPluginConfig.tsx"; +import StepPluginConfigByID from "./StepPluginConfig/StepPluginConfig.tsx"; import StepSignUp from "./StepSignUp/StepSignUp.tsx"; import StepWelcome from "./StepWelcome/StepWelcome.tsx"; @@ -17,93 +18,39 @@ type TSetupStep = | "plugin-settings" | "create-account"; -/** - * Main setup component. - */ +type SetupLoaderData = { stepList: TSetupStep[] }; + function Setup() { - const [step, setStep] = useState(0); - const [pluginID, setPluginID] = useState(null); - const [readyToRender, setReadyToRender] = useState(false); + const { stepList } = useLoaderData() as SetupLoaderData; const navigate = useNavigate(); - // biome-ignore lint/correctness/useExhaustiveDependencies: mobx observers - const steps = useMemo(() => { - const stepList: TSetupStep[] = ["welcome"]; - - if (!store.masterPasswordConfigured) { - stepList.push("master-password"); - } - - if (!store.databaseConfigured) { - stepList.push("database-setup"); - } - - if (pluginID) { - stepList.push("plugin-settings"); - } - - if (!store.accountConfigured) { - stepList.push("create-account"); - } - - return stepList; - }, [ - store.masterPasswordConfigured, - store.databaseConfigured, - store.accountConfigured, - pluginID, - ]); + const [step, setStep] = useState(0); + const [selectedDatabasePluginID, setSelectedDatabasePluginID] = useState< + string | null + >(null); const currentStep = useMemo(() => { - return steps[step] || null; - }, [steps, step]); - - const next = useCallback( - (param: any) => { - if (currentStep === "master-password") { - store.masterPasswordConfigured = true; - store.masterPassword = param as string; - } else if (currentStep === "database-setup") { - setPluginID(param as string); - setStep((state) => state + 1); - } else if (step === steps.length - 1) { - navigate("/console/login"); - } else { - setStep((state) => state + 1); - } - }, - [steps, currentStep, step, navigate], - ); + return stepList[step] || null; + }, [stepList, step]); const back = useCallback(() => { - setStep(step - 1); + if (step > 1) { + setStep(step - 1); + } }, [step]); - /** - * Sets the document title. - */ - useEffect(() => { - if (steps.length === 1) { - navigate("/console"); - } else if (steps.length === 2 && steps[1] === "create-account") { - setReadyToRender(true); - setStep(1); - } else { - setReadyToRender(true); - setDocumentTitle("Setup"); + const next = useCallback(() => { + if (currentStep === "create-account") { + navigate("/console/login"); + return; } - }, [steps, navigate]); - useEffect(() => { - if (!currentStep) { - navigate("/console"); + const nextStep = stepList[step + 1]; + if (nextStep) { + setStep(step + 1); } - }, [currentStep, navigate]); - - if (!readyToRender || !currentStep) { - return null; - } + }, [stepList, step, currentStep, navigate]); if (currentStep === "master-password") { return ( @@ -114,33 +61,43 @@ function Setup() { ); } - if (currentStep === "database-setup") { + if ( + currentStep === "database-setup" || + (currentStep === "plugin-settings" && !selectedDatabasePluginID) + ) { return ( <> - + + setSelectedDatabasePluginID(pluginID) + } + /> ); } - if (currentStep === "create-account") { + if (currentStep === "plugin-settings" && selectedDatabasePluginID) { return ( <> - + ); } - if (currentStep === "plugin-settings" && pluginID) { + if (currentStep === "create-account") { return ( <> - + ); } @@ -153,4 +110,22 @@ function Setup() { ); } +export function getSetupSteps(status: TStatusResponse) { + const stepList: TSetupStep[] = ["welcome"]; + + if (!status.master_password) { + stepList.push("master-password"); + } + + if (!status.database?.configured) { + stepList.push("database-setup", "plugin-settings"); + } + + if (!status.account) { + stepList.push("create-account"); + } + + return stepList; +} + export default observer(Setup); diff --git a/packages/console/src/Components/Setup/StepDatabaseWithStore/StepDatabaseWithStore.tsx b/packages/console/src/Components/Setup/StepDatabaseWithStore/StepDatabaseWithStore.tsx index 1ede136e..7e033bdd 100644 --- a/packages/console/src/Components/Setup/StepDatabaseWithStore/StepDatabaseWithStore.tsx +++ b/packages/console/src/Components/Setup/StepDatabaseWithStore/StepDatabaseWithStore.tsx @@ -8,24 +8,26 @@ import store from "../../../System/Store.ts"; import { Button, EButton, - EIcon, - Icon, Input, Loading, PluginImage, Publisher, - Tooltip, useApiRequest, } from "../../../index.ts"; -import ModalDownloadFromURL from "../../Plugins/Common/ModalDownloadFromURL/ModalDownloadFromURL.tsx"; import ModalInstallPlugin from "../../Plugins/Common/ModalInstallPlugin/ModalInstallPlugin.tsx"; import ModalMasterPassword from "../../Plugins/Common/ModalMasterPassword/ModalMasterPassword.tsx"; -import ModalUploadPlugin from "../../Plugins/Common/ModalUploadPlugin/ModalUploadPlugin.tsx"; import SetupForm from "../SetupForm/SetupForm.tsx"; import * as Style from "./StepDatabaseWithStore.style"; -function StepDatabaseWithStore(props: any) { - const { onBack, onNext } = props; +interface StepDatabaseWithStoreProps { + onBack: () => void; + onNext: () => void; + onSelectDatabasePlugin: (pluginID: string) => void; +} + +function StepDatabaseWithStore(props: StepDatabaseWithStoreProps) { + const { onBack, onNext, onSelectDatabasePlugin } = props; + const [buttonsDisabled, setButtonsDisabled] = useState(false); const [action, setAction] = useState(""); const [modalInstall, setModalInstall] = useState(false); @@ -62,20 +64,6 @@ function StepDatabaseWithStore(props: any) { setModalURL(true); }, []); - /** - * Closes the URL download modal. - */ - const deactivateModalURL = () => { - setModalURL(false); - }; - - /** - * Closes the file selector modal. - */ - const deactivateModalFile = () => { - setModalUpload(false); - }; - /** * Opens the install modal. */ @@ -92,9 +80,11 @@ function StepDatabaseWithStore(props: any) { const deactivateModalInstall = (pluginID: string) => { setModalInstall(false); if (selectedItem) { - onNext(selectedItem?.id); + onSelectDatabasePlugin(selectedItem.id); + onNext(); } else if (pluginID) { - onNext(pluginID); + onSelectDatabasePlugin(pluginID); + onNext(); } }; @@ -119,7 +109,8 @@ function StepDatabaseWithStore(props: any) { setButtonsDisabled(false); } } else { - onNext(selectedItem?.id); + onSelectDatabasePlugin(selectedItem.id); + onNext(); } }, [ platform, @@ -127,10 +118,9 @@ function StepDatabaseWithStore(props: any) { selectedItem, installedListFiltered, activateModalInstall, + onSelectDatabasePlugin, ]); - /** - */ const renderItem = (item: any) => { const installed = installedListFiltered?.some((x) => x.id === item.id); return ( diff --git a/packages/console/src/Components/Setup/StepDatabaseWrapper/StepDatabaseWrapper.tsx b/packages/console/src/Components/Setup/StepDatabaseWrapper/StepDatabaseWrapper.tsx index b09d5d40..9471ed6e 100644 --- a/packages/console/src/Components/Setup/StepDatabaseWrapper/StepDatabaseWrapper.tsx +++ b/packages/console/src/Components/Setup/StepDatabaseWrapper/StepDatabaseWrapper.tsx @@ -1,33 +1,27 @@ -import { PLUGIN_STORE_PLUGIN_ID } from "@tago-io/tcore-shared"; import { observer } from "mobx-react"; import { useEffect, useState } from "react"; import store from "../../../System/Store.ts"; -import { EIcon, EmptyMessage, Loading, useApiRequest } from "../../../index.ts"; +import { EIcon, EmptyMessage, Loading } from "../../../index.ts"; import ModalMasterPassword from "../../Plugins/Common/ModalMasterPassword/ModalMasterPassword.tsx"; import SetupForm from "../SetupForm/SetupForm.tsx"; import StepDatabaseWithStore from "../StepDatabaseWithStore/StepDatabaseWithStore.tsx"; +interface StepDatabaseSetup { + onBack: () => void; + onNext: () => void; + onSelectDatabasePlugin: (pluginID: string) => void; +} + /** * Wrapper of the database step to figure out which screen to show: * * - the database selection without the plugin store. * - or the database selection with the plugin store. */ -function StepDatabaseWrapper(props: { - onBack: () => void; - onNext: (param: any) => void; -}) { +function StepDatabaseWrapper(props: StepDatabaseSetup) { + const { onBack, onNext, onSelectDatabasePlugin } = props; + const [checkPassword, setCheckPassword] = useState(true); - // FIXME: is this request needed? - // FIXME: also see weird error handling below if request is removed - const { data, error } = useApiRequest( - `/plugin/${PLUGIN_STORE_PLUGIN_ID}/get-database-list/call`, - { - method: "post", - skip: !store.masterPassword, - }, - ); - const { onBack, onNext } = props; // biome-ignore lint/correctness/useExhaustiveDependencies(store.masterPassword): mobx observer useEffect(() => { @@ -36,11 +30,9 @@ function StepDatabaseWrapper(props: { } }, [store.masterPassword]); - const loading = !data && !error; - return ( <> - {loading || !store.masterPassword ? ( + {!store.masterPassword ? ( )} - ) : error ? ( - ) : ( - + )} {checkPassword && !store.masterPassword && ( diff --git a/packages/console/src/Components/Setup/StepMasterPassword/StepMasterPassword.tsx b/packages/console/src/Components/Setup/StepMasterPassword/StepMasterPassword.tsx index a290468f..ba49b296 100644 --- a/packages/console/src/Components/Setup/StepMasterPassword/StepMasterPassword.tsx +++ b/packages/console/src/Components/Setup/StepMasterPassword/StepMasterPassword.tsx @@ -1,16 +1,22 @@ import { getSystemName } from "@tago-io/tcore-shared"; import axios from "axios"; +import { runInAction } from "mobx"; +import { observer } from "mobx-react"; import { type KeyboardEvent, useCallback, useRef, useState } from "react"; + +import store from "../../../System/Store.ts"; import { EButton, EIcon, FormGroup, Icon } from "../../../index.ts"; import ErrorMessage from "../../ErrorMessage/ErrorMessage.tsx"; import InputPassword from "../../InputPassword/InputPassword.tsx"; import SetupForm from "../SetupForm/SetupForm.tsx"; import * as Style from "./StepMasterPassword.style"; -function StepMasterPassword(props: { +interface StepMasterPasswordProps { onBack: () => void; - onNext: (password: string) => void; -}) { + onNext: () => void; +} + +function StepMasterPassword(props: StepMasterPasswordProps) { const { onBack, onNext } = props; const [value, setValue] = useState(""); @@ -22,10 +28,7 @@ function StepMasterPassword(props: { const confirmationRef = useRef(null); const validate = useCallback( - (form: { - value: string; - confirmation: string; - }) => { + (form: { value: string; confirmation: string }) => { if (!form.value || !form.confirmation) { return { message: "Password and confirmation are required", @@ -67,7 +70,12 @@ function StepMasterPassword(props: { setIsPending(true); await axios.post("/settings/master/password", { password: value }); - onNext(value); + runInAction(() => { + store.masterPasswordConfigured = true; + store.masterPassword = value; + }); + + onNext(); } catch { setIsPending(false); } @@ -153,4 +161,4 @@ function StepMasterPassword(props: { ); } -export default StepMasterPassword; +export default observer(StepMasterPassword); diff --git a/packages/console/src/Components/Setup/StepPluginConfig/StepPluginConfig.tsx b/packages/console/src/Components/Setup/StepPluginConfig/StepPluginConfig.tsx index 523b5b78..fb128357 100644 --- a/packages/console/src/Components/Setup/StepPluginConfig/StepPluginConfig.tsx +++ b/packages/console/src/Components/Setup/StepPluginConfig/StepPluginConfig.tsx @@ -1,11 +1,15 @@ import type { IPlugin } from "@tago-io/tcore-sdk/types"; import { flattenConfigFields } from "@tago-io/tcore-shared"; +import axios from "axios"; +import { observer } from "mobx-react"; import { useCallback, useEffect, useMemo, useState } from "react"; + import { promiseDelay } from "../../../Helpers/promiseDelay.ts"; import validateConfigFields from "../../../Helpers/validateConfigFields.ts"; import editPluginSettings from "../../../Requests/editPluginSettings.ts"; import editSettings from "../../../Requests/editSettings.ts"; import { usePluginInfo } from "../../../Requests/getPluginInfo.ts"; +import store from "../../../System/Store.ts"; import { EButton, EIcon, @@ -18,16 +22,6 @@ import Status from "../../Plugins/Common/Status/Status.tsx"; import SetupForm, { type SetupFormButton } from "../SetupForm/SetupForm.tsx"; import SuccessMessage from "../SuccessMessage/SuccessMessage.tsx"; -interface IStepPluginConfigProps { - backButton?: any; - description?: string; - mustBeDatabasePlugin?: boolean; - plugin?: IPlugin; - pluginID?: string; - title?: string; - onBack?: () => void; -} - interface StepPluginConfigProps { plugin: IPlugin | null; title?: string; @@ -35,7 +29,8 @@ interface StepPluginConfigProps { backButton?: SetupFormButton; isLoading?: boolean; mustBeDatabasePlugin?: boolean; - onBack?: () => void; + onNext: () => void; + onBack: () => void; } export function StepPluginConfig(props: StepPluginConfigProps) { @@ -45,6 +40,7 @@ export function StepPluginConfig(props: StepPluginConfigProps) { description = "Adjust the settings of your main Database Plugin", isLoading = false, mustBeDatabasePlugin = false, + onNext, onBack, } = props; @@ -142,6 +138,13 @@ export function StepPluginConfig(props: StepPluginConfigProps) { await promiseDelay(1000); await editSettings({ database_plugin: `${plugin.id}:${firstModule.id}` }); + await promiseDelay(1000); + + // replicate master password on the selected database + await axios.post("/settings/master/password", { + password: store.masterPassword, + }); + setSuccess(true); } catch (error: any) { const msg = error?.response?.data?.message || error?.toString?.(); @@ -160,10 +163,10 @@ export function StepPluginConfig(props: StepPluginConfigProps) { (window.location.href = "/")} + onClick={() => onNext()} /> ); - }, [success]); + }, [success, onNext]); useEffect(() => { if (plugin) { @@ -211,7 +214,7 @@ export function StepPluginConfig(props: StepPluginConfigProps) { ); } -export function StepPluginConfigByID( +function StepPluginConfigByID( props: Omit & { pluginID: string; }, @@ -229,6 +232,10 @@ export function StepPluginConfigByID( ); } +const StepPluginConfigByIDWithStore = observer(StepPluginConfigByID); + +export default StepPluginConfigByIDWithStore; + function ErrorStatus(props: { message: string }) { if (!props.message) { return null; diff --git a/packages/console/src/Components/Setup/StepSignUp/StepSignUp.tsx b/packages/console/src/Components/Setup/StepSignUp/StepSignUp.tsx index 70f8e174..e32c2f6b 100644 --- a/packages/console/src/Components/Setup/StepSignUp/StepSignUp.tsx +++ b/packages/console/src/Components/Setup/StepSignUp/StepSignUp.tsx @@ -9,10 +9,12 @@ import ModalMasterPassword from "../../Plugins/Common/ModalMasterPassword/ModalM import SetupForm from "../SetupForm/SetupForm.tsx"; import * as Style from "./StepSignUp.style"; -function StepSignUp(props: { - onNext: (param: any) => void; +interface StepSignUpProps { onBack: () => void; -}) { + onNext: () => void; +} + +function StepSignUp(props: StepSignUpProps) { const [data, setData] = useState({}); const [error, setError] = useState({}); const [loading, setLoading] = useState(false); @@ -70,7 +72,7 @@ function StepSignUp(props: { const headers = { masterPassword: store.masterPassword }; axios .post("/account", data, { headers }) - .then(() => onNext(data)) + .then(() => onNext()) .catch((err) => { const errorMessage = err?.response?.data?.message || err?.toString?.(); setError({ message: errorMessage }); diff --git a/packages/console/src/Requests/deleteBucketVariables.ts b/packages/console/src/Requests/deleteBucketVariables.ts deleted file mode 100644 index 33c3b477..00000000 --- a/packages/console/src/Requests/deleteBucketVariables.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { TGenericID } from "@tago-io/tcore-sdk/types"; - -// FIXME: remove this with the entire bucket module -/** @deprecated */ -async function deleteBucketVariables( - id: TGenericID, - params: any, -): Promise { - throw new Error("To be removed"); -} - -export default deleteBucketVariables; diff --git a/packages/sdk/src/Types/Plugin/Plugin.types.test.ts b/packages/sdk/src/Types/Plugin/Plugin.types.test.ts index 1f3a4548..811a97bc 100644 --- a/packages/sdk/src/Types/Plugin/Plugin.types.test.ts +++ b/packages/sdk/src/Types/Plugin/Plugin.types.test.ts @@ -210,7 +210,6 @@ describe("zPluginModuleList", () => { zPluginModuleList.parse(data); } catch (error) { const e = (error as ZodError).flatten(); - console.log(e); expect(e.formErrors).toStrictEqual({ setup: ["Required", "Required", "Required"], }); diff --git a/packages/server/Controllers/Settings.ts b/packages/server/Controllers/Settings.ts index 04544d5b..523e36b6 100644 --- a/packages/server/Controllers/Settings.ts +++ b/packages/server/Controllers/Settings.ts @@ -51,6 +51,7 @@ class SetMasterPassword extends APIController< public async main() { const settings = await getMainSettings(); + if (settings.master_password) { throw new Error("Master password is already set"); } diff --git a/packages/server/Helpers/Time.test.ts b/packages/server/Helpers/Time.test.ts index 9dc4d6b5..23c591cc 100644 --- a/packages/server/Helpers/Time.test.ts +++ b/packages/server/Helpers/Time.test.ts @@ -1,82 +1,98 @@ import { describe, expect, it } from "vitest"; import { fixDurationMoment, parseRelativeDate } from "./Time.ts"; +// TODO:(rc) fix test description later in the RC describe("Fix Duration Moment", () => { it("should fix the duration", () => { const duration = "S"; const result = fixDurationMoment(duration); expect(result).toBe("millisecond"); }); + it("should fix the duration", () => { const duration = "SS"; const result = fixDurationMoment(duration); expect(result).toBe("milliseconds"); }); + it("should fix the duration", () => { const duration = "s"; const result = fixDurationMoment(duration); expect(result).toBe("second"); }); + it("should fix the duration", () => { const duration = "ss"; const result = fixDurationMoment(duration); expect(result).toBe("seconds"); }); + it("should fix the duration", () => { const duration = "m"; const result = fixDurationMoment(duration); expect(result).toBe("minute"); }); + it("should fix the duration", () => { const duration = "mm"; const result = fixDurationMoment(duration); expect(result).toBe("minutes"); }); + it("should fix the duration", () => { const duration = "h"; const result = fixDurationMoment(duration); expect(result).toBe("hour"); }); + it("should fix the duration", () => { const duration = "hh"; const result = fixDurationMoment(duration); expect(result).toBe("hours"); }); + it("should fix the duration", () => { const duration = "d"; const result = fixDurationMoment(duration); expect(result).toBe("day"); }); + it("should fix the duration", () => { const duration = "dd"; const result = fixDurationMoment(duration); expect(result).toBe("days"); }); + it("should fix the duration", () => { const duration = "w"; const result = fixDurationMoment(duration); expect(result).toBe("week"); }); + it("should fix the duration", () => { const duration = "ww"; const result = fixDurationMoment(duration); expect(result).toBe("weeks"); }); + it("should fix the duration", () => { const duration = "M"; const result = fixDurationMoment(duration); expect(result).toBe("month"); }); + it("should fix the duration", () => { const duration = "MM"; const result = fixDurationMoment(duration); expect(result).toBe("months"); }); + it("should fix the duration", () => { const duration = "y"; const result = fixDurationMoment(duration); expect(result).toBe("year"); }); + it("should fix the duration", () => { const duration = "yy"; const result = fixDurationMoment(duration); @@ -92,6 +108,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2021-01-01T00:00:00.040Z"); }); + it("should parse relative date using plus operator for seconds", () => { const expireTime = "30 seconds"; const operation = "plus"; @@ -99,6 +116,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2021-01-01T00:00:30.000Z"); }); + it("should parse relative date using plus operator for minutes", () => { const expireTime = "15 minutes"; const operation = "plus"; @@ -106,6 +124,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2021-01-01T00:15:00.000Z"); }); + it("should parse relative date using plus operator for hours", () => { const expireTime = "12 hours"; const operation = "plus"; @@ -113,6 +132,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2021-01-01T12:00:00.000Z"); }); + it("should parse relative date using plus operator for days", () => { const expireTime = "9 days"; const operation = "plus"; @@ -120,6 +140,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2021-01-10T00:00:00.000Z"); }); + it("should parse relative date using plus operator for weeks", () => { const expireTime = "6 weeks"; const operation = "plus"; @@ -127,6 +148,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2021-02-12T00:00:00.000Z"); }); + it("should parse relative date using plus operator for months", () => { const expireTime = "12 months"; const operation = "plus"; @@ -134,6 +156,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2022-01-01T00:00:00.000Z"); }); + it("should parse relative date using plus operator for years", () => { const expireTime = "3 years"; const operation = "plus"; @@ -141,6 +164,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2024-01-01T00:00:00.000Z"); }); + it("should parse relative date using minus operator for milliseconds", () => { const expireTime = "20 SS"; const operation = "minus"; @@ -148,6 +172,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2020-12-31T23:59:59.980Z"); }); + it("should parse relative date using minus operator for seconds", () => { const expireTime = "60 sec"; const operation = "minus"; @@ -155,6 +180,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2020-12-31T23:59:00.000Z"); }); + it("should parse relative date using minus operator for minutes", () => { const expireTime = "15 min"; const operation = "minus"; @@ -162,6 +188,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2020-12-31T23:45:00.000Z"); }); + it("should parse relative date using minus operator for hours", () => { const expireTime = "32 hh"; const operation = "minus"; @@ -169,6 +196,7 @@ describe("Parse Relative Date", () => { const result = parseRelativeDate(expireTime, operation, fromDate); expect((result as Date).toISOString()).toBe("2020-12-30T16:00:00.000Z"); }); + it("should parse relative date using minus operator for days", () => { const expireTime = "5 dd"; const operation = "minus"; @@ -177,6 +205,7 @@ describe("Parse Relative Date", () => { expect((result as Date).toISOString()).toBe("2020-12-27T00:00:00.000Z"); }); + it("should parse relative date using minus operator for weeks", () => { const expireTime = "9 ww"; const operation = "minus"; @@ -185,6 +214,7 @@ describe("Parse Relative Date", () => { expect((result as Date).toISOString()).toBe("2020-10-30T00:00:00.000Z"); }); + it("should parse relative date using minus operator for months", () => { const expireTime = "6 MM"; const operation = "minus"; @@ -193,6 +223,7 @@ describe("Parse Relative Date", () => { expect((result as Date).toISOString()).toBe("2020-07-01T00:00:00.000Z"); }); + it("should parse relative date using minus operator for years", () => { const expireTime = "5 yy"; const operation = "minus"; @@ -201,25 +232,29 @@ describe("Parse Relative Date", () => { expect((result as Date).toISOString()).toBe("2016-01-01T00:00:00.000Z"); }); + it("should return never", () => { const expireTime = "Never"; const operation = "plus"; const result = parseRelativeDate(expireTime, operation); expect(result).toBe("never"); }); - //TODO FIX + + // TODO:(rc) re-enable tests later in the RC // it("should split the expireTime string", () => { // const expireTime = "5 months"; // const operation = "plus"; // const fn = () => parseRelativeDate(expireTime, operation); // expect(fn).resolves; // }); + it("should have a number to split the expireTime string", () => { const expireTime = "months"; const operation = "plus"; const fn = () => parseRelativeDate(expireTime, operation); expect(fn).toThrowError("Invalid relative time"); }); + it("should have a valid time", () => { const expireTime = "3 days"; const operation = "plus"; diff --git a/packages/server/Helpers/Yaml.ts b/packages/server/Helpers/Yaml.ts index 6f9d2101..3d012363 100644 --- a/packages/server/Helpers/Yaml.ts +++ b/packages/server/Helpers/Yaml.ts @@ -13,7 +13,7 @@ export async function saveYml(json: any, filePath: string): Promise { await fs.mkdir(folder, { recursive: true }); await fs.writeFile(filePath, file); } catch (ex) { - // TODO handle better + // TODO:(rc) improve error handling later in the RC } } diff --git a/packages/server/Helpers/formatBytes.test.ts b/packages/server/Helpers/formatBytes.test.ts index 0ba02bd6..b5d2de7f 100644 --- a/packages/server/Helpers/formatBytes.test.ts +++ b/packages/server/Helpers/formatBytes.test.ts @@ -53,6 +53,4 @@ describe("Format bytes", () => { expect(start).toBe("1 TB"); expect(end).toBe("1024 TB"); }); - - // TODO: Add more tests }); diff --git a/packages/server/Plugins/Plugin/Plugin.test.ts b/packages/server/Plugins/Plugin/Plugin.test.ts index b6727ccf..ccdb2ea8 100644 --- a/packages/server/Plugins/Plugin/Plugin.test.ts +++ b/packages/server/Plugins/Plugin/Plugin.test.ts @@ -22,7 +22,7 @@ test("throws error if package is not found", () => { expect(() => new Plugin(folder)).toThrowError(msg); }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("calls Validator.validatePackageJSON", async () => { // const fn = vi.spyOn(Validator.prototype, "validatePackageJSON"); // const folder = path.join(dirname__, "..", "__mocks__", "plugin4"); @@ -67,7 +67,7 @@ test("has correct initial state", () => { expect(plugin.state).toEqual("idle"); }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("starts a simple plugin", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin4"); // const plugin = new Plugin(folder); @@ -86,7 +86,7 @@ test("has correct initial state", () => { // await plugin.stop(true); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("throws when attempting to start an already started plugin", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin4"); // const plugin = new Plugin(folder); @@ -100,7 +100,7 @@ test("has correct initial state", () => { // } // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("gracefully stops a simple plugin", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin4"); // const plugin = new Plugin(folder); @@ -128,7 +128,7 @@ test("loads a plugin that has just an error", async () => { await plugin.stop(true).catch(() => null); }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("loads a plugin that has an error after a module definition", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin6"); // const plugin = new Plugin(folder); @@ -147,7 +147,7 @@ test("loads a plugin that has just an error", async () => { // await plugin.stop(true).catch(() => null); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("loads a plugin that has an error in a module.onLoad call", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin9"); // const plugin = new Plugin(folder); @@ -165,7 +165,7 @@ test("loads a plugin that has just an error", async () => { // await plugin.stop(true); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("only returns after all modules were loaded", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin10"); // const plugin = new Plugin(folder); @@ -189,7 +189,7 @@ test("loads a plugin that has just an error", async () => { // await plugin.stop(true); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("stops plugin execution without waiting for onDestroy", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin11"); // const plugin = new Plugin(folder); @@ -209,7 +209,7 @@ test("loads a plugin that has just an error", async () => { // expect(modules[0].state).toEqual("stopped"); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("stops plugin execution waiting for onDestroy", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin11"); // const plugin = new Plugin(folder); @@ -229,7 +229,7 @@ test("loads a plugin that has just an error", async () => { // expect(modules[0].state).toEqual("stopped"); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("sets message property of module correctly", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin12"); // const plugin = new Plugin(folder); @@ -244,7 +244,7 @@ test("loads a plugin that has just an error", async () => { // await plugin.stop(true); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("shuts down plugin when disabling it", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin12"); // const plugin = new Plugin(folder); @@ -272,7 +272,7 @@ test("removes error when disabling plugin", async () => { expect(plugin.error).toBeNull(); }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("ignores disable if already disabled", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin12"); // const plugin = new Plugin(folder); @@ -285,7 +285,7 @@ test("removes error when disabling plugin", async () => { // expect(plugin.error).toEqual("not_null_because_it_wasn't_reset"); // }); -//TODO FIX +// TODO:(rc) re-enable tests later in the RC // test("starts plugin when enabling it", async () => { // const folder = path.join(dirname__, "..", "__mocks__", "plugin12"); // const plugin = new Plugin(folder); diff --git a/packages/server/Plugins/Worker/Worker.ts b/packages/server/Plugins/Worker/Worker.ts index 9c3f961d..ee250d56 100644 --- a/packages/server/Plugins/Worker/Worker.ts +++ b/packages/server/Plugins/Worker/Worker.ts @@ -168,7 +168,7 @@ class Worker extends EventEmitter { this.onModuleLoaded(); - // TODO improve + // TODO:(rc) decide what to "improve" here later in the RC if (setup.type === "action-trigger") { const actions = await getActionList({ amount: 99899, diff --git a/packages/server/Services/DeviceData/DeviceData.test.ts b/packages/server/Services/DeviceData/DeviceData.test.ts index 496e927c..1e2f65b0 100644 --- a/packages/server/Services/DeviceData/DeviceData.test.ts +++ b/packages/server/Services/DeviceData/DeviceData.test.ts @@ -185,7 +185,7 @@ describe("addDeviceDataByDevice", () => { expect(arg3[0].time).toBeInstanceOf(Date); }); - // TODO - Fix this test + // TODO:(rc) re-enable tests later in the RC test("applies payload encoder, then parser, then inserts data", async () => { const m1 = vi .spyOn(DeviceData, "applyPayloadEncoder") @@ -207,7 +207,7 @@ describe("addDeviceDataByDevice", () => { expect(data).toEqual([]); }); - // TODO - Fix this test + // TODO:(rc) re-enable tests later in the RC // test("triggers actions after inserting data", async () => { // const mock = vi.spyOn(DeviceData, "triggerActions"); // await DeviceData.addDeviceDataByDevice(activeDevice, dataCreate); @@ -233,7 +233,7 @@ describe("addDeviceDataByDevice", () => { expect(arg2.last_input).toBeInstanceOf(Date); }); - // TODO - Fix this test + // TODO:(rc) re-enable tests later in the RC // test("throws if mutable device has too much data", async () => { // vi.spyOn(DeviceData, "getDeviceDataAmount").mockResolvedValue(50000); // const fn = () => @@ -241,7 +241,7 @@ describe("addDeviceDataByDevice", () => { // await expect(fn).rejects.toThrow("has reached the limit"); // }); - // TODO - Fix this test + // TODO:(rc) re-enable tests later in the RC // test("doesn't throws if mutable device doesn't have too much data", async () => { // vi.spyOn(DeviceData, "getDeviceDataAmount").mockResolvedValue(1000); // const result = await DeviceData.addDeviceDataByDevice( @@ -410,7 +410,7 @@ describe("deleteDeviceData", () => { expect(arg4).toEqual(mutableDevice.id); }); - // TODO - Fix this test + // TODO:(rc) re-enable tests later in the RC // test("returns the amount of deleted items", async () => { // const deviceData = [mockData, mockData, mockData, mockData]; // vi.spyOn(Device, "getDeviceInfo").mockResolvedValue(mutableDevice); @@ -419,7 +419,7 @@ describe("deleteDeviceData", () => { // expect(result).toEqual(deviceData.length); // }); - // TODO - Fix this test + // TODO:(rc) re-enable tests later in the RC // test("throws if getDeviceData result is not an array", async () => { // vi.spyOn(Device, "getDeviceInfo").mockResolvedValue(mutableDevice); // vi.spyOn(DeviceData, "getDeviceData").mockResolvedValue(123); diff --git a/packages/server/Services/Hardware.test.ts b/packages/server/Services/Hardware.test.ts index eab47578..d7261ead 100644 --- a/packages/server/Services/Hardware.test.ts +++ b/packages/server/Services/Hardware.test.ts @@ -122,6 +122,4 @@ describe("Get platform code", () => { // @ts-ignore expect(getPlatformCode(platform)).toBe(expected); }); - - // TODO: add more tests }); diff --git a/packages/server/Services/Plugins.ts b/packages/server/Services/Plugins.ts index c02bda60..849b0e0e 100644 --- a/packages/server/Services/Plugins.ts +++ b/packages/server/Services/Plugins.ts @@ -627,9 +627,6 @@ export async function getMainQueueModule(): Promise { return null; } -/** - * TODO - */ export async function getMainFilesystemModule(): Promise< Module | null | undefined > { diff --git a/packages/server/Services/Settings.ts b/packages/server/Services/Settings.ts index 0699bc50..541a438b 100644 --- a/packages/server/Services/Settings.ts +++ b/packages/server/Services/Settings.ts @@ -44,6 +44,7 @@ export async function checkMasterPassword( masterPassword, settings.master_password || "", ); + return matches; } diff --git a/packages/server/main.ts b/packages/server/main.ts index 988a9898..dbc8d908 100644 --- a/packages/server/main.ts +++ b/packages/server/main.ts @@ -80,11 +80,11 @@ async function setupExpressRoutes() { app.use("/console", express.static(consolePath, { index: "./index.html" })); - app.get("/", (req, res) => { + app.get("/", (_, res) => { res.redirect("/console"); }); - app.get("/console/*", (req, res) => { + app.get("/console/*", (_, res) => { res.header("content-type", "text/html"); res.sendFile(`${consolePath}/index.html`); }); @@ -92,7 +92,7 @@ async function setupExpressRoutes() { app.get("/images/:plugin/:type/:identifier?", resolvePluginImage); app.get("/images2/:plugin/*", resolvePluginImage2); - app.options("*", (req, res) => { + app.options("*", (_, res) => { const defaultHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,OPTIONS", @@ -104,7 +104,7 @@ async function setupExpressRoutes() { res.set(defaultHeaders); }); - app.use((err, req, res, next) => { + app.use((err, _, res, next) => { if (err.stack.match(/body-parser/)) { res.status(400).send({ status: false, @@ -133,7 +133,7 @@ async function setupPluginPages() { app.use(fullRoute, express.static(assetsPath, { index: "./index.html" })); } - app.use((req, res) => { + app.use((_, res) => { res.status(404); res.send({ status: false, message: "Route Not Found" }); }); diff --git a/plugins/mqtt/src/Events/onConnect.ts b/plugins/mqtt/src/Events/onConnect.ts index 1a58be8a..11e83266 100644 --- a/plugins/mqtt/src/Events/onConnect.ts +++ b/plugins/mqtt/src/Events/onConnect.ts @@ -112,7 +112,8 @@ async function onConnect( connection.addConnection(client.channel, client); console.debug(`Device connected [${client.device.id}]`); - // TODO: ADD Live Inspector support. + + // TODO:(rc) decide what to do about adding live inspector logging support // deviceInspector(client.device.id, client.connID, '[MQTT] Device connected', `Token Ending: ${String(tokenResult.tokenObj.token).slice(-5)} Client-ID: ${packet.clientId} Will-Message: ${!!client.will}`); client.connack({ returnCode: 0, reasonCode: 0 }); diff --git a/plugins/mqtt/src/Events/onDisconnect.ts b/plugins/mqtt/src/Events/onDisconnect.ts index dcc892f5..6b7c0c45 100644 --- a/plugins/mqtt/src/Events/onDisconnect.ts +++ b/plugins/mqtt/src/Events/onDisconnect.ts @@ -10,9 +10,9 @@ async function onDisconnect( connection.delConnection(client.channel as string, client.connID as string); if (!client.disconnected) { - // TODO: Log to Device Inspector + // TODO:(rc) decide what to do about adding live inspector logging support // deviceInspector(client.device.id, client.connID, '[MQTT] Device disconnected', `socket event: ${reason}`); - // TODO: Trigger Action for MQTT Disconnect + // TODO:(rc) decide what to do about triggering action for MQTT disconnect // const actionObj = { // id: client.device.id, // client_id: client.clientId, @@ -30,7 +30,7 @@ async function onDisconnect( JSON.stringify(client.will), ); - // TODO: Log to Device Inspector + // TODO:(rc) decide what to do about adding live inspector logging support // deviceInspector(client.device.id, client.connID, '[MQTT] Device executed will message', JSON.stringify(client.will)); } diff --git a/plugins/mqtt/src/Events/onPublish.ts b/plugins/mqtt/src/Events/onPublish.ts index 981e54b0..c2d5ae64 100644 --- a/plugins/mqtt/src/Events/onPublish.ts +++ b/plugins/mqtt/src/Events/onPublish.ts @@ -93,10 +93,10 @@ async function onPublish( messageId, }); - // TODO: Log to device inspector + // TODO:(rc) decide what to do about adding live inspector logging support // deviceInspector(client.device.id, client.connID, '[MQTT] Device publish', JSON.stringify(scope)); - // TODO: Update Device Last Input + // TODO:(rc) decide what to do about adding updating device last input // deviceProvider.updateLastTime(client.device.id, 'input'); } diff --git a/plugins/mqtt/src/Events/onSubscribe.ts b/plugins/mqtt/src/Events/onSubscribe.ts index c450a24a..e690b3dc 100644 --- a/plugins/mqtt/src/Events/onSubscribe.ts +++ b/plugins/mqtt/src/Events/onSubscribe.ts @@ -42,7 +42,7 @@ async function onSubscribe(client: ITagoIOClient, packet: ISubscribePacket) { } } - // TODO: Log to device inspector + // TODO:(rc) decide what to do about adding live inspector logging support // deviceInspector(client.device.id, client.connID, '[MQTT] Device subscribe', `topic: ${subscription.topic}`); } } diff --git a/plugins/mqtt/src/Events/onUnsubscribe.ts b/plugins/mqtt/src/Events/onUnsubscribe.ts index e0605372..a936dd73 100644 --- a/plugins/mqtt/src/Events/onUnsubscribe.ts +++ b/plugins/mqtt/src/Events/onUnsubscribe.ts @@ -13,7 +13,7 @@ async function onUnsubscribe( client.suback({ granted: [packet.qos], messageId: packet.messageId }); - // TODO: Log to device inspector + // TODO:(rc) decide what to do about adding live inspector logging support // for (const unsubscription of packet.unsubscriptions) { // deviceInspector(client.device.id, client.connID, '[MQTT] Device unsubscribe', `topic: ${unsubscription}`); // } diff --git a/plugins/mqtt/src/index.ts b/plugins/mqtt/src/index.ts index 2118e3e5..acd7ba01 100644 --- a/plugins/mqtt/src/index.ts +++ b/plugins/mqtt/src/index.ts @@ -266,7 +266,7 @@ const MQTTService = new ServiceModule({ defaultValue: 1883, }, - // TODO: Add CA signed Certificate + // TODO:(rc) decide what to do about adding CA signed certificates // { name: "Certificate", field: "cert", type: "file" }, ], }); diff --git a/plugins/mysql/src/Database/index.ts b/plugins/mysql/src/Database/index.ts index fbfa54c8..459e424d 100644 --- a/plugins/mysql/src/Database/index.ts +++ b/plugins/mysql/src/Database/index.ts @@ -21,7 +21,7 @@ const deviceDB = {} as IDatabaseConnection; const migrationConfig: Knex.MigratorConfig = { tableName: "migrations", directory: path.join(dirname__, "..", "Migrations"), - // ! FIX Error: The migration directory is corrupt, the following files are missing: + // TODO:(rc) there was a "FIX Error: migration directory is corrupt" here, decide what to do later in the RC disableMigrationsListValidation: true, }; diff --git a/plugins/mysql/src/Helpers/DeviceDatabase.ts b/plugins/mysql/src/Helpers/DeviceDatabase.ts index bfa71cef..a5fc0dc4 100644 --- a/plugins/mysql/src/Helpers/DeviceDatabase.ts +++ b/plugins/mysql/src/Helpers/DeviceDatabase.ts @@ -36,7 +36,7 @@ export async function getDeviceConnection(id: TGenericID, type: TDeviceType) { }); } - // TODO [>=1.0.0]: remove it + // TODO:(rc) add reminder to remove this in v1 if ( type === "mutable" && !(await deviceDB.write.schema.hasColumn(id, "updated_at")) diff --git a/plugins/mysql/src/Helpers/PluginConnection.ts b/plugins/mysql/src/Helpers/PluginConnection.ts index d2185a24..34758d2e 100644 --- a/plugins/mysql/src/Helpers/PluginConnection.ts +++ b/plugins/mysql/src/Helpers/PluginConnection.ts @@ -15,7 +15,7 @@ export async function getPluginConnection(id: string) { }); } - // TODO [>=1.0.0]: Remove it + // TODO:(rc) add reminder to remove this in v1 await fixKeyIndex(tableName); const connection = { diff --git a/plugins/mysql/src/Providers/DeviceData/getDeviceDataCount.ts b/plugins/mysql/src/Providers/DeviceData/getDeviceDataCount.ts index 40eaffa5..d88c059b 100644 --- a/plugins/mysql/src/Providers/DeviceData/getDeviceDataCount.ts +++ b/plugins/mysql/src/Providers/DeviceData/getDeviceDataCount.ts @@ -41,7 +41,6 @@ async function getDeviceDataCount( knexQuery.whereIn("variable", variables); } - console.log(knexQuery.toQuery()); const data = await knexQuery; return Number(data?.amount || 0); diff --git a/plugins/postgres/src/Database/index.ts b/plugins/postgres/src/Database/index.ts index 5f86aedb..4a2866cb 100644 --- a/plugins/postgres/src/Database/index.ts +++ b/plugins/postgres/src/Database/index.ts @@ -37,7 +37,7 @@ const deviceDB = {} as IDatabaseConnection; const migrationConfig: Knex.MigratorConfig = { tableName: "migrations", directory: path.join(dirname__, "..", "Migrations"), - // ! FIX Error: The migration directory is corrupt, the following files are missing: + // TODO:(rc) there was a "FIX Error: migration directory is corrupt" here, decide what to do later in the RC disableMigrationsListValidation: true, }; diff --git a/plugins/tagoio-integration/index.html b/plugins/tagoio-integration/index.html index 6fcad7cf..e52ade38 100644 --- a/plugins/tagoio-integration/index.html +++ b/plugins/tagoio-integration/index.html @@ -9,12 +9,6 @@
- diff --git a/plugins/tagoio-integration/src/front/App.tsx b/plugins/tagoio-integration/src/front/App.tsx index 304decfe..3c23666f 100644 --- a/plugins/tagoio-integration/src/front/App.tsx +++ b/plugins/tagoio-integration/src/front/App.tsx @@ -51,10 +51,7 @@ function App() { } }, [port, fetchTCore]); - /** - * TODO: - * Tries to fetch tcore once we load the app. - */ + // TODO:(rc) decide what to do about this later in the RC // useEffect(() => { // const portConfig = data?.modules?.find((x) => x.id === "navbar-button")?.configs?.[0]; // if (portConfig?.type == "number") { diff --git a/plugins/tagoio-integration/vite.config.ts b/plugins/tagoio-integration/vite.config.ts index e6c28201..9db1e0a1 100644 --- a/plugins/tagoio-integration/vite.config.ts +++ b/plugins/tagoio-integration/vite.config.ts @@ -12,7 +12,6 @@ const buildPath = join(dirname(configFilePath), "./build/front"); const TAGOIO_API = "https://api.tago.io"; const TAGOIO_REALTIME = "https://realtime.tago.io"; -// FIXME:(klaus) external dep 'path'? 'eventsource'? // https://vite.dev/config/ export default defineConfig({ plugins: [ @@ -23,7 +22,6 @@ export default defineConfig({ }), react(), ], - // FIXME::(klaus) test it base: "/pages/tagoio-integration", esbuild: { target: ["chrome58", "safari11"], @@ -31,15 +29,11 @@ export default defineConfig({ build: { minify: true, sourcemap: false, - // FIXME:(klaus) ./build/front/index.js ? - // FIXME:(klaus) ./build/front/index.html ? outDir: buildPath, }, - // FIXME:(klaus) see what tests this has, fix config if needed test: { environment: "jsdom", globals: true, - // setupFiles: ["./utils/setup-tests.ts"], }, define: { "process.env.TAGOIO_API": `"${TAGOIO_API}"`,