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: [
|
||||
{ path: 'invites', lazy: () => import('./pages/dashboard/admin/invites') },
|
||||
{ path: 'settings', lazy: () => import('./pages/dashboard/admin/settings') },
|
||||
{ path: 'actions', lazy: () => import('./pages/dashboard/admin/actions') },
|
||||
{
|
||||
path: 'users',
|
||||
children: [
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
IconRefreshDot,
|
||||
IconSettingsFilled,
|
||||
IconShieldLockFilled,
|
||||
IconStopwatch,
|
||||
IconTags,
|
||||
IconUpload,
|
||||
IconUsersGroup,
|
||||
@@ -126,6 +127,12 @@ const navLinks: NavLinks[] = [
|
||||
if: (user) => user?.role === 'SUPERADMIN',
|
||||
href: '/dashboard/admin/settings',
|
||||
},
|
||||
{
|
||||
label: 'Actions',
|
||||
icon: <IconStopwatch size='1rem' />,
|
||||
active: (path: string) => path === '/dashboard/admin/actions',
|
||||
href: '/dashboard/admin/actions',
|
||||
},
|
||||
{
|
||||
label: 'Users',
|
||||
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 { fetchApi } from '@/lib/fetchApi';
|
||||
import { Button } from '@mantine/core';
|
||||
import { modals } from '@mantine/modals';
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import { IconTrashFilled } from '@tabler/icons-react';
|
||||
import ActionButton from '../ActionButton';
|
||||
|
||||
export default function ClearTempButton() {
|
||||
const openModal = () =>
|
||||
@@ -30,11 +30,5 @@ export default function ClearTempButton() {
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button size='sm' leftSection={<IconTrashFilled size='1rem' />} onClick={openModal}>
|
||||
Clear Temp Files
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
return <ActionButton onClick={openModal} Icon={IconTrashFilled} />;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Response } from '@/lib/api/response';
|
||||
import { fetchApi } from '@/lib/fetchApi';
|
||||
import { Button } from '@mantine/core';
|
||||
import { modals } from '@mantine/modals';
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import { IconTrashFilled } from '@tabler/icons-react';
|
||||
import useSWR from 'swr';
|
||||
import ActionButton from '../ActionButton';
|
||||
|
||||
export default function ClearZerosButton() {
|
||||
const { data } = useSWR<Response['/api/server/clear_zeros']>('/api/server/clear_zeros');
|
||||
@@ -32,11 +32,5 @@ export default function ClearZerosButton() {
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button size='sm' leftSection={<IconTrashFilled size='1rem' />} onClick={openModal}>
|
||||
Clear Zero Byte Files
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
return <ActionButton onClick={openModal} Icon={IconTrashFilled} />;
|
||||
}
|
||||
@@ -2,8 +2,9 @@ import { Response } from '@/lib/api/response';
|
||||
import { fetchApi } from '@/lib/fetchApi';
|
||||
import { Button, Group, Modal, Stack, Switch } from '@mantine/core';
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import { IconVideo, IconVideoOff } from '@tabler/icons-react';
|
||||
import { IconVideoOff, IconVideoPlusFilled } from '@tabler/icons-react';
|
||||
import { useState } from 'react';
|
||||
import ActionButton from '../ActionButton';
|
||||
|
||||
export default function GenThumbsButton() {
|
||||
const [rerun, setRerun] = useState(false);
|
||||
@@ -53,9 +54,8 @@ export default function GenThumbsButton() {
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
<Button size='sm' leftSection={<IconVideo size='1rem' />} onClick={() => setOpen(true)}>
|
||||
Generate Thumbnails
|
||||
</Button>
|
||||
|
||||
<ActionButton onClick={() => setOpen(true)} Icon={IconVideoPlusFilled} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -93,8 +93,6 @@ export default function ImportV3Button() {
|
||||
color: 'green',
|
||||
icon: <IconDeviceFloppy size='1rem' />,
|
||||
});
|
||||
|
||||
await fetch('/reload');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useUserStore } from '@/lib/store/user';
|
||||
import { modals } from '@mantine/modals';
|
||||
import { fetchApi } from '@/lib/fetchApi';
|
||||
import { Response } from '@/lib/api/response';
|
||||
import { mutate } from 'swr';
|
||||
|
||||
export default function ImportV4Button() {
|
||||
const [open, setOpen] = useState(false);
|
||||
@@ -52,6 +53,36 @@ export default function ImportV4Button() {
|
||||
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 () => {
|
||||
if (!export4) return;
|
||||
|
||||
@@ -88,6 +119,8 @@ export default function ImportV4Button() {
|
||||
|
||||
setOpen(false);
|
||||
|
||||
await handleImportSettings();
|
||||
|
||||
const { error, data } = await fetchApi<Response['/api/server/import/v4']>(
|
||||
'/api/server/import/v4',
|
||||
'POST',
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Button, Divider, Group, Modal } from '@mantine/core';
|
||||
import { IconDatabaseExport } from '@tabler/icons-react';
|
||||
import { Divider, Group, Modal } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import ImportV3Button from './ImportV3Button';
|
||||
import ImportV4Button from './ImportV4Button';
|
||||
import ExportButton from './ExportButton';
|
||||
import ActionButton from '../../ActionButton';
|
||||
import { IconDatabasePlus } from '@tabler/icons-react';
|
||||
|
||||
export default function ImportExport() {
|
||||
const [open, setOpen] = useState(false);
|
||||
@@ -21,9 +22,7 @@ export default function ImportExport() {
|
||||
<ExportButton />
|
||||
</Modal>
|
||||
|
||||
<Button size='sm' leftSection={<IconDatabaseExport size='1rem' />} onClick={() => setOpen(true)}>
|
||||
Import / Export Data
|
||||
</Button>
|
||||
<ActionButton onClick={() => setOpen(true)} Icon={IconDatabasePlus} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { Button, Group, Modal, Stack, Switch } from '@mantine/core';
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import { IconFileSearch } from '@tabler/icons-react';
|
||||
import { useState } from 'react';
|
||||
import ActionButton from '../ActionButton';
|
||||
|
||||
export default function RequerySizeButton() {
|
||||
const [forceUpdate, setForceUpdate] = useState(false);
|
||||
@@ -65,9 +66,8 @@ export default function RequerySizeButton() {
|
||||
</Button>
|
||||
</Group>
|
||||
</Modal>
|
||||
<Button size='sm' leftSection={<IconFileSearch size='1rem' />} onClick={() => setOpen(true)}>
|
||||
Requery Size of Files
|
||||
</Button>
|
||||
|
||||
<ActionButton onClick={() => setOpen(true)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
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' />,
|
||||
});
|
||||
|
||||
await fetch('/reload');
|
||||
mutate('/api/server/settings', data);
|
||||
mutate('/api/server/settings/web');
|
||||
mutate('/api/server/public');
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useConfig } from '@/components/ConfigProvider';
|
||||
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 { lazy } from 'react';
|
||||
|
||||
@@ -10,7 +8,6 @@ const SettingsDashboard = lazy(() => import('./parts/SettingsDashboard'));
|
||||
const SettingsFileView = lazy(() => import('./parts/SettingsFileView'));
|
||||
const SettingsGenerators = lazy(() => import('./parts/SettingsGenerators'));
|
||||
const SettingsMfa = lazy(() => import('./parts/SettingsMfa'));
|
||||
const SettingsServerActions = lazy(() => import('./parts/SettingsServerUtil'));
|
||||
const SettingsUser = lazy(() => import('./parts/SettingsUser'));
|
||||
const SettingsExports = lazy(() => import('./parts/SettingsExports'));
|
||||
const SettingsSessions = lazy(() => import('./parts/SettingsSessions'));
|
||||
@@ -18,7 +15,6 @@ const SettingsOAuth = lazy(() => import('./parts/SettingsOAuth'));
|
||||
|
||||
export default function DashboardSettings() {
|
||||
const config = useConfig();
|
||||
const user = useUserStore((state) => state.user);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -49,8 +45,6 @@ export default function DashboardSettings() {
|
||||
|
||||
<SettingsExports />
|
||||
<SettingsGenerators />
|
||||
|
||||
{isAdministrator(user?.role) && <SettingsServerActions />}
|
||||
</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