mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-11 07:55:52 +01:00
♻️ Add compability for legacy config in config loader
This commit is contained in:
@@ -57,9 +57,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"accept": {
|
"accept": {
|
||||||
|
"title": "Configuration Upload",
|
||||||
"text": "Drag files here to upload a config. Support for JSON only."
|
"text": "Drag files here to upload a config. Support for JSON only."
|
||||||
},
|
},
|
||||||
"reject": {
|
"reject": {
|
||||||
|
"title": "Drag and Drop Upload rejected",
|
||||||
"text": "This file format is not supported. Please only upload JSON."
|
"text": "This file format is not supported. Please only upload JSON."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,94 @@
|
|||||||
import { Group, Text, useMantineTheme } from '@mantine/core';
|
import { Group, Stack, Text, Title, useMantineTheme } from '@mantine/core';
|
||||||
import { Dropzone } from '@mantine/dropzone';
|
import { Dropzone } from '@mantine/dropzone';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { IconCheck as Check, IconPhoto, IconUpload, IconX, IconX as X } from '@tabler/icons';
|
import { IconCheck as Check, IconPhoto, IconUpload, IconX, IconX as X } from '@tabler/icons';
|
||||||
import { setCookie } from 'cookies-next';
|
import { setCookie } from 'cookies-next';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useConfigStore } from '../../config/store';
|
import { useConfigStore } from '../../config/store';
|
||||||
|
import { migrateConfig } from '../../tools/config/migrateConfig';
|
||||||
import { Config } from '../../tools/types';
|
import { Config } from '../../tools/types';
|
||||||
|
import { ConfigType } from '../../types/config';
|
||||||
|
|
||||||
export default function LoadConfigComponent(props: any) {
|
export const LoadConfigComponent = () => {
|
||||||
const { updateConfig } = useConfigStore();
|
const { addConfig } = useConfigStore();
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
const { t } = useTranslation('settings/general/config-changer');
|
const { t } = useTranslation('settings/general/config-changer');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropzone.FullScreen
|
<Dropzone.FullScreen
|
||||||
onDrop={(files) => {
|
onDrop={async (files) => {
|
||||||
files[0].text().then((e) => {
|
const fileName = files[0].name.replaceAll('.json', '');
|
||||||
try {
|
const fileText = await files[0].text();
|
||||||
JSON.parse(e) as Config;
|
|
||||||
} catch (e) {
|
try {
|
||||||
showNotification({
|
JSON.parse(fileText) as ConfigType;
|
||||||
autoClose: 5000,
|
} catch (e) {
|
||||||
title: <Text>{t('dropzone.notifications.invalidConfig.title')}</Text>,
|
|
||||||
color: 'red',
|
|
||||||
icon: <X />,
|
|
||||||
message: t('dropzone.notifications.invalidConfig.message'),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newConfig: Config = JSON.parse(e);
|
|
||||||
showNotification({
|
showNotification({
|
||||||
autoClose: 5000,
|
autoClose: 5000,
|
||||||
radius: 'md',
|
title: <Text>{t('dropzone.notifications.invalidConfig.title')}</Text>,
|
||||||
title: (
|
color: 'red',
|
||||||
<Text>
|
icon: <X />,
|
||||||
{t('dropzone.notifications.loadedSuccessfully.title', {
|
message: t('dropzone.notifications.invalidConfig.message'),
|
||||||
configName: newConfig.name,
|
|
||||||
})}
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
color: 'green',
|
|
||||||
icon: <Check />,
|
|
||||||
message: undefined,
|
|
||||||
});
|
});
|
||||||
setCookie('config-name', newConfig.name, {
|
return;
|
||||||
maxAge: 60 * 60 * 24 * 30,
|
}
|
||||||
sameSite: 'strict',
|
|
||||||
});
|
let newConfig: ConfigType = JSON.parse(fileText);
|
||||||
updateConfig(newConfig.name, (previousConfig) => ({ ...previousConfig, newConfig }));
|
|
||||||
|
if (!newConfig.schemaVersion) {
|
||||||
|
console.warn('a legacy configuration schema was deteced and migrated to the current schema');
|
||||||
|
const oldConfig = JSON.parse(fileText) as Config;
|
||||||
|
newConfig = migrateConfig(oldConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
await addConfig(fileName, newConfig, true);
|
||||||
|
showNotification({
|
||||||
|
autoClose: 5000,
|
||||||
|
radius: 'md',
|
||||||
|
title: (
|
||||||
|
<Text>
|
||||||
|
{t('dropzone.notifications.loadedSuccessfully.title', {
|
||||||
|
configName: fileName,
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
color: 'green',
|
||||||
|
icon: <Check />,
|
||||||
|
message: undefined,
|
||||||
|
});
|
||||||
|
setCookie('config-name', fileName, {
|
||||||
|
maxAge: 60 * 60 * 24 * 30,
|
||||||
|
sameSite: 'strict',
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
accept={['application/json']}
|
accept={['application/json']}
|
||||||
>
|
>
|
||||||
<Group position="center" spacing="xl" style={{ minHeight: 220, pointerEvents: 'none' }}>
|
<Group position="center" spacing="xl" style={{ minHeight: 220, pointerEvents: 'none' }}>
|
||||||
<Dropzone.Accept>
|
<Dropzone.Accept>
|
||||||
<Text size="xl" inline>
|
<Stack align="center">
|
||||||
<IconUpload
|
<IconUpload
|
||||||
size={50}
|
size={50}
|
||||||
stroke={1.5}
|
stroke={1.5}
|
||||||
color={theme.colors[theme.primaryColor][theme.colorScheme === 'dark' ? 4 : 6]}
|
color={theme.colors[theme.primaryColor][theme.colorScheme === 'dark' ? 4 : 6]}
|
||||||
/>
|
/>
|
||||||
{t('dropzone.accept.text')}
|
<Title>{t('dropzone.accept.title')}</Title>
|
||||||
</Text>
|
<Text size="xl" inline>
|
||||||
|
{t('dropzone.accept.text')}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
</Dropzone.Accept>
|
</Dropzone.Accept>
|
||||||
<Dropzone.Reject>
|
<Dropzone.Reject>
|
||||||
<Text size="xl" inline>
|
<Stack align="center">
|
||||||
<IconX
|
<IconX
|
||||||
size={50}
|
size={50}
|
||||||
stroke={1.5}
|
stroke={1.5}
|
||||||
color={theme.colors.red[theme.colorScheme === 'dark' ? 4 : 6]}
|
color={theme.colors.red[theme.colorScheme === 'dark' ? 4 : 6]}
|
||||||
/>
|
/>
|
||||||
{t('dropzone.reject.text')}
|
<Title>{t('dropzone.reject.title')}</Title>
|
||||||
</Text>
|
<Text size="xl" inline>
|
||||||
|
{t('dropzone.reject.text')}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
</Dropzone.Reject>
|
</Dropzone.Reject>
|
||||||
<Dropzone.Idle>
|
<Dropzone.Idle>
|
||||||
<IconPhoto size={50} stroke={1.5} />
|
<IconPhoto size={50} stroke={1.5} />
|
||||||
@@ -79,4 +96,4 @@ export default function LoadConfigComponent(props: any) {
|
|||||||
</Group>
|
</Group>
|
||||||
</Dropzone.FullScreen>
|
</Dropzone.FullScreen>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ export const WidgetsEditModal = ({
|
|||||||
|
|
||||||
if (!configName || !innerProps.options) return null;
|
if (!configName || !innerProps.options) return null;
|
||||||
|
|
||||||
console.log(`loaded namespace modules/${innerProps.widgetId}`);
|
|
||||||
|
|
||||||
const handleChange = (key: string, value: IntegrationOptionsValueType) => {
|
const handleChange = (key: string, value: IntegrationOptionsValueType) => {
|
||||||
setModuleProperties((prev) => {
|
setModuleProperties((prev) => {
|
||||||
const copyOfPrev: any = { ...prev };
|
const copyOfPrev: any = { ...prev };
|
||||||
|
|||||||
@@ -13,6 +13,20 @@ export const useConfigStore = create<UseConfigStoreType>((set, get) => ({
|
|||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
addConfig: async (name: string, config: ConfigType, shouldSaveConfigToFileSystem = true) => {
|
||||||
|
set((old) => ({
|
||||||
|
...old,
|
||||||
|
configs: [
|
||||||
|
...old.configs.filter((x) => x.value.configProperties.name !== name),
|
||||||
|
{ value: config, increaseVersion: () => {} },
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!shouldSaveConfigToFileSystem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
axios.put(`/api/configs/${name}`, { ...config });
|
||||||
|
},
|
||||||
updateConfig: async (
|
updateConfig: async (
|
||||||
name,
|
name,
|
||||||
updateCallback: (previous: ConfigType) => ConfigType,
|
updateCallback: (previous: ConfigType) => ConfigType,
|
||||||
@@ -21,7 +35,9 @@ export const useConfigStore = create<UseConfigStoreType>((set, get) => ({
|
|||||||
) => {
|
) => {
|
||||||
const { configs } = get();
|
const { configs } = get();
|
||||||
const currentConfig = configs.find((x) => x.value.configProperties.name === name);
|
const currentConfig = configs.find((x) => x.value.configProperties.name === name);
|
||||||
if (!currentConfig) return;
|
if (!currentConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// copies the value of currentConfig and creates a non reference object named previousConfig
|
// copies the value of currentConfig and creates a non reference object named previousConfig
|
||||||
const previousConfig: ConfigType = JSON.parse(JSON.stringify(currentConfig.value));
|
const previousConfig: ConfigType = JSON.parse(JSON.stringify(currentConfig.value));
|
||||||
|
|
||||||
@@ -51,6 +67,11 @@ export const useConfigStore = create<UseConfigStoreType>((set, get) => ({
|
|||||||
interface UseConfigStoreType {
|
interface UseConfigStoreType {
|
||||||
configs: { increaseVersion: () => void; value: ConfigType }[];
|
configs: { increaseVersion: () => void; value: ConfigType }[];
|
||||||
initConfig: (name: string, config: ConfigType, increaseVersion: () => void) => void;
|
initConfig: (name: string, config: ConfigType, increaseVersion: () => void) => void;
|
||||||
|
addConfig: (
|
||||||
|
name: string,
|
||||||
|
config: ConfigType,
|
||||||
|
shouldSaveConfigToFileSystem: boolean,
|
||||||
|
) => Promise<void>;
|
||||||
updateConfig: (
|
updateConfig: (
|
||||||
name: string,
|
name: string,
|
||||||
updateCallback: (previous: ConfigType) => ConfigType,
|
updateCallback: (previous: ConfigType) => ConfigType,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { GetServerSidePropsContext } from 'next';
|
import { GetServerSidePropsContext } from 'next';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import LoadConfigComponent from '../components/Config/LoadConfig';
|
import { LoadConfigComponent } from '../components/Config/LoadConfig';
|
||||||
import { Dashboard } from '../components/Dashboard/Dashboard';
|
import { Dashboard } from '../components/Dashboard/Dashboard';
|
||||||
import Layout from '../components/layout/Layout';
|
import Layout from '../components/layout/Layout';
|
||||||
import { useInitConfig } from '../config/init';
|
import { useInitConfig } from '../config/init';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import Consola from 'consola';
|
||||||
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { BackendConfigType, ConfigType } from '../../../types/config';
|
import { BackendConfigType, ConfigType } from '../../../types/config';
|
||||||
import { getConfig } from '../../../tools/config/getConfig';
|
import { getConfig } from '../../../tools/config/getConfig';
|
||||||
|
|
||||||
@@ -10,11 +11,14 @@ function Put(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
// Get the body of the request
|
// Get the body of the request
|
||||||
const { body: config }: { body: ConfigType } = req;
|
const { body: config }: { body: ConfigType } = req;
|
||||||
if (!slug || !config) {
|
if (!slug || !config) {
|
||||||
|
Consola.warn('Rejected configuration update because either config or slug were undefined');
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
error: 'Wrong request',
|
error: 'Wrong request',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Consola.info(`Saving updated configuration of '${slug}' config.`);
|
||||||
|
|
||||||
const previousConfig = getConfig(slug);
|
const previousConfig = getConfig(slug);
|
||||||
|
|
||||||
const newConfig: BackendConfigType = {
|
const newConfig: BackendConfigType = {
|
||||||
@@ -56,11 +60,11 @@ function Put(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Save the body in the /data/config folder with the slug as filename
|
// Save the body in the /data/config folder with the slug as filename
|
||||||
fs.writeFileSync(
|
const targetPath = path.join('data/configs', `${slug}.json`);
|
||||||
path.join('data/configs', `${slug}.json`),
|
fs.writeFileSync(targetPath, JSON.stringify(newConfig, null, 2), 'utf8');
|
||||||
JSON.stringify(newConfig, null, 2),
|
|
||||||
'utf8'
|
Consola.info(`Config '${slug}' has been updated and flushed to '${targetPath}'.`);
|
||||||
);
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: 'Configuration saved with success',
|
message: 'Configuration saved with success',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { getCookie, setCookie } from 'cookies-next';
|
|||||||
import { GetServerSidePropsContext } from 'next';
|
import { GetServerSidePropsContext } from 'next';
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import LoadConfigComponent from '../components/Config/LoadConfig';
|
import Consola from 'consola';
|
||||||
import { Dashboard } from '../components/Dashboard/Dashboard';
|
import { Dashboard } from '../components/Dashboard/Dashboard';
|
||||||
import Layout from '../components/layout/Layout';
|
import Layout from '../components/layout/Layout';
|
||||||
import { useInitConfig } from '../config/init';
|
import { useInitConfig } from '../config/init';
|
||||||
@@ -10,6 +10,7 @@ import { getFrontendConfig } from '../tools/config/getFrontendConfig';
|
|||||||
import { getServerSideTranslations } from '../tools/getServerSideTranslations';
|
import { getServerSideTranslations } from '../tools/getServerSideTranslations';
|
||||||
import { dashboardNamespaces } from '../tools/translation-namespaces';
|
import { dashboardNamespaces } from '../tools/translation-namespaces';
|
||||||
import { DashboardServerSideProps } from '../types/dashboardPageType';
|
import { DashboardServerSideProps } from '../types/dashboardPageType';
|
||||||
|
import { LoadConfigComponent } from '../components/Config/LoadConfig';
|
||||||
|
|
||||||
export async function getServerSideProps({
|
export async function getServerSideProps({
|
||||||
req,
|
req,
|
||||||
@@ -46,6 +47,9 @@ export async function getServerSideProps({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const translations = await getServerSideTranslations(req, res, dashboardNamespaces, locale);
|
const translations = await getServerSideTranslations(req, res, dashboardNamespaces, locale);
|
||||||
|
|
||||||
|
Consola.info(`Decided to use configuration '${configName}'`);
|
||||||
|
|
||||||
const config = getFrontendConfig(configName as string);
|
const config = getFrontendConfig(configName as string);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,45 +1,45 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { GetServerSidePropsContext } from 'next';
|
import { GetServerSidePropsContext } from 'next';
|
||||||
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createStyles,
|
Alert,
|
||||||
Title,
|
Anchor,
|
||||||
Text,
|
|
||||||
Container,
|
|
||||||
Group,
|
|
||||||
Stepper,
|
|
||||||
useMantineTheme,
|
|
||||||
Header,
|
|
||||||
AppShell,
|
AppShell,
|
||||||
useMantineColorScheme,
|
Badge,
|
||||||
Switch,
|
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Alert,
|
Container,
|
||||||
Badge,
|
createStyles,
|
||||||
|
Group,
|
||||||
|
Header,
|
||||||
List,
|
List,
|
||||||
Loader,
|
Loader,
|
||||||
Paper,
|
Paper,
|
||||||
Progress,
|
Progress,
|
||||||
Space,
|
Space,
|
||||||
Stack,
|
Stack,
|
||||||
|
Stepper,
|
||||||
|
Switch,
|
||||||
|
Text,
|
||||||
ThemeIcon,
|
ThemeIcon,
|
||||||
Anchor,
|
Title,
|
||||||
|
useMantineColorScheme,
|
||||||
|
useMantineTheme,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconSun,
|
|
||||||
IconMoonStars,
|
|
||||||
IconCheck,
|
|
||||||
IconAlertCircle,
|
IconAlertCircle,
|
||||||
IconCircleCheck,
|
|
||||||
IconBrandDiscord,
|
IconBrandDiscord,
|
||||||
|
IconCheck,
|
||||||
|
IconCircleCheck,
|
||||||
|
IconMoonStars,
|
||||||
|
IconSun,
|
||||||
} from '@tabler/icons';
|
} from '@tabler/icons';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Logo } from '../components/layout/Logo';
|
import { Logo } from '../components/layout/Logo';
|
||||||
import { usePrimaryGradient } from '../components/layout/useGradient';
|
import { usePrimaryGradient } from '../components/layout/useGradient';
|
||||||
import { migrateConfig } from '../tools/config/migrateConfig';
|
import { backendMigrateConfig } from '../tools/config/backendMigrateConfig';
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
root: {
|
root: {
|
||||||
@@ -274,7 +274,7 @@ export async function getServerSideProps({ req, res, locale }: GetServerSideProp
|
|||||||
const configData = JSON.parse(fs.readFileSync(`./data/configs/${config}`, 'utf8'));
|
const configData = JSON.parse(fs.readFileSync(`./data/configs/${config}`, 'utf8'));
|
||||||
if (!configData.schemaVersion) {
|
if (!configData.schemaVersion) {
|
||||||
// Migrate the config
|
// Migrate the config
|
||||||
migrateConfig(configData, config.replace('.json', ''));
|
backendMigrateConfig(configData, config.replace('.json', ''));
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|||||||
14
src/tools/config/backendMigrateConfig.ts
Normal file
14
src/tools/config/backendMigrateConfig.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import { ConfigType } from '../../types/config';
|
||||||
|
import { Config } from '../types';
|
||||||
|
import { migrateConfig } from './migrateConfig';
|
||||||
|
|
||||||
|
export function backendMigrateConfig(config: Config, name: string): ConfigType {
|
||||||
|
const migratedConfig = migrateConfig(config);
|
||||||
|
|
||||||
|
// Overrite the file ./data/configs/${name}.json
|
||||||
|
// with the new config format
|
||||||
|
fs.writeFileSync(`./data/configs/${name}.json`, JSON.stringify(migratedConfig, null, 2));
|
||||||
|
|
||||||
|
return migratedConfig;
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
import fs from 'fs';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import { AppType } from '../../types/app';
|
||||||
|
import { AreaType } from '../../types/area';
|
||||||
|
import { CategoryType } from '../../types/category';
|
||||||
import { ConfigType } from '../../types/config';
|
import { ConfigType } from '../../types/config';
|
||||||
import { Config } from '../types';
|
import { Config, serviceItem } from '../types';
|
||||||
|
|
||||||
export function migrateConfig(config: Config, name: string): ConfigType {
|
export function migrateConfig(config: Config): ConfigType {
|
||||||
const newConfig: ConfigType = {
|
const newConfig: ConfigType = {
|
||||||
schemaVersion: 1,
|
schemaVersion: 1,
|
||||||
configProperties: {
|
configProperties: {
|
||||||
@@ -41,45 +44,86 @@ export function migrateConfig(config: Config, name: string): ConfigType {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
newConfig.apps = config.services.map((s, idx) => ({
|
config.services.forEach((service, index) => {
|
||||||
name: s.name,
|
const { category: categoryName } = service;
|
||||||
id: s.id,
|
|
||||||
url: s.url,
|
if (!categoryName) {
|
||||||
behaviour: {
|
newConfig.apps.push(
|
||||||
isOpeningNewTab: s.newTab ?? true,
|
migrateService(service, index, {
|
||||||
externalUrl: s.openedUrl ?? '',
|
type: 'wrapper',
|
||||||
},
|
properties: {
|
||||||
network: {
|
id: 'default',
|
||||||
enabledStatusChecker: s.ping ?? true,
|
},
|
||||||
okStatus: s.status?.map((str) => parseInt(str, 10)) ?? [200],
|
})
|
||||||
},
|
);
|
||||||
appearance: {
|
return;
|
||||||
iconUrl: s.icon,
|
}
|
||||||
},
|
|
||||||
integration: {
|
const category = getConfigAndCreateIfNotExsists(newConfig, categoryName);
|
||||||
type: null,
|
|
||||||
properties: [],
|
if (!category) {
|
||||||
},
|
return;
|
||||||
area: {
|
}
|
||||||
type: 'wrapper',
|
|
||||||
properties: {
|
newConfig.apps.push(
|
||||||
id: 'default',
|
migrateService(service, index, { type: 'category', properties: { id: category.id } })
|
||||||
},
|
);
|
||||||
},
|
});
|
||||||
shape: {
|
|
||||||
location: {
|
|
||||||
x: (idx * 3) % 18,
|
|
||||||
y: Math.floor(idx / 6) * 3,
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
width: 3,
|
|
||||||
height: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
// Overrite the file ./data/configs/${name}.json
|
|
||||||
// with the new config format
|
|
||||||
fs.writeFileSync(`./data/configs/${name}.json`, JSON.stringify(newConfig, null, 2));
|
|
||||||
|
|
||||||
return newConfig;
|
return newConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getConfigAndCreateIfNotExsists = (
|
||||||
|
config: ConfigType,
|
||||||
|
categoryName: string
|
||||||
|
): CategoryType | null => {
|
||||||
|
const foundCategory = config.categories.find((c) => c.name === categoryName);
|
||||||
|
if (foundCategory) {
|
||||||
|
return foundCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
const category: CategoryType = {
|
||||||
|
id: uuidv4(),
|
||||||
|
name: categoryName,
|
||||||
|
position: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
config.categories.push(category);
|
||||||
|
return category;
|
||||||
|
};
|
||||||
|
|
||||||
|
const migrateService = (
|
||||||
|
oldService: serviceItem,
|
||||||
|
serviceIndex: number,
|
||||||
|
areaType: AreaType
|
||||||
|
): AppType => ({
|
||||||
|
id: uuidv4(),
|
||||||
|
name: oldService.name,
|
||||||
|
url: oldService.url,
|
||||||
|
behaviour: {
|
||||||
|
isOpeningNewTab: oldService.newTab ?? true,
|
||||||
|
externalUrl: oldService.openedUrl ?? '',
|
||||||
|
},
|
||||||
|
network: {
|
||||||
|
enabledStatusChecker: oldService.ping ?? true,
|
||||||
|
okStatus: oldService.status?.map((str) => parseInt(str, 10)) ?? [200],
|
||||||
|
},
|
||||||
|
appearance: {
|
||||||
|
iconUrl: oldService.icon,
|
||||||
|
},
|
||||||
|
integration: {
|
||||||
|
type: null,
|
||||||
|
properties: [],
|
||||||
|
},
|
||||||
|
area: areaType,
|
||||||
|
shape: {
|
||||||
|
location: {
|
||||||
|
x: (serviceIndex * 3) % 18,
|
||||||
|
y: Math.floor(serviceIndex / 6) * 3,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: 3,
|
||||||
|
height: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user