diff --git a/src/client/pages/dashboard/admin/actions.tsx b/src/client/pages/dashboard/admin/actions.tsx
new file mode 100644
index 00000000..c19f123c
--- /dev/null
+++ b/src/client/pages/dashboard/admin/actions.tsx
@@ -0,0 +1,10 @@
+import DashboardServerActions from '@/components/pages/serverActions';
+import { useTitle } from '@/lib/hooks/useTitle';
+
+export function Component() {
+ useTitle('Server Actions');
+
+ return ;
+}
+
+Component.displayName = 'Dashboard/Admin/Actions';
diff --git a/src/client/routes.tsx b/src/client/routes.tsx
index 71a09a3b..c87a8fb8 100644
--- a/src/client/routes.tsx
+++ b/src/client/routes.tsx
@@ -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: [
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
index e7a48f9b..36a5601f 100755
--- a/src/components/Layout.tsx
+++ b/src/components/Layout.tsx
@@ -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: ,
+ active: (path: string) => path === '/dashboard/admin/actions',
+ href: '/dashboard/admin/actions',
+ },
{
label: 'Users',
icon: ,
diff --git a/src/components/pages/serverActions/ActionButton.tsx b/src/components/pages/serverActions/ActionButton.tsx
new file mode 100644
index 00000000..4b1fb11e
--- /dev/null
+++ b/src/components/pages/serverActions/ActionButton.tsx
@@ -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 }) {
+ return (
+
+ {Icon ? : }
+
+ );
+}
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ClearTempButton.tsx b/src/components/pages/serverActions/actions/ClearTempButton.tsx
similarity index 84%
rename from src/components/pages/settings/parts/SettingsServerUtil/ClearTempButton.tsx
rename to src/components/pages/serverActions/actions/ClearTempButton.tsx
index 42131f9d..dd57c6b9 100755
--- a/src/components/pages/settings/parts/SettingsServerUtil/ClearTempButton.tsx
+++ b/src/components/pages/serverActions/actions/ClearTempButton.tsx
@@ -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 (
- <>
- } onClick={openModal}>
- Clear Temp Files
-
- >
- );
+ return ;
}
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ClearZerosButton.tsx b/src/components/pages/serverActions/actions/ClearZerosButton.tsx
similarity index 84%
rename from src/components/pages/settings/parts/SettingsServerUtil/ClearZerosButton.tsx
rename to src/components/pages/serverActions/actions/ClearZerosButton.tsx
index aa2dad22..edc92296 100755
--- a/src/components/pages/settings/parts/SettingsServerUtil/ClearZerosButton.tsx
+++ b/src/components/pages/serverActions/actions/ClearZerosButton.tsx
@@ -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('/api/server/clear_zeros');
@@ -32,11 +32,5 @@ export default function ClearZerosButton() {
},
});
- return (
- <>
- } onClick={openModal}>
- Clear Zero Byte Files
-
- >
- );
+ return ;
}
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/GenThumbsButton.tsx b/src/components/pages/serverActions/actions/GenThumbsButton.tsx
similarity index 88%
rename from src/components/pages/settings/parts/SettingsServerUtil/GenThumbsButton.tsx
rename to src/components/pages/serverActions/actions/GenThumbsButton.tsx
index a44490ab..3719e9ad 100644
--- a/src/components/pages/settings/parts/SettingsServerUtil/GenThumbsButton.tsx
+++ b/src/components/pages/serverActions/actions/GenThumbsButton.tsx
@@ -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() {
- } onClick={() => setOpen(true)}>
- Generate Thumbnails
-
+
+ setOpen(true)} Icon={IconVideoPlusFilled} />
>
);
}
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ExportButton.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ExportButton.tsx
similarity index 100%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ExportButton.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ExportButton.tsx
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV3Button/Export3Details.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ImportV3Button/Export3Details.tsx
similarity index 100%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV3Button/Export3Details.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ImportV3Button/Export3Details.tsx
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV3Button/Export3ImportSettings.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ImportV3Button/Export3ImportSettings.tsx
similarity index 100%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV3Button/Export3ImportSettings.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ImportV3Button/Export3ImportSettings.tsx
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV3Button/Export3UserChoose.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ImportV3Button/Export3UserChoose.tsx
similarity index 100%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV3Button/Export3UserChoose.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ImportV3Button/Export3UserChoose.tsx
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV3Button/index.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ImportV3Button/index.tsx
similarity index 99%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV3Button/index.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ImportV3Button/index.tsx
index 3613d59b..a8aeb71d 100644
--- a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV3Button/index.tsx
+++ b/src/components/pages/serverActions/actions/ImportExportButton/ImportV3Button/index.tsx
@@ -93,8 +93,6 @@ export default function ImportV3Button() {
color: 'green',
icon: ,
});
-
- await fetch('/reload');
}
};
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/Export4Details.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/Export4Details.tsx
similarity index 100%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/Export4Details.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/Export4Details.tsx
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/Export4ImportSettings.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/Export4ImportSettings.tsx
similarity index 100%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/Export4ImportSettings.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/Export4ImportSettings.tsx
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/Export4UserChoose.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/Export4UserChoose.tsx
similarity index 100%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/Export4UserChoose.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/Export4UserChoose.tsx
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/Export4WarningSameInstance.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/Export4WarningSameInstance.tsx
similarity index 100%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/Export4WarningSameInstance.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/Export4WarningSameInstance.tsx
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/index.tsx b/src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/index.tsx
similarity index 90%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/index.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/index.tsx
index f6f7c758..12755afb 100644
--- a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/ImportV4Button/index.tsx
+++ b/src/components/pages/serverActions/actions/ImportExportButton/ImportV4Button/index.tsx
@@ -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(
+ '/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(
'/api/server/import/v4',
'POST',
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/index.tsx b/src/components/pages/serverActions/actions/ImportExportButton/index.tsx
similarity index 67%
rename from src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/index.tsx
rename to src/components/pages/serverActions/actions/ImportExportButton/index.tsx
index 004dfd5d..6692120c 100644
--- a/src/components/pages/settings/parts/SettingsServerUtil/ImportExportButton/index.tsx
+++ b/src/components/pages/serverActions/actions/ImportExportButton/index.tsx
@@ -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() {
- } onClick={() => setOpen(true)}>
- Import / Export Data
-
+ setOpen(true)} Icon={IconDatabasePlus} />
>
);
}
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/RequerySizeButton.tsx b/src/components/pages/serverActions/actions/RequerySizeButton.tsx
similarity index 93%
rename from src/components/pages/settings/parts/SettingsServerUtil/RequerySizeButton.tsx
rename to src/components/pages/serverActions/actions/RequerySizeButton.tsx
index d44d93d1..7b1f25a3 100755
--- a/src/components/pages/settings/parts/SettingsServerUtil/RequerySizeButton.tsx
+++ b/src/components/pages/serverActions/actions/RequerySizeButton.tsx
@@ -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() {
- } onClick={() => setOpen(true)}>
- Requery Size of Files
-
+
+ setOpen(true)} />
>
);
}
diff --git a/src/components/pages/serverActions/index.tsx b/src/components/pages/serverActions/index.tsx
new file mode 100644
index 00000000..80522e70
--- /dev/null
+++ b/src/components/pages/serverActions/index.tsx
@@ -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 (
+ <>
+
+ Server Actions
+
+
+ Useful tools and scripts for server management.
+
+
+ {ACTIONS.map(({ name, desc, Component }) => (
+
+
+
+
+
+
{name}
+ {desc}
+
+
+
+ ))}
+
+ >
+ );
+}
diff --git a/src/components/pages/serverSettings/settingsOnSubmit.tsx b/src/components/pages/serverSettings/settingsOnSubmit.tsx
index b080567b..f50639f3 100644
--- a/src/components/pages/serverSettings/settingsOnSubmit.tsx
+++ b/src/components/pages/serverSettings/settingsOnSubmit.tsx
@@ -39,7 +39,6 @@ export function settingsOnSubmit(navigate: NavigateFunction, form: ReturnType,
});
- await fetch('/reload');
mutate('/api/server/settings', data);
mutate('/api/server/settings/web');
mutate('/api/server/public');
diff --git a/src/components/pages/settings/index.tsx b/src/components/pages/settings/index.tsx
index 9e003ba8..ba1135e1 100755
--- a/src/components/pages/settings/index.tsx
+++ b/src/components/pages/settings/index.tsx
@@ -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() {
-
- {isAdministrator(user?.role) && }
>
);
diff --git a/src/components/pages/settings/parts/SettingsServerUtil/index.tsx b/src/components/pages/settings/parts/SettingsServerUtil/index.tsx
deleted file mode 100755
index bf7ca9d5..00000000
--- a/src/components/pages/settings/parts/SettingsServerUtil/index.tsx
+++ /dev/null
@@ -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 (
-
- Server Actions
-
- Helpful scripts and tools for server management.
-
-
-
-
-
-
-
-
-
-
- );
-}