mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-09 15:05:48 +01:00
✨ Make discord integration a module
This allows for an error message if the docker integration fails to load
This commit is contained in:
@@ -18,6 +18,9 @@
|
|||||||
},
|
},
|
||||||
"Date": {
|
"Date": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
},
|
||||||
|
"Docker": {
|
||||||
|
"enabled": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export const REPO_URL = 'ajnart/homarr';
|
export const REPO_URL = 'ajnart/homarr';
|
||||||
export const CURRENT_VERSION = 'v0.8.0';
|
export const CURRENT_VERSION = 'v0.8.1';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "homarr",
|
"name": "homarr",
|
||||||
"version": "0.8.0",
|
"version": "0.8.1",
|
||||||
"description": "Homarr - A homepage for your server.",
|
"description": "Homarr - A homepage for your server.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
Switch,
|
Switch,
|
||||||
Tabs,
|
Tabs,
|
||||||
Text,
|
|
||||||
TextInput,
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
import { Menu, Text, useMantineTheme } from '@mantine/core';
|
|
||||||
import { showNotification, updateNotification } from '@mantine/notifications';
|
|
||||||
import {
|
|
||||||
IconCheck,
|
|
||||||
IconCodePlus,
|
|
||||||
IconPlayerPlay,
|
|
||||||
IconPlayerStop,
|
|
||||||
IconRotateClockwise,
|
|
||||||
IconX,
|
|
||||||
} from '@tabler/icons';
|
|
||||||
import axios from 'axios';
|
|
||||||
import Dockerode from 'dockerode';
|
|
||||||
|
|
||||||
function sendNotification(action: string, containerId: string, containerName: string) {
|
|
||||||
showNotification({
|
|
||||||
id: 'load-data',
|
|
||||||
loading: true,
|
|
||||||
title: `${action}ing container ${containerName}`,
|
|
||||||
message: 'Your password is being checked...',
|
|
||||||
autoClose: false,
|
|
||||||
disallowClose: true,
|
|
||||||
});
|
|
||||||
axios.get(`/api/docker/container/${containerId}?action=${action}`).then((res) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (res.data.success === true) {
|
|
||||||
updateNotification({
|
|
||||||
id: 'load-data',
|
|
||||||
title: 'Container restarted',
|
|
||||||
message: 'Your container was successfully restarted',
|
|
||||||
icon: <IconCheck />,
|
|
||||||
autoClose: 2000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (res.data.success === false) {
|
|
||||||
updateNotification({
|
|
||||||
id: 'load-data',
|
|
||||||
color: 'red',
|
|
||||||
title: 'There was an error restarting your container.',
|
|
||||||
message: 'Your container has encountered issues while restarting.',
|
|
||||||
icon: <IconX />,
|
|
||||||
autoClose: 2000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function restart(container: Dockerode.ContainerInfo) {
|
|
||||||
sendNotification('restart', container.Id, container.Names[0]);
|
|
||||||
}
|
|
||||||
function stop(container: Dockerode.ContainerInfo) {
|
|
||||||
console.log('stoping container', container.Id);
|
|
||||||
}
|
|
||||||
function start(container: Dockerode.ContainerInfo) {
|
|
||||||
console.log('starting container', container.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DockerMenu(props: any) {
|
|
||||||
const { container }: { container: Dockerode.ContainerInfo } = props;
|
|
||||||
const theme = useMantineTheme();
|
|
||||||
if (container === undefined) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Menu shadow="lg" radius="md">
|
|
||||||
<Menu.Label>Actions</Menu.Label>
|
|
||||||
<Menu.Item icon={<IconRotateClockwise color="orange" />} onClick={() => restart(container)}>
|
|
||||||
<Text>Restart</Text>
|
|
||||||
</Menu.Item>
|
|
||||||
{container.State === 'running' ? (
|
|
||||||
<Menu.Item icon={<IconPlayerStop color="red" />}>
|
|
||||||
<Text>Stop</Text>
|
|
||||||
</Menu.Item>
|
|
||||||
) : (
|
|
||||||
<Menu.Item icon={<IconPlayerPlay color="green" />}>
|
|
||||||
<Text>Start</Text>
|
|
||||||
</Menu.Item>
|
|
||||||
)}
|
|
||||||
{/* <Menu.Item icon={<IconDownload color="blue" />}>
|
|
||||||
<Text>Pull latest image </Text>
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item icon={<IconFileText color="grey" />}>
|
|
||||||
<Text>Logs</Text>
|
|
||||||
</Menu.Item> */}
|
|
||||||
<Menu.Label>Homarr</Menu.Label>
|
|
||||||
<Menu.Item icon={<IconCodePlus color={theme.primaryColor} />}>
|
|
||||||
<Text>Add to Homarr</Text>
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
WeatherModule,
|
WeatherModule,
|
||||||
DashdotModule,
|
DashdotModule,
|
||||||
} from '../modules';
|
} from '../modules';
|
||||||
|
import DockerMenuButton from '../modules/docker/DockerModule';
|
||||||
import { ModuleWrapper } from '../modules/moduleWrapper';
|
import { ModuleWrapper } from '../modules/moduleWrapper';
|
||||||
import DockerDrawer from '../Docker/DockerDrawer';
|
|
||||||
import SearchBar from '../modules/search/SearchModule';
|
import SearchBar from '../modules/search/SearchModule';
|
||||||
import { SettingsMenuButton } from '../Settings/SettingsMenu';
|
import { SettingsMenuButton } from '../Settings/SettingsMenu';
|
||||||
import { Logo } from './Logo';
|
import { Logo } from './Logo';
|
||||||
@@ -53,7 +53,7 @@ export function Header(props: any) {
|
|||||||
</Box>
|
</Box>
|
||||||
<Group noWrap>
|
<Group noWrap>
|
||||||
<SearchBar />
|
<SearchBar />
|
||||||
<DockerDrawer />
|
<DockerMenuButton />
|
||||||
<SettingsMenuButton />
|
<SettingsMenuButton />
|
||||||
<AddItemShelfButton />
|
<AddItemShelfButton />
|
||||||
<ActionIcon className={classes.burger} variant="default" radius="md" size="xl">
|
<ActionIcon className={classes.burger} variant="default" radius="md" size="xl">
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ export function DashdotComponent() {
|
|||||||
<div className={classes.tableRow}>
|
<div className={classes.tableRow}>
|
||||||
<p className={classes.tableLabel}>Storage:</p>
|
<p className={classes.tableLabel}>Storage:</p>
|
||||||
<p className={classes.tableValue}>
|
<p className={classes.tableValue}>
|
||||||
{(100 * totalUsed / (totalSize || 1)).toFixed(1)}%{'\n'}
|
{((100 * totalUsed) / (totalSize || 1)).toFixed(1)}%{'\n'}
|
||||||
{bytePrettyPrint(totalUsed)} / {bytePrettyPrint(totalSize)}
|
{bytePrettyPrint(totalUsed)} / {bytePrettyPrint(totalSize)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import {
|
|||||||
} from '@tabler/icons';
|
} from '@tabler/icons';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Dockerode from 'dockerode';
|
import Dockerode from 'dockerode';
|
||||||
import { tryMatchService } from '../../tools/addToHomarr';
|
import { tryMatchService } from '../../../tools/addToHomarr';
|
||||||
import { useConfig } from '../../tools/state';
|
import { useConfig } from '../../../tools/state';
|
||||||
import { AddAppShelfItemForm } from '../AppShelf/AddAppShelfItem';
|
import { AddAppShelfItemForm } from '../../AppShelf/AddAppShelfItem';
|
||||||
|
|
||||||
function sendDockerCommand(action: string, containerId: string, containerName: string) {
|
function sendDockerCommand(action: string, containerId: string, containerName: string) {
|
||||||
showNotification({
|
showNotification({
|
||||||
@@ -1,31 +1,58 @@
|
|||||||
import { ActionIcon, Drawer, Group, LoadingOverlay } from '@mantine/core';
|
import { ActionIcon, Drawer, Group, LoadingOverlay, Text } from '@mantine/core';
|
||||||
import { IconBrandDocker } from '@tabler/icons';
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import Docker from 'dockerode';
|
import Docker from 'dockerode';
|
||||||
|
import { IconBrandDocker, IconX } from '@tabler/icons';
|
||||||
|
import { showNotification } from '@mantine/notifications';
|
||||||
import ContainerActionBar from './ContainerActionBar';
|
import ContainerActionBar from './ContainerActionBar';
|
||||||
import DockerTable from './DockerTable';
|
import DockerTable from './DockerTable';
|
||||||
|
import { useConfig } from '../../../tools/state';
|
||||||
|
import { IModule } from '../modules';
|
||||||
|
|
||||||
export default function DockerDrawer(props: any) {
|
export const DockerModule: IModule = {
|
||||||
|
title: 'Docker',
|
||||||
|
description: 'Allows you to easily manage your torrents',
|
||||||
|
icon: IconBrandDocker,
|
||||||
|
component: DockerMenuButton,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function DockerMenuButton(props: any) {
|
||||||
const [opened, setOpened] = useState(false);
|
const [opened, setOpened] = useState(false);
|
||||||
const [containers, setContainers] = useState<Docker.ContainerInfo[]>([]);
|
const [containers, setContainers] = useState<Docker.ContainerInfo[]>([]);
|
||||||
const [selection, setSelection] = useState<Docker.ContainerInfo[]>([]);
|
const [selection, setSelection] = useState<Docker.ContainerInfo[]>([]);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
const { config } = useConfig();
|
||||||
function reload() {
|
|
||||||
setVisible(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
axios.get('/api/docker/containers').then((res) => {
|
|
||||||
setContainers(res.data);
|
|
||||||
setSelection([]);
|
|
||||||
setVisible(false);
|
|
||||||
});
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reload();
|
reload();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
function reload() {
|
||||||
|
setVisible(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
axios
|
||||||
|
.get('/api/docker/containers')
|
||||||
|
.then((res) => {
|
||||||
|
setContainers(res.data);
|
||||||
|
setSelection([]);
|
||||||
|
setVisible(false);
|
||||||
|
})
|
||||||
|
.catch(() =>
|
||||||
|
// Send an Error notification
|
||||||
|
showNotification({
|
||||||
|
autoClose: 1500,
|
||||||
|
title: <Text>Docker integration failed</Text>,
|
||||||
|
color: 'red',
|
||||||
|
icon: <IconX />,
|
||||||
|
message: 'Did you forget to mount the docker socket ?',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
const exists = config.modules?.[DockerModule.title]?.enabled ?? false;
|
||||||
|
if (!exists) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
// Check if the user has at least one container
|
// Check if the user has at least one container
|
||||||
if (containers.length < 1) return null;
|
if (containers.length < 1) return null;
|
||||||
return (
|
return (
|
||||||
1
src/components/modules/docker/index.ts
Normal file
1
src/components/modules/docker/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { DockerModule } from './DockerModule';
|
||||||
@@ -5,3 +5,4 @@ export * from './downloads';
|
|||||||
export * from './ping';
|
export * from './ping';
|
||||||
export * from './search';
|
export * from './search';
|
||||||
export * from './weather';
|
export * from './weather';
|
||||||
|
export * from './docker';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Kbd, createStyles, Text, Popover, Autocomplete, Tooltip } from '@mantine/core';
|
import { Kbd, createStyles, Autocomplete } from '@mantine/core';
|
||||||
import { useDebouncedValue, useForm, useHotkeys } from '@mantine/hooks';
|
import { useDebouncedValue, useForm, useHotkeys } from '@mantine/hooks';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
|
|
||||||
import Docker from 'dockerode';
|
import Docker from 'dockerode';
|
||||||
|
|
||||||
const docker = new Docker();
|
|
||||||
|
|
||||||
async function Get(req: NextApiRequest, res: NextApiResponse) {
|
async function Get(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const containers = await docker.listContainers({ all: true });
|
try {
|
||||||
return res.status(200).json(containers);
|
const docker = new Docker();
|
||||||
|
const containers = await docker.listContainers();
|
||||||
|
res.status(200).json(containers);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ err });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user