diff --git a/data/constants.ts b/data/constants.ts index b1ff95a49..b10f63d7e 100644 --- a/data/constants.ts +++ b/data/constants.ts @@ -1,3 +1,2 @@ export const REPO_URL = 'ajnart/homarr'; -export const CURRENT_VERSION = 'v0.11.3'; export const ICON_PICKER_SLICE_LIMIT = 36; diff --git a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx index 6ec6b6536..905085e47 100644 --- a/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx +++ b/src/components/Dashboard/Modals/AboutModal/AboutModal.tsx @@ -14,6 +14,7 @@ import { Title, } from '@mantine/core'; import { + IconAnchor, IconBrandDiscord, IconBrandGithub, IconFile, @@ -28,11 +29,11 @@ import { InitOptions } from 'i18next'; import { i18n, Trans, useTranslation } from 'next-i18next'; import Image from 'next/image'; import { ReactNode } from 'react'; -import { CURRENT_VERSION } from '../../../../../data/constants'; -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; -import { usePrimaryGradient } from '../../../layout/useGradient'; -import Credits from '../../../Settings/Common/Credits'; +import { useConfigContext } from '../../config/provider'; +import { useConfigStore } from '../../config/store'; +import { usePackageAttributesStore } from '../../tools/client/zustands/usePackageAttributesStore'; +import { usePrimaryGradient } from '../layout/useGradient'; +import Credits from '../Settings/Common/Credits'; interface AboutModalProps { opened: boolean; @@ -143,6 +144,7 @@ interface ExtendedInitOptions extends InitOptions { const useInformationTableItems = (newVersionAvailable?: string): InformationTableItem[] => { // TODO: Fix this to not request. Pass it as a prop. const colorGradiant = usePrimaryGradient(); + const { attributes } = usePackageAttributesStore(); const { configVersion } = useConfigContext(); const { configs } = useConfigStore(); @@ -201,7 +203,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl content: ( - {CURRENT_VERSION} + {attributes.packageVersion ?? 'Unknown'} {newVersionAvailable && ( @@ -229,13 +231,22 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl {newVersionAvailable} {' '} - is available ! Current version: {CURRENT_VERSION} + is available ! Current version: {attributes.packageVersion} )} ), }, + { + icon: , + label: 'Node environment', + content: ( + + {attributes.environment} + + ), + }, ...items, ]; diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx index f74ab7d60..d8795d25f 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx @@ -1,4 +1,4 @@ -import { Autocomplete, createStyles, Flex, Tabs, TextInput } from '@mantine/core'; +import { Autocomplete, createStyles, Flex, Tabs } from '@mantine/core'; import { UseFormReturnType } from '@mantine/form'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; diff --git a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx index 7225f3b5d..6be7e99a4 100644 --- a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx +++ b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx @@ -3,7 +3,6 @@ import { useConfigStore } from '../../../../config/store'; import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions'; import { AppType } from '../../../../types/app'; import { CategoryType } from '../../../../types/category'; -import { ConfigType } from '../../../../types/config'; import { WrapperType } from '../../../../types/wrapper'; import { IWidget } from '../../../../widgets/widgets'; import { CategoryEditModalInnerProps } from './CategoryEditModal'; diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index 45f432f34..ec7a9f33d 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -1,10 +1,10 @@ import { Box, createStyles, Group, Header as MantineHeader, Indicator } from '@mantine/core'; import { useEffect, useState } from 'react'; -import { CURRENT_VERSION, REPO_URL } from '../../../../data/constants'; -import { useConfigContext } from '../../../config/provider'; +import { REPO_URL } from '../../../../data/constants'; +import DockerMenuButton from '../../../modules/Docker/DockerModule'; +import { usePackageAttributesStore } from '../../../tools/client/zustands/usePackageAttributesStore'; import { Logo } from '../Logo'; import { useCardStyles } from '../useCardStyles'; -import DockerMenuButton from '../../../modules/Docker/DockerModule'; import { ToggleEditModeAction } from './Actions/ToggleEditMode/ToggleEditMode'; import { Search } from './Search'; import { SettingsMenu } from './SettingsMenu'; @@ -14,20 +14,19 @@ export const HeaderHeight = 64; export function Header(props: any) { const { classes } = useStyles(); const { classes: cardClasses } = useCardStyles(false); - - const { config } = useConfigContext(); + const { attributes } = usePackageAttributesStore(); const [newVersionAvailable, setNewVersionAvailable] = useState(''); useEffect(() => { // Fetch Data here when component first mounted fetch(`https://api.github.com/repos/${REPO_URL}/releases/latest`).then((res) => { res.json().then((data) => { - if (data.tag_name > CURRENT_VERSION) { + if (data.tag_name > `v${attributes.packageVersion}`) { setNewVersionAvailable(data.tag_name); } }); }); - }, [CURRENT_VERSION]); + }, []); return ( diff --git a/src/pages/[slug].tsx b/src/pages/[slug].tsx index 5a0876847..c3ac1b272 100644 --- a/src/pages/[slug].tsx +++ b/src/pages/[slug].tsx @@ -8,8 +8,8 @@ import Layout from '../components/layout/Layout'; import { useInitConfig } from '../config/init'; import { getFallbackConfig } from '../tools/config/getFallbackConfig'; import { getFrontendConfig } from '../tools/config/getFrontendConfig'; -import { getServerSideTranslations } from '../tools/getServerSideTranslations'; -import { dashboardNamespaces } from '../tools/translation-namespaces'; +import { getServerSideTranslations } from '../tools/server/getServerSideTranslations'; +import { dashboardNamespaces } from '../tools/server/translation-namespaces'; import { ConfigType } from '../types/config'; import { DashboardServerSideProps } from '../types/dashboardPageType'; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 088141e6c..b372436a0 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -8,7 +8,7 @@ import { GetServerSidePropsContext } from 'next'; import { appWithTranslation } from 'next-i18next'; import { AppProps } from 'next/app'; import Head from 'next/head'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal'; import { ChangeWidgetPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal'; @@ -22,8 +22,16 @@ import '../styles/global.scss'; import { ColorTheme } from '../tools/color'; import { queryClient } from '../tools/queryClient'; import { theme } from '../tools/theme'; +import { + getServiceSidePackageAttributes, + ServerSidePackageAttributesType, +} from '../tools/server/getPackageVersion'; +import { usePackageAttributesStore } from '../tools/client/zustands/usePackageAttributesStore'; -function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { +function App( + this: any, + props: AppProps & { colorScheme: ColorScheme; packageAttributes: ServerSidePackageAttributesType } +) { const { Component, pageProps } = props; const [primaryColor, setPrimaryColor] = useState('red'); const [secondaryColor, setSecondaryColor] = useState('orange'); @@ -46,6 +54,12 @@ function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { getInitialValueInEffect: true, }); + const { setInitialPackageAttributes } = usePackageAttributesStore(); + + useEffect(() => { + setInitialPackageAttributes(props.packageAttributes); + }, []); + const toggleColorScheme = (value?: ColorScheme) => setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark')); @@ -111,6 +125,7 @@ function App(this: any, props: AppProps & { colorScheme: ColorScheme }) { App.getInitialProps = ({ ctx }: { ctx: GetServerSidePropsContext }) => ({ colorScheme: getCookie('color-scheme', ctx) || 'light', + packageAttributes: getServiceSidePackageAttributes(), }); export default appWithTranslation(App); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index dd416158b..5b1f60d8a 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,15 +1,15 @@ import { getCookie, setCookie } from 'cookies-next'; +import fs from 'fs'; import { GetServerSidePropsContext } from 'next'; -import fs from 'fs'; +import { LoadConfigComponent } from '../components/Config/LoadConfig'; import { Dashboard } from '../components/Dashboard/Dashboard'; import Layout from '../components/layout/Layout'; import { useInitConfig } from '../config/init'; import { getFrontendConfig } from '../tools/config/getFrontendConfig'; -import { getServerSideTranslations } from '../tools/getServerSideTranslations'; -import { dashboardNamespaces } from '../tools/translation-namespaces'; +import { getServerSideTranslations } from '../tools/server/getServerSideTranslations'; +import { dashboardNamespaces } from '../tools/server/translation-namespaces'; import { DashboardServerSideProps } from '../types/dashboardPageType'; -import { LoadConfigComponent } from '../components/Config/LoadConfig'; export async function getServerSideProps({ req, @@ -47,11 +47,14 @@ export async function getServerSideProps({ } const translations = await getServerSideTranslations(req, res, dashboardNamespaces, locale); - const config = getFrontendConfig(configName as string); return { - props: { configName: configName as string, config, ...translations }, + props: { + configName: configName as string, + config, + ...translations, + }, }; } diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 7587bbde0..bf414ed83 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -8,7 +8,7 @@ import { useRouter } from 'next/router'; import { useTranslation } from 'next-i18next'; import { useForm } from '@mantine/form'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; -import { loginNamespaces } from '../tools/translation-namespaces'; +import { loginNamespaces } from '../tools/server/translation-namespaces'; // TODO: Add links to the wiki articles about the login process. export default function AuthenticationTitle() { diff --git a/src/tools/addToHomarr.ts b/src/tools/addToHomarr.ts index 5f97bf49d..bb340ba4e 100644 --- a/src/tools/addToHomarr.ts +++ b/src/tools/addToHomarr.ts @@ -1,14 +1,5 @@ import Dockerode from 'dockerode'; -import { Config, MatchingImages, ServiceType, tryMatchPort } from './types'; - -async function MatchIcon(name: string) { - const res = await fetch( - `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${name - .replace(/\s+/g, '-') - .toLowerCase()}.png` - ); - return res.ok ? res.url : '/imgs/favicon/favicon.png'; -} +import { MatchingImages, ServiceType, tryMatchPort } from './types'; function tryMatchType(imageName: string): ServiceType { // Try to find imageName inside MatchingImages diff --git a/src/tools/calculateEta.ts b/src/tools/client/calculateEta.ts similarity index 100% rename from src/tools/calculateEta.ts rename to src/tools/client/calculateEta.ts diff --git a/src/tools/parseDuration.ts b/src/tools/client/parseDuration.ts similarity index 100% rename from src/tools/parseDuration.ts rename to src/tools/client/parseDuration.ts diff --git a/src/tools/client/zustands/usePackageAttributesStore.ts b/src/tools/client/zustands/usePackageAttributesStore.ts new file mode 100644 index 000000000..6d710997d --- /dev/null +++ b/src/tools/client/zustands/usePackageAttributesStore.ts @@ -0,0 +1,15 @@ +import create from 'zustand'; + +import { ServerSidePackageAttributesType } from '../../server/getPackageVersion'; + +interface PackageAttributesState { + attributes: ServerSidePackageAttributesType; + setInitialPackageAttributes: (attributes: ServerSidePackageAttributesType) => void; +} + +export const usePackageAttributesStore = create((set) => ({ + attributes: { packageVersion: undefined, environment: 'test' }, + setInitialPackageAttributes(attributes) { + set((state) => ({ ...state, attributes })); + }, +})); diff --git a/src/tools/server/getPackageVersion.ts b/src/tools/server/getPackageVersion.ts new file mode 100644 index 000000000..0c74d56a7 --- /dev/null +++ b/src/tools/server/getPackageVersion.ts @@ -0,0 +1,14 @@ +const getServerPackageVersion = (): string | undefined => process.env.npm_package_version; + +const getServerNodeEnvironment = (): 'development' | 'production' | 'test' => + process.env.NODE_ENV; + +export const getServiceSidePackageAttributes = (): ServerSidePackageAttributesType => ({ + packageVersion: getServerPackageVersion(), + environment: getServerNodeEnvironment(), +}); + +export type ServerSidePackageAttributesType = { + packageVersion: string | undefined; + environment: 'development' | 'production' | 'test'; +}; diff --git a/src/tools/getServerSideTranslations.ts b/src/tools/server/getServerSideTranslations.ts similarity index 100% rename from src/tools/getServerSideTranslations.ts rename to src/tools/server/getServerSideTranslations.ts diff --git a/src/tools/translation-namespaces.ts b/src/tools/server/translation-namespaces.ts similarity index 100% rename from src/tools/translation-namespaces.ts rename to src/tools/server/translation-namespaces.ts diff --git a/src/types/dashboardPageType.ts b/src/types/dashboardPageType.ts index 416c3b1bf..f361e449f 100644 --- a/src/types/dashboardPageType.ts +++ b/src/types/dashboardPageType.ts @@ -1,4 +1,5 @@ import { SSRConfig } from 'next-i18next'; + import { ConfigType } from './config'; export type DashboardServerSideProps = { diff --git a/src/widgets/torrent/TorrentQueueItem.tsx b/src/widgets/torrent/TorrentQueueItem.tsx index 6ec0c1a71..6d7afd891 100644 --- a/src/widgets/torrent/TorrentQueueItem.tsx +++ b/src/widgets/torrent/TorrentQueueItem.tsx @@ -24,7 +24,7 @@ import { IconUpload, } from '@tabler/icons'; import { useTranslation } from 'next-i18next'; -import { calculateETA } from '../../tools/calculateEta'; +import { calculateETA } from '../../tools/client/calculateEta'; import { humanFileSize } from '../../tools/humanFileSize'; import { AppType } from '../../types/app'; diff --git a/src/widgets/useNet/UsenetHistoryList.tsx b/src/widgets/useNet/UsenetHistoryList.tsx index 28ee10ccf..2578fdec6 100644 --- a/src/widgets/useNet/UsenetHistoryList.tsx +++ b/src/widgets/useNet/UsenetHistoryList.tsx @@ -20,7 +20,7 @@ import { useTranslation } from 'next-i18next'; import { FunctionComponent, useState } from 'react'; import { useGetUsenetHistory } from '../../hooks/widgets/dashDot/api'; import { humanFileSize } from '../../tools/humanFileSize'; -import { parseDuration } from '../../tools/parseDuration'; +import { parseDuration } from '../../tools/client/parseDuration'; dayjs.extend(duration);