diff --git a/.gitignore b/.gitignore index 1731b2f41..66a0673f5 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,7 @@ data/configs !.yarn/plugins !.yarn/releases !.yarn/sdks -!.yarn/versions \ No newline at end of file +!.yarn/versions + +#envfiles +.env \ No newline at end of file diff --git a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx index 936779bff..cd175fa99 100644 --- a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx +++ b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx @@ -10,10 +10,10 @@ import { HoverCard, Kbd, Modal, - Stack, Table, Text, Title, + Tooltip, } from '@mantine/core'; import { IconAnchor, @@ -36,6 +36,7 @@ import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; import { useEditModeInformationStore } from '../../../../hooks/useEditModeInformation'; import { usePackageAttributesStore } from '../../../../tools/client/zustands/usePackageAttributesStore'; +import { useColorTheme } from '../../../../tools/color'; import Tip from '../../../layout/Tip'; import { usePrimaryGradient } from '../../../layout/useGradient'; import Credits from '../../../Settings/Common/Credits'; @@ -198,9 +199,9 @@ interface ExtendedInitOptions extends InitOptions { } const useInformationTableItems = (newVersionAvailable?: string): InformationTableItem[] => { - const colorGradiant = usePrimaryGradient(); const { attributes } = usePackageAttributesStore(); const { editModeEnabled } = useEditModeInformationStore(); + const { primaryColor } = useColorTheme(); const { configVersion } = useConfigContext(); const { configs } = useConfigStore(); @@ -214,15 +215,19 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl icon: , label: 'experimental_disableEditMode', content: ( - + WARNING - - This is an experimental feature, where the edit mode is disabled entirely - no config - modifications are possbile anymore. All update requests for the config will be dropped - on the API. This will be removed in future versions, as Homarr will receive a proper - authentication system, which will make this obsolete. - - + ), }, ]; @@ -238,7 +243,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl icon: , label: 'i18n', content: ( - + {usedI18nNamespaces.length} ), @@ -247,7 +252,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl icon: , label: 'locales', content: ( - + {initOptions.locales.length} ), @@ -260,7 +265,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl icon: , label: 'configurationSchemaVersion', content: ( - + {configVersion} ), @@ -269,7 +274,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl icon: , label: 'configurationsCount', content: ( - + {configs.length} ), @@ -279,7 +284,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl label: 'version', content: ( - + {attributes.packageVersion ?? 'Unknown'} {newVersionAvailable && ( @@ -319,7 +324,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl icon: , label: 'nodeEnvironment', content: ( - + {attributes.environment} ), diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index 44bb12b51..5eeadc149 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -42,7 +42,7 @@ export function Header(props: any) { > {!editModeEnabled && } - + {!editModeEnabled && } + {!editModeEnabled && ( } onClick={drawer.open}> diff --git a/src/components/layout/header/SettingsMenu/EditModeToggle.tsx b/src/components/layout/header/SettingsMenu/EditModeToggle.tsx new file mode 100644 index 000000000..fed323e8c --- /dev/null +++ b/src/components/layout/header/SettingsMenu/EditModeToggle.tsx @@ -0,0 +1,78 @@ +import { Button, Code, Menu, PasswordInput, Stack, Text } from '@mantine/core'; +import { useForm } from '@mantine/form'; +import { openModal } from '@mantine/modals'; +import { showNotification } from '@mantine/notifications'; +import { IconEdit, IconEditOff } from '@tabler/icons'; +import axios from 'axios'; +import { useEditModeInformationStore } from '../../../../hooks/useEditModeInformation'; + +function ModalContent() { + const form = useForm({ + initialValues: { + triedPassword: '', + }, + }); + return ( +
{ + axios + .post('/api/configs/tryPassword', { tried: values.triedPassword, type: 'edit' }) + .then((res) => { + showNotification({ + title: 'Success', + message: 'Successfully toggled edit mode, reloading the page...', + color: 'green', + }); + setTimeout(() => { + window.location.reload(); + }, 500); + }) + .catch((_) => { + showNotification({ + title: 'Error', + message: 'Failed to toggle edit mode, please try again.', + color: 'red', + }); + }); + })} + > + + + In order to toggle edit mode, you need to enter the password you entered in the + environment variable named EDIT_MODE_PASSWORD . If it is not set, you are not + able to toggle edit mode on and off. + + + + +
+ ); +} + +export function EditModeToggle() { + const { editModeEnabled } = useEditModeInformationStore(); + const Icon = editModeEnabled ? IconEdit : IconEditOff; + + return ( + } + onClick={() => + openModal({ + title: 'Toggle edit mode', + centered: true, + size: 'lg', + children: , + }) + } + > + {editModeEnabled ? 'Enable edit mode' : 'Disable edit mode'} + + ); +} diff --git a/src/modules/Docker/DockerModule.tsx b/src/modules/Docker/DockerModule.tsx index 91de2247b..1948c60a3 100644 --- a/src/modules/Docker/DockerModule.tsx +++ b/src/modules/Docker/DockerModule.tsx @@ -54,7 +54,7 @@ export default function DockerMenuButton(props: any) { }, 300); } - if (!dockerEnabled) { + if (!dockerEnabled || process.env.DISABLE_EDIT_MODE === 'true') { return null; } diff --git a/src/pages/api/configs/tryPassword.tsx b/src/pages/api/configs/tryPassword.tsx index 381845abf..1632985ab 100644 --- a/src/pages/api/configs/tryPassword.tsx +++ b/src/pages/api/configs/tryPassword.tsx @@ -2,26 +2,31 @@ import Consola from 'consola'; import { NextApiRequest, NextApiResponse } from 'next'; function Post(req: NextApiRequest, res: NextApiResponse) { - const { tried } = req.body; - // Try to match the password with the PASSWORD env variable - if (tried === process.env.PASSWORD) { + const { tried, type = 'password' } = req.body; + // If the type of password is "edit", we run this branch to check the edit password + if (type === 'edit') { + if (tried === process.env.EDIT_MODE_PASSWORD) { + process.env.DISABLE_EDIT_MODE = process.env.DISABLE_EDIT_MODE === 'true' ? 'false' : 'true'; + return res.status(200).json({ + success: true, + }); + } + } else if (tried === process.env.PASSWORD) { return res.status(200).json({ success: true, }); } - // Warn that there was a wrong password attempt (date : wrong password, person's IP) Consola.warn( `${new Date().toLocaleString()} : Wrong password attempt, from ${ req.headers['x-forwarded-for'] }` ); - return res.status(200).json({ + return res.status(401).json({ success: false, }); } export default async (req: NextApiRequest, res: NextApiResponse) => { - // Filter out if the request is a POST or a GET if (req.method === 'POST') { return Post(req, res); } diff --git a/vercel.json b/vercel.json new file mode 100644 index 000000000..77cfe5754 --- /dev/null +++ b/vercel.json @@ -0,0 +1,6 @@ +{ + "env": { + "EDIT_MODE_PASSWORD": "edit", + "DISABLE_EDIT_MODE": "TRUE" + } +} \ No newline at end of file