diff --git a/src/components/Dashboard/Modals/EditAppModal/EditAppModal.tsx b/src/components/Dashboard/Modals/EditAppModal/EditAppModal.tsx index a618f6d2edd..77ae999ec79 100644 --- a/src/components/Dashboard/Modals/EditAppModal/EditAppModal.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/EditAppModal.tsx @@ -71,7 +71,7 @@ export const EditAppModal = ({ behaviour: { externalUrl: (url: string) => { if (url === undefined || url.length < 1) { - return null; + return 'External URI is required'; } if (!url.match(appUrlWithAnyProtocolRegex)) { diff --git a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx index a352773843a..671570cb03a 100644 --- a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx +++ b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx @@ -101,7 +101,7 @@ export const AvailableElementTypes = ({ }, behaviour: { isOpeningNewTab: true, - externalUrl: '', + externalUrl: 'https://homarr.dev', }, area: { diff --git a/src/modules/Docker/ContainerActionBar.tsx b/src/modules/Docker/ContainerActionBar.tsx index 518fd49df2e..31a792a5d96 100644 --- a/src/modules/Docker/ContainerActionBar.tsx +++ b/src/modules/Docker/ContainerActionBar.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Button, Group } from '@mantine/core'; import { notifications } from '@mantine/notifications'; import { @@ -18,7 +17,6 @@ import { RouterInputs, api } from '~/utils/api'; import { useConfigContext } from '../../config/provider'; import { openContextModalGeneric } from '../../tools/mantineModalManagerExtensions'; -import { MatchingImages, ServiceType, tryMatchPort } from '../../tools/types'; import { AppType } from '../../types/app'; export interface ContainerActionBarProps { @@ -28,10 +26,15 @@ export interface ContainerActionBarProps { export default function ContainerActionBar({ selected, reload }: ContainerActionBarProps) { const { t } = useTranslation('modules/docker'); - const [isLoading, setisLoading] = useState(false); + const [isLoading, setLoading] = useState(false); const { config } = useConfigContext(); - const getLowestWrapper = () => config?.wrappers.sort((a, b) => a.position - b.position)[0]; + const sendDockerCommand = useDockerActionMutation(); + if (!config) { + return null; + } + const getLowestWrapper = () => + config.wrappers.sort((wrapper1, wrapper2) => wrapper1.position - wrapper2.position)[0]; if (process.env.DISABLE_EDIT_MODE === 'true') { return null; @@ -42,10 +45,10 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction - - - - - ); -} - -function SwitchToggle() { - const { colorScheme, toggleColorScheme } = useMantineColorScheme(); - const theme = useMantineTheme(); - - return ( - toggleColorScheme()} - size="lg" - onLabel={} - offLabel={} - /> - ); -} - -export async function getServerSideProps({ req, res, locale }: GetServerSidePropsContext) { - // Get all the configs in the /data/configs folder - // All the files that end in ".json" - const configs = fs.readdirSync('./data/configs').filter((file) => file.endsWith('.json')); - - if (configs.length === 0) { - res.writeHead(302, { - Location: '/', - }); - res.end(); - return { props: {} }; - } - // If all the configs are migrated (contains a schemaVersion), redirect to the index - if ( - configs.every( - (config) => JSON.parse(fs.readFileSync(`./data/configs/${config}`, 'utf8')).schemaVersion - ) - ) { - res.writeHead(302, { - Location: '/', - }); - res.end(); - return { - processed: true, - }; - } - return { - props: { - configs: configs.map( - // Get all the file names in ./data/configs - (config) => config.replace('.json', '') - ), - ...(await serverSideTranslations(locale!, [])), - // Will be passed to the page component as props - }, - }; -} - -const ICON_SIZE = 60; - -export function StatsCard({ - configs, - progress, - setProgress, -}: { - configs: string[]; - progress: number; - setProgress: (progress: number) => void; -}) { - const { classes } = useStyles(); - const numberOfConfigs = configs.length; - // Update the progress every 100ms - const [treatedConfigs, setTreatedConfigs] = useState([]); - // Stop the progress at 100% - useEffect(() => { - const data = axios.post('/api/migrate').then((response) => { - setProgress(100); - }); - - const interval = setInterval(() => { - if (configs.length === 0) { - clearInterval(interval); - setProgress(100); - return; - } - // Add last element of configs to the treatedConfigs array - setTreatedConfigs((treatedConfigs) => [...treatedConfigs, configs[configs.length - 1]]); - // Remove last element of configs - configs.pop(); - }, 500); - return () => clearInterval(interval); - }, [configs]); - - return ( - - - - Progress - - - {(100 / (numberOfConfigs + 1)).toFixed(1)}% - - - - - - - - } - > - {configs.map((config, index) => ( - }> - {config ?? 'Unknown'} - - ))} - {treatedConfigs.map((config, index) => ( - {config ?? 'Unknown'} - ))} - - - - - - {configs.length} configs left - - - ); -} diff --git a/src/tools/config/backendMigrateConfig.ts b/src/tools/config/backendMigrateConfig.ts deleted file mode 100644 index cd35cd9d663..00000000000 --- a/src/tools/config/backendMigrateConfig.ts +++ /dev/null @@ -1,19 +0,0 @@ -import fs from 'fs'; - -import { BackendConfigType } from '../../types/config'; -import { Config } from '../types'; -import { migrateConfig } from './migrateConfig'; - -export function backendMigrateConfig(config: Config, name: string): BackendConfigType { - const migratedConfig = migrateConfig(config); - - // Make a backup of the old file ./data/configs/${name}.json - // New name is ./data/configs/${name}.bak - fs.copyFileSync(`./data/configs/${name}.json`, `./data/configs/${name}.json.bak`); - - // 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; -} diff --git a/src/tools/config/getConfig.ts b/src/tools/config/getConfig.ts index 080f20e655c..6bee0db4a28 100644 --- a/src/tools/config/getConfig.ts +++ b/src/tools/config/getConfig.ts @@ -2,7 +2,6 @@ import Consola from 'consola'; import { v4 as uuidv4 } from 'uuid'; import { BackendConfigType, ConfigType } from '../../types/config'; -import { backendMigrateConfig } from './backendMigrateConfig'; import { configExists } from './configExists'; import { getFallbackConfig } from './getFallbackConfig'; import { readConfig } from './readConfig'; @@ -16,10 +15,6 @@ export const getConfig = (name: string): BackendConfigType => { // then it is an old config file and we should try to migrate it // to the new format. const config = readConfig(name); - if (config.schemaVersion === undefined) { - Consola.log('Migrating config file...', config.name); - return backendMigrateConfig(config, name); - } let backendConfig = config as BackendConfigType; diff --git a/src/tools/config/migrateConfig.ts b/src/tools/config/migrateConfig.ts deleted file mode 100644 index 12f4b343546..00000000000 --- a/src/tools/config/migrateConfig.ts +++ /dev/null @@ -1,490 +0,0 @@ -import Consola from 'consola'; -import { v4 as uuidv4 } from 'uuid'; - -import { ConfigAppIntegrationType, ConfigAppType, IntegrationType } from '../../types/app'; -import { AreaType } from '../../types/area'; -import { CategoryType } from '../../types/category'; -import { BackendConfigType } from '../../types/config'; -import { SearchEngineCommonSettingsType } from '../../types/settings'; -import { ICalendarWidget } from '../../widgets/calendar/CalendarTile'; -import { IDashDotTile } from '../../widgets/dashDot/DashDotTile'; -import { IDateWidget } from '../../widgets/date/DateTile'; -import { ITorrentNetworkTraffic } from '../../widgets/download-speed/TorrentNetworkTrafficTile'; -import { ITorrent } from '../../widgets/torrent/TorrentTile'; -import { IUsenetWidget } from '../../widgets/useNet/UseNetTile'; -import { IWeatherWidget } from '../../widgets/weather/WeatherTile'; -import { IWidget } from '../../widgets/widgets'; -import { Config, serviceItem } from '../types'; - -export function migrateConfig(config: Config): BackendConfigType { - const newConfig: BackendConfigType = { - schemaVersion: 1, - configProperties: { - name: config.name ?? 'default', - }, - categories: [], - widgets: migrateModules(config).filter((widget) => widget !== null), - apps: [], - settings: { - common: { - searchEngine: migrateSearchEngine(config), - defaultConfig: 'default', - }, - customization: { - colors: { - primary: config.settings.primaryColor ?? 'red', - secondary: config.settings.secondaryColor ?? 'orange', - shade: config.settings.primaryShade ?? 7, - }, - layout: { - enabledDocker: config.modules.docker?.enabled ?? false, - enabledLeftSidebar: false, - enabledPing: config.modules.ping?.enabled ?? false, - enabledRightSidebar: false, - enabledSearchbar: config.modules.search?.enabled ?? true, - }, - accessibility: { - disablePingPulse: false, - replacePingDotsWithIcons: false, - }, - }, - }, - wrappers: [ - { - id: 'default', - position: 0, - }, - ], - }; - - config.services.forEach((service) => { - const { category: categoryName } = service; - - if (!categoryName) { - newConfig.apps.push( - migrateService(service, { - type: 'wrapper', - properties: { - id: 'default', - }, - }) - ); - return; - } - - const category = getConfigAndCreateIfNotExsists(newConfig, categoryName); - - if (!category) { - return; - } - - newConfig.apps.push( - migrateService(service, { type: 'category', properties: { id: category.id } }) - ); - }); - - Consola.info('Migrator converted a configuration with the old schema to the new schema'); - - return newConfig; -} - -const migrateSearchEngine = (config: Config): SearchEngineCommonSettingsType => { - switch (config.settings.searchUrl) { - case 'https://bing.com/search?q=': - return { - type: 'bing', - properties: { - enabled: true, - openInNewTab: true, - }, - }; - case 'https://google.com/search?q=': - return { - type: 'google', - properties: { - enabled: true, - openInNewTab: true, - }, - }; - case 'https://duckduckgo.com/?q=': - return { - type: 'duckDuckGo', - properties: { - enabled: true, - openInNewTab: true, - }, - }; - default: - return { - type: 'custom', - properties: { - enabled: true, - openInNewTab: true, - template: config.settings.searchUrl, - }, - }; - } -}; - -const getConfigAndCreateIfNotExsists = ( - config: BackendConfigType, - 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: config.categories.length + 1, // sync up with index of categories - }; - - config.categories.push(category); - - // sync up with categories - if (config.wrappers.length < config.categories.length) { - config.wrappers.push({ - id: uuidv4(), - position: config.wrappers.length + 1, // sync up with index of categories - }); - } - - return category; -}; - -const migrateService = (oldService: serviceItem, areaType: AreaType): ConfigAppType => ({ - id: uuidv4(), - name: oldService.name, - url: oldService.url, - behaviour: { - isOpeningNewTab: oldService.newTab ?? true, - externalUrl: oldService.openedUrl ?? '', - }, - network: { - enabledStatusChecker: oldService.ping ?? true, - statusCodes: oldService.status ?? ['200'], - }, - appearance: { - iconUrl: migrateIcon(oldService.icon), - }, - integration: migrateIntegration(oldService), - area: areaType, - shape: {}, -}); - -const migrateModules = (config: Config): IWidget[] => { - const moduleKeys = Object.keys(config.modules); - return moduleKeys - .map((moduleKey): IWidget | null => { - const oldModule = config.modules[moduleKey]; - - if (!oldModule.enabled) { - return null; - } - - switch (moduleKey.toLowerCase()) { - case 'torrent-status': - case 'Torrent': - return { - id: uuidv4(), - type: 'torrents-status', - properties: { - refreshInterval: 10, - displayCompletedTorrents: oldModule.options?.hideComplete?.value ?? false, - displayStaleTorrents: true, - labelFilter: [], - labelFilterIsWhitelist: true, - }, - area: { - type: 'wrapper', - properties: { - id: 'default', - }, - }, - shape: {}, - } as ITorrent; - case 'weather': - return { - id: uuidv4(), - type: 'weather', - properties: { - displayInFahrenheit: oldModule.options?.freedomunit?.value ?? false, - location: { - name: oldModule.options?.location?.value ?? '', - latitude: 0, - longitude: 0, - }, - }, - area: { - type: 'wrapper', - properties: { - id: 'default', - }, - }, - shape: {}, - } as IWeatherWidget; - case 'dashdot': - case 'Dash.': { - const oldDashDotService = config.services.find((service) => service.type === 'Dash.'); - return { - id: uuidv4(), - type: 'dashdot', - properties: { - url: oldModule.options?.url?.value ?? oldDashDotService?.url ?? '', - cpuMultiView: oldModule.options?.cpuMultiView?.value ?? false, - storageMultiView: oldModule.options?.storageMultiView?.value ?? false, - useCompactView: oldModule.options?.useCompactView?.value ?? false, - graphs: oldModule.options?.graphs?.value ?? ['cpu', 'ram'], - }, - area: { - type: 'wrapper', - properties: { - id: 'default', - }, - }, - shape: {}, - } as unknown as IDashDotTile; - } - case 'date': - return { - id: uuidv4(), - type: 'date', - properties: { - display24HourFormat: oldModule.options?.full?.value ?? true, - }, - area: { - type: 'wrapper', - properties: { - id: 'default', - }, - }, - shape: {}, - } as IDateWidget; - case 'Download Speed' || 'dlspeed': - return { - id: uuidv4(), - type: 'dlspeed', - properties: {}, - area: { - type: 'wrapper', - properties: { - id: 'default', - }, - }, - shape: {}, - } as ITorrentNetworkTraffic; - case 'calendar': - return { - id: uuidv4(), - type: 'calendar', - properties: {}, - area: { - type: 'wrapper', - properties: { - id: 'default', - }, - }, - shape: {}, - } as ICalendarWidget; - case 'usenet': - return { - id: uuidv4(), - type: 'usenet', - properties: {}, - area: { - type: 'wrapper', - properties: { - id: 'default', - }, - }, - shape: {}, - } as IUsenetWidget; - default: - Consola.error(`Failed to map unknown module type ${moduleKey} to new type definitions.`); - return null; - } - }) - .filter((x) => x !== null) as IWidget[]; -}; - -const migrateIcon = (iconUrl: string) => { - if (iconUrl.startsWith('https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/')) { - const icon = iconUrl.split('/').at(-1); - Consola.warn( - `Detected legacy icon repository. Upgrading to replacement repository: ${iconUrl} -> ${icon}` - ); - return `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${icon}`; - } - - return iconUrl; -}; - -const migrateIntegration = (oldService: serviceItem): ConfigAppIntegrationType => { - const logInformation = (newType: IntegrationType) => { - Consola.info(`Migrated integration ${oldService.type} to the new type ${newType}`); - }; - switch (oldService.type) { - case 'Deluge': - logInformation('deluge'); - return { - type: 'deluge', - properties: [ - { - field: 'password', - type: 'private', - value: oldService.password, - }, - ], - }; - case 'Jellyseerr': - logInformation('jellyseerr'); - return { - type: 'jellyseerr', - properties: [ - { - field: 'apiKey', - type: 'private', - value: oldService.apiKey, - }, - ], - }; - case 'Overseerr': - logInformation('overseerr'); - return { - type: 'overseerr', - properties: [ - { - field: 'apiKey', - type: 'private', - value: oldService.apiKey, - }, - ], - }; - case 'Lidarr': - logInformation('lidarr'); - return { - type: 'lidarr', - properties: [ - { - field: 'apiKey', - type: 'private', - value: oldService.apiKey, - }, - ], - }; - case 'Radarr': - logInformation('radarr'); - return { - type: 'radarr', - properties: [ - { - field: 'apiKey', - type: 'private', - value: oldService.apiKey, - }, - ], - }; - case 'Readarr': - logInformation('readarr'); - return { - type: 'readarr', - properties: [ - { - field: 'apiKey', - type: 'private', - value: oldService.apiKey, - }, - ], - }; - case 'Sabnzbd': - logInformation('sabnzbd'); - return { - type: 'sabnzbd', - properties: [ - { - field: 'apiKey', - type: 'private', - value: oldService.apiKey, - }, - ], - }; - case 'Sonarr': - logInformation('sonarr'); - return { - type: 'sonarr', - properties: [ - { - field: 'apiKey', - type: 'private', - value: oldService.apiKey, - }, - ], - }; - case 'NZBGet': - logInformation('nzbGet'); - return { - type: 'nzbGet', - properties: [ - { - field: 'username', - type: 'private', - value: oldService.username, - }, - { - field: 'password', - type: 'private', - value: oldService.password, - }, - ], - }; - case 'qBittorrent': - logInformation('qBittorrent'); - return { - type: 'qBittorrent', - properties: [ - { - field: 'username', - type: 'private', - value: oldService.username, - }, - { - field: 'password', - type: 'private', - value: oldService.password, - }, - ], - }; - case 'Transmission': - logInformation('transmission'); - return { - type: 'transmission', - properties: [ - { - field: 'username', - type: 'private', - value: oldService.username, - }, - { - field: 'password', - type: 'private', - value: oldService.password, - }, - ], - }; - case 'Other': - return { - type: null, - properties: [], - }; - default: - Consola.warn( - `Integration type of service ${oldService.name} could not be mapped to new integration type definition` - ); - return { - type: null, - properties: [], - }; - } -}; diff --git a/src/tools/getConfig.ts b/src/tools/getConfig.ts deleted file mode 100644 index c594e9b01a2..00000000000 --- a/src/tools/getConfig.ts +++ /dev/null @@ -1,22 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -import { ConfigType } from '../types/config'; -import { getFallbackConfig } from './config/getFallbackConfig'; - -export function getConfig(name: string, props: any = undefined) { - // Check if the config file exists - const configPath = path.join(process.cwd(), 'data/configs', `${name}.json`); - if (!fs.existsSync(configPath)) { - return getFallbackConfig() as unknown as ConfigType; - } - const config = fs.readFileSync(configPath, 'utf8'); - // Print loaded config - return { - props: { - configName: name, - config: JSON.parse(config), - ...props, - }, - }; -} diff --git a/src/tools/types.ts b/src/tools/types.ts deleted file mode 100644 index 71046a43397..00000000000 --- a/src/tools/types.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { MantineTheme } from '@mantine/core'; - -import { OptionValues } from '../modules/ModuleTypes'; - -export interface Settings { - searchUrl: string; - searchNewTab?: boolean; - title?: string; - logo?: string; - favicon?: string; - primaryColor?: MantineTheme['primaryColor']; - secondaryColor?: MantineTheme['primaryColor']; - primaryShade?: MantineTheme['primaryShade']; - background?: string; - customCSS?: string; - appOpacity?: number; - widgetPosition?: string; - grow?: boolean; - appCardWidth?: number; -} - -export interface Config { - name: string; - services: serviceItem[]; - settings: Settings; - modules: { - [key: string]: ConfigModule; - }; -} - -interface ConfigModule { - title: string; - enabled: boolean; - options: { - [key: string]: OptionValues; - }; -} - -export const Targets = [ - { value: '_blank', label: 'New Tab' }, - { value: '_top', label: 'Same Window' }, -]; - -export const ServiceTypeList = [ - 'Other', - 'Dash.', - 'Deluge', - 'Emby', - 'Lidarr', - 'Plex', - 'qBittorrent', - 'Radarr', - 'Readarr', - 'Sonarr', - 'Transmission', - 'Overseerr', - 'Jellyseerr', - 'Sabnzbd', - 'NZBGet', -]; -export type ServiceType = - | 'Other' - | 'Dash.' - | 'Deluge' - | 'Emby' - | 'Lidarr' - | 'Plex' - | 'qBittorrent' - | 'Radarr' - | 'Readarr' - | 'Sonarr' - | 'Overseerr' - | 'Jellyseerr' - | 'Transmission' - | 'Sabnzbd' - | 'NZBGet'; - -/** - * @deprecated - * @param name the name to match - * @param form the form - * @returns the port from the map - */ -export function tryMatchPort(name: string | undefined, form?: any) { - if (!name) { - return undefined; - } - // Match name with portmap key - const port = portmap.find((p) => p.name === name.toLowerCase()); - if (form && port) { - form.setFieldValue('url', `http://localhost:${port.value}`); - } - return port; -} - -export const portmap = [ - { name: 'qbittorrent', value: '8080' }, - { name: 'sonarr', value: '8989' }, - { name: 'radarr', value: '7878' }, - { name: 'lidarr', value: '8686' }, - { name: 'readarr', value: '8787' }, - { name: 'deluge', value: '8112' }, - { name: 'transmission', value: '9091' }, - { name: 'plex', value: '32400' }, - { name: 'emby', value: '8096' }, - { name: 'overseerr', value: '5055' }, - { name: 'dash.', value: '3001' }, - { name: 'sabnzbd', value: '8080' }, - { name: 'nzbget', value: '6789' }, -]; - -//TODO: Fix this to be used in the docker add to homarr button -export const MatchingImages: { - image: string; - type: ServiceType; -}[] = [ - //Official images - { image: 'mauricenino/dashdot', type: 'Dash.' }, - { image: 'emby/embyserver', type: 'Emby' }, - { image: 'plexinc/pms-docker', type: 'Plex' }, - //Lidarr images - { image: 'hotio/lidarr', type: 'Lidarr' }, - { image: 'ghcr.io/hotio/lidarr', type: 'Lidarr' }, - { image: 'cr.hotio.dev/hotio/lidarr', type: 'Lidarr' }, - // Plex - { image: 'hotio/plex', type: 'Plex' }, - { image: 'ghcr.io/hotio/plex', type: 'Plex' }, - { image: 'cr.hotio.dev/hotio/plex', type: 'Plex' }, - // qbittorrent - { image: 'hotio/qbittorrent', type: 'qBittorrent' }, - { image: 'ghcr.io/hotio/qbittorrent', type: 'qBittorrent' }, - { image: 'cr.hotio.dev/hotio/qbittorrent', type: 'qBittorrent' }, - // Radarr - { image: 'hotio/radarr', type: 'Radarr' }, - { image: 'ghcr.io/hotio/radarr', type: 'Radarr' }, - { image: 'cr.hotio.dev/hotio/radarr', type: 'Radarr' }, - // Readarr - { image: 'hotio/readarr', type: 'Readarr' }, - { image: 'ghcr.io/hotio/readarr', type: 'Readarr' }, - { image: 'cr.hotio.dev/hotio/readarr', type: 'Readarr' }, - // Sonarr - { image: 'hotio/sonarr', type: 'Sonarr' }, - { image: 'ghcr.io/hotio/sonarr', type: 'Sonarr' }, - { image: 'cr.hotio.dev/hotio/sonarr', type: 'Sonarr' }, - //LinuxServer images - { image: 'lscr.io/linuxserver/deluge', type: 'Deluge' }, - { image: 'lscr.io/linuxserver/emby', type: 'Emby' }, - { image: 'lscr.io/linuxserver/lidarr', type: 'Lidarr' }, - { image: 'lscr.io/linuxserver/plex', type: 'Plex' }, - { image: 'lscr.io/linuxserver/qbittorrent', type: 'qBittorrent' }, - { image: 'lscr.io/linuxserver/radarr', type: 'Radarr' }, - { image: 'lscr.io/linuxserver/readarr', type: 'Readarr' }, - { image: 'lscr.io/linuxserver/sonarr', type: 'Sonarr' }, - { image: 'lscr.io/linuxserver/transmission', type: 'Transmission' }, - // LinuxServer but on Docker Hub - { image: 'linuxserver/deluge', type: 'Deluge' }, - { image: 'linuxserver/emby', type: 'Emby' }, - { image: 'linuxserver/lidarr', type: 'Lidarr' }, - { image: 'linuxserver/plex', type: 'Plex' }, - { image: 'linuxserver/qbittorrent', type: 'qBittorrent' }, - { image: 'linuxserver/radarr', type: 'Radarr' }, - { image: 'linuxserver/readarr', type: 'Readarr' }, - { image: 'linuxserver/sonarr', type: 'Sonarr' }, - { image: 'linuxserver/transmission', type: 'Transmission' }, - //High usage - { image: 'markusmcnugen/qbittorrentvpn', type: 'qBittorrent' }, - { image: 'haugene/transmission-openvpn', type: 'Transmission' }, -]; - -export interface serviceItem { - id: string; - name: string; - type: ServiceType; - url: string; - icon: string; - category?: string; - apiKey?: string; - password?: string; - username?: string; - openedUrl?: string; - newTab?: boolean; - ping?: boolean; - status?: string[]; -} diff --git a/src/types/app.ts b/src/types/app.ts index ce5f6f5b8c3..6438968b451 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -23,6 +23,9 @@ interface AppBehaviourType { interface AppNetworkType { enabledStatusChecker: boolean; + /** + * @deprecated replaced by statusCodes + */ okStatus?: number[]; statusCodes: string[]; } diff --git a/src/hooks/widgets/dashDot/api.ts b/src/widgets/dashDot/api.ts similarity index 83% rename from src/hooks/widgets/dashDot/api.ts rename to src/widgets/dashDot/api.ts index 6ede6e2b493..aac79be9bf0 100644 --- a/src/hooks/widgets/dashDot/api.ts +++ b/src/widgets/dashDot/api.ts @@ -1,11 +1,6 @@ import { useConfigContext } from '~/config/provider'; import { RouterInputs, api } from '~/utils/api'; - -import { UsenetInfoRequestParams } from '../../../pages/api/modules/usenet'; -import type { UsenetHistoryRequestParams } from '../../../pages/api/modules/usenet/history'; -import { UsenetPauseRequestParams } from '../../../pages/api/modules/usenet/pause'; -import type { UsenetQueueRequestParams } from '../../../pages/api/modules/usenet/queue'; -import { UsenetResumeRequestParams } from '../../../pages/api/modules/usenet/resume'; +import { UsenetHistoryRequestParams, UsenetInfoRequestParams, UsenetPauseRequestParams, UsenetQueueRequestParams, UsenetResumeRequestParams } from '../useNet/types'; const POLLING_INTERVAL = 2000; diff --git a/src/widgets/download-speed/Tile.tsx b/src/widgets/download-speed/Tile.tsx index 71e9ffd02b2..83d568e18dd 100644 --- a/src/widgets/download-speed/Tile.tsx +++ b/src/widgets/download-speed/Tile.tsx @@ -18,7 +18,7 @@ import { useEffect } from 'react'; import { AppAvatar } from '../../components/AppAvatar'; import { useConfigContext } from '../../config/provider'; -import { useGetDownloadClientsQueue } from '../../hooks/widgets/download-speed/useGetNetworkSpeed'; +import { useGetDownloadClientsQueue } from './useGetNetworkSpeed'; import { useColorTheme } from '../../tools/color'; import { humanFileSize } from '../../tools/humanFileSize'; import { diff --git a/src/hooks/widgets/download-speed/useGetNetworkSpeed.tsx b/src/widgets/download-speed/useGetNetworkSpeed.tsx similarity index 100% rename from src/hooks/widgets/download-speed/useGetNetworkSpeed.tsx rename to src/widgets/download-speed/useGetNetworkSpeed.tsx diff --git a/src/widgets/media-server/MediaServerTile.tsx b/src/widgets/media-server/MediaServerTile.tsx index a5451834235..c66d2f2e788 100644 --- a/src/widgets/media-server/MediaServerTile.tsx +++ b/src/widgets/media-server/MediaServerTile.tsx @@ -15,7 +15,7 @@ import { useTranslation } from 'next-i18next'; import { AppAvatar } from '../../components/AppAvatar'; import { useEditModeStore } from '../../components/Dashboard/Views/useEditModeStore'; import { useConfigContext } from '../../config/provider'; -import { useGetMediaServers } from '../../hooks/widgets/media-servers/useGetMediaServers'; +import { useGetMediaServers } from './useGetMediaServers'; import { defineWidget } from '../helper'; import { IWidget } from '../widgets'; import { TableRow } from './TableRow'; diff --git a/src/hooks/widgets/media-servers/useGetMediaServers.tsx b/src/widgets/media-server/useGetMediaServers.tsx similarity index 100% rename from src/hooks/widgets/media-servers/useGetMediaServers.tsx rename to src/widgets/media-server/useGetMediaServers.tsx diff --git a/src/widgets/torrent/TorrentTile.tsx b/src/widgets/torrent/TorrentTile.tsx index c34f6668e24..27b3ab02645 100644 --- a/src/widgets/torrent/TorrentTile.tsx +++ b/src/widgets/torrent/TorrentTile.tsx @@ -19,7 +19,7 @@ import relativeTime from 'dayjs/plugin/relativeTime'; import { useTranslation } from 'next-i18next'; import { MIN_WIDTH_MOBILE } from '../../constants/constants'; -import { useGetDownloadClientsQueue } from '../../hooks/widgets/download-speed/useGetNetworkSpeed'; +import { useGetDownloadClientsQueue } from '../download-speed/useGetNetworkSpeed'; import { NormalizedDownloadQueueResponse } from '../../types/api/downloads/queue/NormalizedDownloadQueueResponse'; import { AppIntegrationType } from '../../types/app'; import { defineWidget } from '../helper'; diff --git a/src/widgets/useNet/UseNetTile.tsx b/src/widgets/useNet/UseNetTile.tsx index bc28de4d935..fdc04f54199 100644 --- a/src/widgets/useNet/UseNetTile.tsx +++ b/src/widgets/useNet/UseNetTile.tsx @@ -12,7 +12,7 @@ import { useGetUsenetInfo, usePauseUsenetQueueMutation, useResumeUsenetQueueMutation, -} from '../../hooks/widgets/dashDot/api'; +} from '../dashDot/api'; import { humanFileSize } from '../../tools/humanFileSize'; import { AppIntegrationType } from '../../types/app'; import { defineWidget } from '../helper'; diff --git a/src/widgets/useNet/UsenetHistoryList.tsx b/src/widgets/useNet/UsenetHistoryList.tsx index 7123a9a8f73..2db65f03338 100644 --- a/src/widgets/useNet/UsenetHistoryList.tsx +++ b/src/widgets/useNet/UsenetHistoryList.tsx @@ -18,7 +18,7 @@ import duration from 'dayjs/plugin/duration'; import { useTranslation } from 'next-i18next'; import { FunctionComponent, useState } from 'react'; -import { useGetUsenetHistory } from '../../hooks/widgets/dashDot/api'; +import { useGetUsenetHistory } from '../dashDot/api'; import { parseDuration } from '../../tools/client/parseDuration'; import { humanFileSize } from '../../tools/humanFileSize'; diff --git a/src/widgets/useNet/UsenetQueueList.tsx b/src/widgets/useNet/UsenetQueueList.tsx index b3e39e61bde..f034d818be5 100644 --- a/src/widgets/useNet/UsenetQueueList.tsx +++ b/src/widgets/useNet/UsenetQueueList.tsx @@ -21,7 +21,7 @@ import duration from 'dayjs/plugin/duration'; import { useTranslation } from 'next-i18next'; import { FunctionComponent, useState } from 'react'; -import { useGetUsenetDownloads } from '../../hooks/widgets/dashDot/api'; +import { useGetUsenetDownloads } from '../dashDot/api'; import { humanFileSize } from '../../tools/humanFileSize'; dayjs.extend(duration); diff --git a/src/widgets/useNet/types.ts b/src/widgets/useNet/types.ts index a4dd8c5a072..cafc97058da 100644 --- a/src/widgets/useNet/types.ts +++ b/src/widgets/useNet/types.ts @@ -18,3 +18,45 @@ export interface UsenetHistoryItem { id: string; time: number; } + +export interface UsenetHistoryRequestParams { + appId: string; + offset: number; + limit: number; +} + +export interface UsenetHistoryResponse { + items: UsenetHistoryItem[]; + total: number; +} + +export interface UsenetInfoRequestParams { + appId: string; +} + +export interface UsenetInfoResponse { + paused: boolean; + sizeLeft: number; + speed: number; + eta: number; +} + +export interface UsenetPauseRequestParams { + appId: string; +} + +export interface UsenetQueueRequestParams { + appId: string; + offset: number; + limit: number; +} + +export interface UsenetQueueResponse { + items: UsenetQueueItem[]; + total: number; +} + +export interface UsenetResumeRequestParams { + appId: string; + nzbId?: string; +}