mirror of
https://github.com/diced/zipline.git
synced 2025-12-12 07:40:45 -08:00
feat: new actions page + finish impl v4 export
This commit is contained in:
10
src/client/pages/dashboard/admin/actions.tsx
Normal file
10
src/client/pages/dashboard/admin/actions.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import DashboardServerActions from '@/components/pages/serverActions';
|
||||||
|
import { useTitle } from '@/lib/hooks/useTitle';
|
||||||
|
|
||||||
|
export function Component() {
|
||||||
|
useTitle('Server Actions');
|
||||||
|
|
||||||
|
return <DashboardServerActions />;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.displayName = 'Dashboard/Admin/Actions';
|
||||||
@@ -82,6 +82,7 @@ export const router = createBrowserRouter([
|
|||||||
children: [
|
children: [
|
||||||
{ path: 'invites', lazy: () => import('./pages/dashboard/admin/invites') },
|
{ path: 'invites', lazy: () => import('./pages/dashboard/admin/invites') },
|
||||||
{ path: 'settings', lazy: () => import('./pages/dashboard/admin/settings') },
|
{ path: 'settings', lazy: () => import('./pages/dashboard/admin/settings') },
|
||||||
|
{ path: 'actions', lazy: () => import('./pages/dashboard/admin/actions') },
|
||||||
{
|
{
|
||||||
path: 'users',
|
path: 'users',
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import {
|
|||||||
IconRefreshDot,
|
IconRefreshDot,
|
||||||
IconSettingsFilled,
|
IconSettingsFilled,
|
||||||
IconShieldLockFilled,
|
IconShieldLockFilled,
|
||||||
|
IconStopwatch,
|
||||||
IconTags,
|
IconTags,
|
||||||
IconUpload,
|
IconUpload,
|
||||||
IconUsersGroup,
|
IconUsersGroup,
|
||||||
@@ -126,6 +127,12 @@ const navLinks: NavLinks[] = [
|
|||||||
if: (user) => user?.role === 'SUPERADMIN',
|
if: (user) => user?.role === 'SUPERADMIN',
|
||||||
href: '/dashboard/admin/settings',
|
href: '/dashboard/admin/settings',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Actions',
|
||||||
|
icon: <IconStopwatch size='1rem' />,
|
||||||
|
active: (path: string) => path === '/dashboard/admin/actions',
|
||||||
|
href: '/dashboard/admin/actions',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Users',
|
label: 'Users',
|
||||||
icon: <IconUsersGroup size='1rem' />,
|
icon: <IconUsersGroup size='1rem' />,
|
||||||
|
|||||||
19
src/components/pages/serverActions/ActionButton.tsx
Normal file
19
src/components/pages/serverActions/ActionButton.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { ActionIcon } from '@mantine/core';
|
||||||
|
import { IconPlayerPlayFilled } from '@tabler/icons-react';
|
||||||
|
|
||||||
|
const ICON_SIZE = '1.75rem';
|
||||||
|
|
||||||
|
export default function ActionButton({ onClick, Icon }: { onClick: () => void; Icon?: React.FC<any> }) {
|
||||||
|
return (
|
||||||
|
<ActionIcon
|
||||||
|
onClick={onClick}
|
||||||
|
variant='filled'
|
||||||
|
color='blue'
|
||||||
|
radius='md'
|
||||||
|
size='xl'
|
||||||
|
className='zip-click-action-button'
|
||||||
|
>
|
||||||
|
{Icon ? <Icon size={ICON_SIZE} /> : <IconPlayerPlayFilled size={ICON_SIZE} />}
|
||||||
|
</ActionIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Response } from '@/lib/api/response';
|
import { Response } from '@/lib/api/response';
|
||||||
import { fetchApi } from '@/lib/fetchApi';
|
import { fetchApi } from '@/lib/fetchApi';
|
||||||
import { Button } from '@mantine/core';
|
|
||||||
import { modals } from '@mantine/modals';
|
import { modals } from '@mantine/modals';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { IconTrashFilled } from '@tabler/icons-react';
|
import { IconTrashFilled } from '@tabler/icons-react';
|
||||||
|
import ActionButton from '../ActionButton';
|
||||||
|
|
||||||
export default function ClearTempButton() {
|
export default function ClearTempButton() {
|
||||||
const openModal = () =>
|
const openModal = () =>
|
||||||
@@ -30,11 +30,5 @@ export default function ClearTempButton() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return <ActionButton onClick={openModal} Icon={IconTrashFilled} />;
|
||||||
<>
|
|
||||||
<Button size='sm' leftSection={<IconTrashFilled size='1rem' />} onClick={openModal}>
|
|
||||||
Clear Temp Files
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Response } from '@/lib/api/response';
|
import { Response } from '@/lib/api/response';
|
||||||
import { fetchApi } from '@/lib/fetchApi';
|
import { fetchApi } from '@/lib/fetchApi';
|
||||||
import { Button } from '@mantine/core';
|
|
||||||
import { modals } from '@mantine/modals';
|
import { modals } from '@mantine/modals';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { IconTrashFilled } from '@tabler/icons-react';
|
import { IconTrashFilled } from '@tabler/icons-react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
import ActionButton from '../ActionButton';
|
||||||
|
|
||||||
export default function ClearZerosButton() {
|
export default function ClearZerosButton() {
|
||||||
const { data } = useSWR<Response['/api/server/clear_zeros']>('/api/server/clear_zeros');
|
const { data } = useSWR<Response['/api/server/clear_zeros']>('/api/server/clear_zeros');
|
||||||
@@ -32,11 +32,5 @@ export default function ClearZerosButton() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return <ActionButton onClick={openModal} Icon={IconTrashFilled} />;
|
||||||
<>
|
|
||||||
<Button size='sm' leftSection={<IconTrashFilled size='1rem' />} onClick={openModal}>
|
|
||||||
Clear Zero Byte Files
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,9 @@ import { Response } from '@/lib/api/response';
|
|||||||
import { fetchApi } from '@/lib/fetchApi';
|
import { fetchApi } from '@/lib/fetchApi';
|
||||||
import { Button, Group, Modal, Stack, Switch } from '@mantine/core';
|
import { Button, Group, Modal, Stack, Switch } from '@mantine/core';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { IconVideo, IconVideoOff } from '@tabler/icons-react';
|
import { IconVideoOff, IconVideoPlusFilled } from '@tabler/icons-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import ActionButton from '../ActionButton';
|
||||||
|
|
||||||
export default function GenThumbsButton() {
|
export default function GenThumbsButton() {
|
||||||
const [rerun, setRerun] = useState(false);
|
const [rerun, setRerun] = useState(false);
|
||||||
@@ -53,9 +54,8 @@ export default function GenThumbsButton() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Button size='sm' leftSection={<IconVideo size='1rem' />} onClick={() => setOpen(true)}>
|
|
||||||
Generate Thumbnails
|
<ActionButton onClick={() => setOpen(true)} Icon={IconVideoPlusFilled} />
|
||||||
</Button>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -93,8 +93,6 @@ export default function ImportV3Button() {
|
|||||||
color: 'green',
|
color: 'green',
|
||||||
icon: <IconDeviceFloppy size='1rem' />,
|
icon: <IconDeviceFloppy size='1rem' />,
|
||||||
});
|
});
|
||||||
|
|
||||||
await fetch('/reload');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ import { useUserStore } from '@/lib/store/user';
|
|||||||
import { modals } from '@mantine/modals';
|
import { modals } from '@mantine/modals';
|
||||||
import { fetchApi } from '@/lib/fetchApi';
|
import { fetchApi } from '@/lib/fetchApi';
|
||||||
import { Response } from '@/lib/api/response';
|
import { Response } from '@/lib/api/response';
|
||||||
|
import { mutate } from 'swr';
|
||||||
|
|
||||||
export default function ImportV4Button() {
|
export default function ImportV4Button() {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -52,6 +53,36 @@ export default function ImportV4Button() {
|
|||||||
setExport4(validated.data);
|
setExport4(validated.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleImportSettings = async () => {
|
||||||
|
if (!export4) return;
|
||||||
|
|
||||||
|
const { error } = await fetchApi<Response['/api/server/settings']>(
|
||||||
|
'/api/server/settings',
|
||||||
|
'PATCH',
|
||||||
|
export4.data.settings,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
showNotification({
|
||||||
|
title: 'Failed to import settings',
|
||||||
|
message: error.issues
|
||||||
|
? error.issues.map((x: { message: string }) => x.message).join('\n')
|
||||||
|
: error.error,
|
||||||
|
color: 'red',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
showNotification({
|
||||||
|
title: 'Settings imported',
|
||||||
|
message: 'To ensure that all settings take effect, it is recommended to restart Zipline.',
|
||||||
|
color: 'green',
|
||||||
|
});
|
||||||
|
|
||||||
|
mutate('/api/server/settings');
|
||||||
|
mutate('/api/server/settings/web');
|
||||||
|
mutate('/api/server/public');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleImport = async () => {
|
const handleImport = async () => {
|
||||||
if (!export4) return;
|
if (!export4) return;
|
||||||
|
|
||||||
@@ -88,6 +119,8 @@ export default function ImportV4Button() {
|
|||||||
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|
||||||
|
await handleImportSettings();
|
||||||
|
|
||||||
const { error, data } = await fetchApi<Response['/api/server/import/v4']>(
|
const { error, data } = await fetchApi<Response['/api/server/import/v4']>(
|
||||||
'/api/server/import/v4',
|
'/api/server/import/v4',
|
||||||
'POST',
|
'POST',
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { Button, Divider, Group, Modal } from '@mantine/core';
|
import { Divider, Group, Modal } from '@mantine/core';
|
||||||
import { IconDatabaseExport } from '@tabler/icons-react';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import ImportV3Button from './ImportV3Button';
|
import ImportV3Button from './ImportV3Button';
|
||||||
import ImportV4Button from './ImportV4Button';
|
import ImportV4Button from './ImportV4Button';
|
||||||
import ExportButton from './ExportButton';
|
import ExportButton from './ExportButton';
|
||||||
|
import ActionButton from '../../ActionButton';
|
||||||
|
import { IconDatabasePlus } from '@tabler/icons-react';
|
||||||
|
|
||||||
export default function ImportExport() {
|
export default function ImportExport() {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -21,9 +22,7 @@ export default function ImportExport() {
|
|||||||
<ExportButton />
|
<ExportButton />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Button size='sm' leftSection={<IconDatabaseExport size='1rem' />} onClick={() => setOpen(true)}>
|
<ActionButton onClick={() => setOpen(true)} Icon={IconDatabasePlus} />
|
||||||
Import / Export Data
|
|
||||||
</Button>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import { Button, Group, Modal, Stack, Switch } from '@mantine/core';
|
|||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { IconFileSearch } from '@tabler/icons-react';
|
import { IconFileSearch } from '@tabler/icons-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import ActionButton from '../ActionButton';
|
||||||
|
|
||||||
export default function RequerySizeButton() {
|
export default function RequerySizeButton() {
|
||||||
const [forceUpdate, setForceUpdate] = useState(false);
|
const [forceUpdate, setForceUpdate] = useState(false);
|
||||||
@@ -65,9 +66,8 @@ export default function RequerySizeButton() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Button size='sm' leftSection={<IconFileSearch size='1rem' />} onClick={() => setOpen(true)}>
|
|
||||||
Requery Size of Files
|
<ActionButton onClick={() => setOpen(true)} />
|
||||||
</Button>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
61
src/components/pages/serverActions/index.tsx
Normal file
61
src/components/pages/serverActions/index.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { Group, Paper, Stack, Text, Title } from '@mantine/core';
|
||||||
|
import ClearTempButton from './actions/ClearTempButton';
|
||||||
|
import ClearZerosButton from './actions/ClearZerosButton';
|
||||||
|
import GenThumbsButton from './actions/GenThumbsButton';
|
||||||
|
import ImportExport from './actions/ImportExportButton';
|
||||||
|
import RequerySizeButton from './actions/RequerySizeButton';
|
||||||
|
|
||||||
|
const ACTIONS = [
|
||||||
|
{
|
||||||
|
name: 'Import/Export Data',
|
||||||
|
desc: 'Allows you to import or export server data and configurations.',
|
||||||
|
Component: ImportExport,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Clear Temporary Files',
|
||||||
|
desc: 'Removes all temporary files from the temporary directory.',
|
||||||
|
Component: ClearTempButton,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Clear Zero Byte Files',
|
||||||
|
desc: 'Deletes all files with zero bytes from the database and/or storage.',
|
||||||
|
Component: ClearZerosButton,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Requery File Sizes',
|
||||||
|
desc: 'Recalculates and updates the sizes of all files in the database.',
|
||||||
|
Component: RequerySizeButton,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Generate Thumbnails',
|
||||||
|
desc: 'Creates thumbnails for all image and video files that lack them.',
|
||||||
|
Component: GenThumbsButton,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function DashboardServerActions() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Group gap='sm'>
|
||||||
|
<Title order={1}>Server Actions</Title>
|
||||||
|
</Group>
|
||||||
|
<Text c='dimmed' mb='xs'>
|
||||||
|
Useful tools and scripts for server management.
|
||||||
|
</Text>
|
||||||
|
<Stack gap='xs' my='sm'>
|
||||||
|
{ACTIONS.map(({ name, desc, Component }) => (
|
||||||
|
<Paper withBorder p='sm' key={name}>
|
||||||
|
<Group gap='md'>
|
||||||
|
<Component />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Title order={4}>{name}</Title>
|
||||||
|
<Text c='dimmed'>{desc}</Text>
|
||||||
|
</div>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -39,7 +39,6 @@ export function settingsOnSubmit(navigate: NavigateFunction, form: ReturnType<ty
|
|||||||
icon: <IconDeviceFloppy size='1rem' />,
|
icon: <IconDeviceFloppy size='1rem' />,
|
||||||
});
|
});
|
||||||
|
|
||||||
await fetch('/reload');
|
|
||||||
mutate('/api/server/settings', data);
|
mutate('/api/server/settings', data);
|
||||||
mutate('/api/server/settings/web');
|
mutate('/api/server/settings/web');
|
||||||
mutate('/api/server/public');
|
mutate('/api/server/public');
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { useConfig } from '@/components/ConfigProvider';
|
import { useConfig } from '@/components/ConfigProvider';
|
||||||
import { eitherTrue } from '@/lib/primitive';
|
import { eitherTrue } from '@/lib/primitive';
|
||||||
import { isAdministrator } from '@/lib/role';
|
|
||||||
import { useUserStore } from '@/lib/store/user';
|
|
||||||
import { Group, SimpleGrid, Stack, Title } from '@mantine/core';
|
import { Group, SimpleGrid, Stack, Title } from '@mantine/core';
|
||||||
import { lazy } from 'react';
|
import { lazy } from 'react';
|
||||||
|
|
||||||
@@ -10,7 +8,6 @@ const SettingsDashboard = lazy(() => import('./parts/SettingsDashboard'));
|
|||||||
const SettingsFileView = lazy(() => import('./parts/SettingsFileView'));
|
const SettingsFileView = lazy(() => import('./parts/SettingsFileView'));
|
||||||
const SettingsGenerators = lazy(() => import('./parts/SettingsGenerators'));
|
const SettingsGenerators = lazy(() => import('./parts/SettingsGenerators'));
|
||||||
const SettingsMfa = lazy(() => import('./parts/SettingsMfa'));
|
const SettingsMfa = lazy(() => import('./parts/SettingsMfa'));
|
||||||
const SettingsServerActions = lazy(() => import('./parts/SettingsServerUtil'));
|
|
||||||
const SettingsUser = lazy(() => import('./parts/SettingsUser'));
|
const SettingsUser = lazy(() => import('./parts/SettingsUser'));
|
||||||
const SettingsExports = lazy(() => import('./parts/SettingsExports'));
|
const SettingsExports = lazy(() => import('./parts/SettingsExports'));
|
||||||
const SettingsSessions = lazy(() => import('./parts/SettingsSessions'));
|
const SettingsSessions = lazy(() => import('./parts/SettingsSessions'));
|
||||||
@@ -18,7 +15,6 @@ const SettingsOAuth = lazy(() => import('./parts/SettingsOAuth'));
|
|||||||
|
|
||||||
export default function DashboardSettings() {
|
export default function DashboardSettings() {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const user = useUserStore((state) => state.user);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -49,8 +45,6 @@ export default function DashboardSettings() {
|
|||||||
|
|
||||||
<SettingsExports />
|
<SettingsExports />
|
||||||
<SettingsGenerators />
|
<SettingsGenerators />
|
||||||
|
|
||||||
{isAdministrator(user?.role) && <SettingsServerActions />}
|
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import { Group, Paper, Text, Title } from '@mantine/core';
|
|
||||||
import ClearTempButton from './ClearTempButton';
|
|
||||||
import ClearZerosButton from './ClearZerosButton';
|
|
||||||
import GenThumbsButton from './GenThumbsButton';
|
|
||||||
import RequerySizeButton from './RequerySizeButton';
|
|
||||||
import ImportExportButton from './ImportExportButton';
|
|
||||||
|
|
||||||
export default function SettingsServerActions() {
|
|
||||||
return (
|
|
||||||
<Paper withBorder p='sm'>
|
|
||||||
<Title order={2}>Server Actions</Title>
|
|
||||||
<Text size='sm' c='dimmed' mt={3}>
|
|
||||||
Helpful scripts and tools for server management.
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<Group mt='xs'>
|
|
||||||
<ClearZerosButton />
|
|
||||||
<ClearTempButton />
|
|
||||||
<RequerySizeButton />
|
|
||||||
<GenThumbsButton />
|
|
||||||
<ImportExportButton />
|
|
||||||
</Group>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user