mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 07:25:48 +01:00
Add config migration process
This commit is contained in:
@@ -128,9 +128,9 @@
|
||||
{
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990",
|
||||
"name": "Donate",
|
||||
"url": "https://homarr.dev/docs/community/donate",
|
||||
"url": "https://ko-fi.com/ajnart",
|
||||
"behaviour": {
|
||||
"onClickUrl": "https://homarr.dev/docs/community/donate",
|
||||
"onClickUrl": "https://ko-fi.com/ajnart",
|
||||
"isOpeningInNewTab": true
|
||||
},
|
||||
"network": {
|
||||
@@ -140,7 +140,7 @@
|
||||
]
|
||||
},
|
||||
"appearance": {
|
||||
"iconUrl": "/imgs/logo/logo.png"
|
||||
"iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png"
|
||||
},
|
||||
"integration": {
|
||||
"type": null,
|
||||
|
||||
@@ -62,7 +62,7 @@ export const EditAppModal = ({
|
||||
},
|
||||
},
|
||||
behaviour: {
|
||||
onClickUrl: (url: string) => {
|
||||
externalUrl: (url: string) => {
|
||||
if (url === undefined || url.length < 1) {
|
||||
return null;
|
||||
}
|
||||
@@ -158,7 +158,7 @@ export const EditAppModal = ({
|
||||
{t('tabs.general')}
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
rightSection={<ValidationErrorIndicator keys={['behaviour.onClickUrl']} />}
|
||||
rightSection={<ValidationErrorIndicator keys={['behaviour.externalUrl']} />}
|
||||
icon={<IconClick size={14} />}
|
||||
value="behaviour"
|
||||
>
|
||||
|
||||
@@ -35,7 +35,7 @@ export const GeneralTab = ({ form, openTab }: GeneralTabProps) => {
|
||||
required
|
||||
{...form.getInputProps('url')}
|
||||
onChange={(e) => {
|
||||
form.setFieldValue('behaviour.onClickUrl', e.target.value);
|
||||
form.setFieldValue('behaviour.externalUrl', e.target.value);
|
||||
form.setFieldValue('url', e.target.value);
|
||||
}}
|
||||
/>
|
||||
@@ -46,7 +46,7 @@ export const GeneralTab = ({ form, openTab }: GeneralTabProps) => {
|
||||
placeholder="https://homarr.mywebsite.com/"
|
||||
variant="default"
|
||||
mb="md"
|
||||
{...form.getInputProps('behaviour.onClickUrl')}
|
||||
{...form.getInputProps('behaviour.externalUrl')}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
);
|
||||
|
||||
@@ -43,7 +43,7 @@ export const AvailableElementTypes = ({
|
||||
},
|
||||
behaviour: {
|
||||
isOpeningNewTab: true,
|
||||
onClickUrl: '',
|
||||
externalUrl: '',
|
||||
},
|
||||
area: {
|
||||
type: 'sidebar',
|
||||
|
||||
@@ -18,7 +18,8 @@ interface WrapperContentProps {
|
||||
};
|
||||
}
|
||||
|
||||
export const WrapperContent = ({ apps, refs, widgets }: WrapperContentProps) => (
|
||||
export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) {
|
||||
return (
|
||||
<>
|
||||
{apps?.map((app) => {
|
||||
const { component: TileComponent, ...tile } = Tiles.app;
|
||||
@@ -60,3 +61,4 @@ export const WrapperContent = ({ apps, refs, widgets }: WrapperContentProps) =>
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ export function Search() {
|
||||
label: app.name,
|
||||
value: app.name,
|
||||
icon: app.appearance.iconUrl,
|
||||
url: app.behaviour.onClickUrl ?? app.url,
|
||||
url: app.behaviour.externalUrl ?? app.url,
|
||||
}));
|
||||
const AutoCompleteItem = forwardRef<HTMLDivElement, any>(
|
||||
({ label, value, icon, url, ...others }: any, ref) => (
|
||||
|
||||
@@ -179,7 +179,7 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction
|
||||
},
|
||||
behaviour: {
|
||||
isOpeningNewTab: true,
|
||||
onClickUrl: '',
|
||||
externalUrl: '',
|
||||
},
|
||||
area: {
|
||||
type: 'wrapper', // TODO: Set the wrapper automatically
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { BackendConfigType, ConfigType } from '../../types/config';
|
||||
import { BackendConfigType } from '../../types/config';
|
||||
import { configExists } from './configExists';
|
||||
import { getFallbackConfig } from './getFallbackConfig';
|
||||
import { migrateConfig } from './migrateConfig';
|
||||
import { readConfig } from './readConfig';
|
||||
|
||||
export const getConfig = (name: string): BackendConfigType => {
|
||||
if (!configExists(name)) return getFallbackConfig();
|
||||
return readConfig(name);
|
||||
// Else if config exists but contains no "schema_version" property
|
||||
// then it is an old config file and we should try to migrate it
|
||||
// to the new format.
|
||||
let config = readConfig(name);
|
||||
if (!config.schemaVersion) {
|
||||
// TODO: Migrate config to new format
|
||||
config = migrateConfig(config);
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
||||
81
src/tools/config/migrateConfig.ts
Normal file
81
src/tools/config/migrateConfig.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { ConfigType } from '../../types/config';
|
||||
import { Config } from '../types';
|
||||
|
||||
export function migrateConfig(config: Config): ConfigType {
|
||||
const newConfig: ConfigType = {
|
||||
schemaVersion: '1.0.0',
|
||||
configProperties: {
|
||||
name: config.name ?? 'default',
|
||||
},
|
||||
categories: [],
|
||||
widgets: [],
|
||||
apps: [],
|
||||
settings: {
|
||||
common: {
|
||||
searchEngine: {
|
||||
type: 'google',
|
||||
properties: {
|
||||
enabled: true,
|
||||
openInNewTab: true,
|
||||
},
|
||||
},
|
||||
defaultConfig: 'default',
|
||||
},
|
||||
customization: {
|
||||
colors: {},
|
||||
layout: {
|
||||
enabledDocker: false,
|
||||
enabledLeftSidebar: false,
|
||||
enabledPing: false,
|
||||
enabledRightSidebar: false,
|
||||
enabledSearchbar: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
wrappers: [
|
||||
{
|
||||
id: 'default',
|
||||
position: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
newConfig.apps = config.services.map((s, idx) => ({
|
||||
name: s.name,
|
||||
id: s.id,
|
||||
url: s.url,
|
||||
behaviour: {
|
||||
isOpeningNewTab: s.newTab ?? true,
|
||||
externalUrl: s.openedUrl ?? '',
|
||||
},
|
||||
network: {
|
||||
enabledStatusChecker: s.ping ?? true,
|
||||
okStatus: s.status?.map((str) => parseInt(str, 10)) ?? [200],
|
||||
},
|
||||
appearance: {
|
||||
iconUrl: s.icon,
|
||||
},
|
||||
integration: {
|
||||
type: null,
|
||||
properties: [],
|
||||
},
|
||||
area: {
|
||||
type: 'wrapper',
|
||||
properties: {
|
||||
id: 'default',
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
location: {
|
||||
x: (idx * 3) % 18,
|
||||
y: Math.floor((idx / 6)) * 3,
|
||||
},
|
||||
size: {
|
||||
width: 3,
|
||||
height: 3,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
return newConfig;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export interface Settings {
|
||||
|
||||
export interface Config {
|
||||
name: string;
|
||||
apps: serviceItem[];
|
||||
services: serviceItem[];
|
||||
settings: Settings;
|
||||
modules: {
|
||||
[key: string]: ConfigModule;
|
||||
|
||||
@@ -16,7 +16,7 @@ export type ConfigAppType = Omit<AppType, 'integration'> & {
|
||||
};
|
||||
|
||||
interface AppBehaviourType {
|
||||
onClickUrl: string;
|
||||
externalUrl: string;
|
||||
isOpeningNewTab: boolean;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user