Compare commits

...

5 Commits

Author SHA1 Message Date
diced cb2590aae5 feat(v4.5.2): version 2026-03-28 23:58:39 -07:00
diced 93ff18a120 fix: reformat routes to include catch-all 2026-03-28 23:57:51 -07:00
diced 4343f130fb fix: refine batch uploads 2026-03-28 23:36:52 -07:00
diced 5e9778d18a fix: mfa showing when disabled 2026-03-28 20:35:49 -07:00
diced 9bcccbc8aa fix: #1031 2026-03-28 19:41:02 -07:00
9 changed files with 58 additions and 52 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "zipline",
"private": true,
"license": "MIT",
"version": "4.5.1",
"version": "4.5.2",
"scripts": {
"build": "tsx scripts/build.ts",
"dev": "cross-env NODE_ENV=development DEBUG=zipline tsx --require dotenv/config --enable-source-maps ./src/server",
+21 -28
View File
@@ -8,6 +8,11 @@ import FourOhFour from './pages/404';
import Login from './pages/auth/login';
import Root from './Root';
const fourOhFourCatchall = {
path: '*',
Component: FourOhFour,
};
export async function dashboardLoader() {
try {
const res = await fetch('/api/server/settings/web');
@@ -33,17 +38,16 @@ export const router = createBrowserRouter([
{
ErrorBoundary: RootErrorBoundary,
children: [
{ path: '*', Component: FourOhFour },
fourOhFourCatchall,
{
path: '/auth',
children: [
{ path: 'login', Component: Login },
{ path: 'register', lazy: () => import('./pages/auth/register') },
{ path: 'auth/login', Component: Login },
{ path: 'auth/register', lazy: () => import('./pages/auth/register') },
{
path: 'setup',
path: 'auth/setup',
lazy: () => import('./pages/auth/setup'),
},
{ path: 'tos', lazy: () => import('./pages/auth/tos') },
{ path: 'auth/tos', lazy: () => import('./pages/auth/tos') },
],
},
{
@@ -60,37 +64,26 @@ export const router = createBrowserRouter([
{ path: 'files', lazy: () => import('./pages/dashboard/files') },
{ path: 'folders/*', lazy: () => import('./pages/dashboard/folders') },
{ path: 'urls', lazy: () => import('./pages/dashboard/urls') },
{ path: 'upload/file', lazy: () => import('./pages/dashboard/upload/file') },
{ path: 'upload/text', lazy: () => import('./pages/dashboard/upload/text') },
// admin routes
{
path: 'upload',
children: [
{ path: 'file', lazy: () => import('./pages/dashboard/upload/file') },
{ path: 'text', lazy: () => import('./pages/dashboard/upload/text') },
],
},
{
path: 'admin',
loader: async () => {
const res = await fetch('/api/user');
if (!res.ok) {
return redirect('/auth/login');
}
if (!res.ok) return redirect('/auth/login');
const { user } = await res.json();
if (!isAdministrator(user.role)) return redirect('/dashboard');
},
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: 'admin/invites', lazy: () => import('./pages/dashboard/admin/invites') },
{ path: 'admin/settings', lazy: () => import('./pages/dashboard/admin/settings') },
{ path: 'admin/actions', lazy: () => import('./pages/dashboard/admin/actions') },
{ path: 'admin/users', lazy: () => import('./pages/dashboard/admin/users') },
{
path: 'users',
children: [
{ index: true, lazy: () => import('./pages/dashboard/admin/users') },
{
path: ':id/files',
lazy: () => import('./pages/dashboard/admin/users/[id]/files'),
},
],
path: 'admin/users/:id/files',
lazy: () => import('./pages/dashboard/admin/users/[id]/files'),
},
],
},
+1 -1
View File
@@ -41,7 +41,7 @@ export default function DashboardSettings() {
config.oauthEnabled.oidc,
) && <SettingsOAuth />}
{eitherTrue(config.mfa.totp.enabled, config.mfa.passkeys) && <SettingsMfa />}
{eitherTrue(config.mfa.totp.enabled, config.mfa.passkeys.enabled) && <SettingsMfa />}
<SettingsExports />
<SettingsGenerators />
@@ -1,8 +1,8 @@
import { useConfig } from '@/components/ConfigProvider';
import { Response } from '@/lib/api/response';
import { useUserStore } from '@/lib/client/store/user';
import { fetchApi } from '@/lib/fetchApi';
import { findProvider } from '@/lib/oauth/providers';
import { useUserStore } from '@/lib/client/store/user';
import { darken } from '@/lib/theme/color';
import type { OAuthProviderType } from '@/prisma/client';
import { Button, ButtonProps, Paper, SimpleGrid, Text, Title, useMantineTheme } from '@mantine/core';
@@ -68,6 +68,12 @@ function OAuthButton({ provider, linked }: { provider: OAuthProviderType; linked
'--z-bol-color': darken(t.colors?.[provider.toLowerCase()]?.[0] ?? '', 0.2, t),
},
className: !linked ? styles.button : undefined,
styles: {
label: {
whiteSpace: 'normal',
textAlign: 'center',
},
},
};
return linked ? (
+11 -8
View File
@@ -15,6 +15,7 @@ import {
Progress,
Text,
Title,
Tooltip,
rem,
useMantineTheme,
} from '@mantine/core';
@@ -244,14 +245,16 @@ export default function UploadFile({ title, folder }: { title?: string; folder?:
>
Show more
</Button>
<Button
size='compact-sm'
variant='subtle'
disabled={dropLoading}
onClick={() => setVisibleCount(files.length)}
>
Show all
</Button>
<Tooltip label='This may cause performance issues if there are a lot of files' hidden={dropLoading}>
<Button
size='compact-sm'
variant='subtle'
disabled={dropLoading}
onClick={() => setVisibleCount(files.length)}
>
Show all
</Button>
</Tooltip>
</Group>
)}
+12 -10
View File
@@ -69,10 +69,11 @@ export async function uploadFiles(
notifications.show({
id: 'upload',
title: 'Uploading files',
message: `Uploading ${files.length} file${files.length === 1 ? '' : 's'} in ${batches} request${
batches === 1 ? '' : 's'
}`,
title: `Preparing file${files.length > 1 ? 's' : ''}`,
message:
batches > 1
? `Uploading ${files.length} file${files.length > 1 ? 's' : ''} in ${batches} batche${batches > 1 ? 's' : ''}`
: `Uploading ${files.length} file${files.length > 1 ? 's' : ''}`,
loading: true,
autoClose: false,
});
@@ -132,13 +133,14 @@ export async function uploadFiles(
const batchBytes = batchFiles.reduce((acc, file) => acc + file.size, 0);
notifications.update({
id: 'upload',
title: 'Uploading files',
message: `Uploading batch ${batchIndex + 1}/${batches} (${batchFiles.length} file${
batchFiles.length === 1 ? '' : 's'
})`,
title:
batches > 1
? `Uploading batch ${batchIndex + 1}/${batches}`
: `Uploading file${batchFiles.length > 1 ? 's' : ''}`,
message: `${batchFiles.length} file${batchFiles.length > 1 ? 's' : ''}`,
loading: true,
autoClose: false,
id: 'upload',
});
const res = await uploadBatch(batchFiles, completedBytes);
@@ -150,7 +152,7 @@ export async function uploadFiles(
notifications.update({
id: 'upload',
title: 'Uploaded files',
title: 'Upload complete',
message: `Uploaded ${files.length} file${files.length === 1 ? '' : 's'}`,
color: 'green',
icon: <IconFileUpload size='1rem' />,
+1 -1
View File
@@ -121,7 +121,7 @@ export function showUploadModal(
};
modals.open({
title: 'Uploaded files',
title: `Uploaded ${files.length} file${files.length > 1 ? 's' : ''}`,
size: 'auto',
children: (
<>
+3 -1
View File
@@ -41,7 +41,9 @@ async function vitePlugin(fastify: FastifyInstance) {
...reservedRoutes.filter((x) => x !== '/dashboard' && x !== '/auth' && x !== '/r'),
config.files.route,
config.urls.route,
].some((route) => url.startsWith(route));
]
.filter((url) => url.trim() !== '/')
.some((route) => url.startsWith(route));
if (reserved) return;
+1 -1
View File
@@ -19,7 +19,7 @@ export default defineConfig(({ mode }) => {
},
output: {
format: isSSR ? 'cjs' : 'esm',
entryFileNames: isSSR ? `${mode}.js` : '[name]-[hash].js',
entryFileNames: isSSR ? `${mode}.js` : 'assets/[name]-[hash].js',
},
},
target: 'esnext',