diff --git a/src/components/pages/folders/views/FolderTableView.tsx b/src/components/pages/folders/views/FolderTableView.tsx
index ddc24db8..e0901286 100755
--- a/src/components/pages/folders/views/FolderTableView.tsx
+++ b/src/components/pages/folders/views/FolderTableView.tsx
@@ -16,6 +16,7 @@ import {
IconShare,
IconShareOff,
IconTrashFilled,
+ IconZip,
} from '@tabler/icons-react';
import ViewFilesModal from '../ViewFilesModal';
import EditFolderNameModal from '../EditFolderNameModal';
@@ -169,6 +170,14 @@ export default function FolderTableView() {
+
+ window.open(`/api/user/folders/${folder.id}/export`, '_blank')}
+ >
+
+
+
{
+ server.get<{ Params: Params }>(PATH, { preHandler: [userMiddleware] }, async (req, res) => {
+ const { id } = req.params;
+
+ const folder = await prisma.folder.findUnique({
+ where: {
+ id,
+ },
+ include: {
+ files: true,
+ },
+ });
+ if (!folder) return res.notFound('Folder not found');
+ if (req.user.id !== folder.userId) return res.forbidden('You do not own this folder');
+
+ if (!folder.files.length) return res.badRequest("Can't export an empty folder.");
+
+ logger.info(`folder export requested: ${folder.name}`, { user: req.user.id, folder: folder.id });
+
+ res.hijack();
+
+ const zip = archiver('zip', {
+ zlib: { level: 9 },
+ });
+
+ zip.pipe(res.raw);
+
+ for (const file of folder.files) {
+ const stream = await datasource.get(file.name);
+ if (!stream) {
+ logger.warn('failed to get file stream for folder export', { file: file.id, folder: folder.id });
+ continue;
+ }
+
+ zip.append(stream, { name: file.name });
+ }
+
+ zip.on('error', (err) => {
+ logger.error('error during folder export zip creation', { folder: folder.id }).error(err as Error);
+ });
+
+ zip.on('finish', () => {
+ logger.info(`folder export completed: ${folder.name}`, { user: req.user.id, folder: folder.id });
+ });
+
+ await zip.finalize();
+ });
+ done();
+ },
+ { name: PATH },
+);
diff --git a/src/server/routes/api/user/folders/[id].ts b/src/server/routes/api/user/folders/[id]/index.ts
similarity index 100%
rename from src/server/routes/api/user/folders/[id].ts
rename to src/server/routes/api/user/folders/[id]/index.ts