From 97a7701f5d152e9458ab265392715339fa4bfb58 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Mon, 4 Aug 2025 03:20:18 +0100 Subject: [PATCH 01/46] fix local storage copy function (#1442) --- apps/server/src/integrations/storage/drivers/local.driver.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/server/src/integrations/storage/drivers/local.driver.ts b/apps/server/src/integrations/storage/drivers/local.driver.ts index 36d9ca9e..caa6ab36 100644 --- a/apps/server/src/integrations/storage/drivers/local.driver.ts +++ b/apps/server/src/integrations/storage/drivers/local.driver.ts @@ -40,8 +40,11 @@ export class LocalDriver implements StorageDriver { async copy(fromFilePath: string, toFilePath: string): Promise { try { + const fromFullPath = this._fullPath(fromFilePath); + const toFullPath = this._fullPath(toFilePath); + if (await this.exists(fromFilePath)) { - await fs.copy(fromFilePath, toFilePath); + await fs.copy(fromFullPath, toFullPath); } } catch (err) { throw new Error(`Failed to copy file: ${(err as Error).message}`); From aa6eec754ebce1ca9e962a7311f56c2b309872bf Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Mon, 4 Aug 2025 00:00:06 -0700 Subject: [PATCH 02/46] fix: exclude trashed pages from position generation --- apps/server/src/core/page/services/page.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/server/src/core/page/services/page.service.ts b/apps/server/src/core/page/services/page.service.ts index 3a97f960..d07b6133 100644 --- a/apps/server/src/core/page/services/page.service.ts +++ b/apps/server/src/core/page/services/page.service.ts @@ -109,6 +109,7 @@ export class PageService { .selectFrom('pages') .select(['position']) .where('spaceId', '=', spaceId) + .where('deletedAt', 'is', null) .orderBy('position', 'desc') .limit(1); From dddfd48934ef00fd0b2909053c30587f62dc22cc Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Mon, 4 Aug 2025 08:01:11 +0100 Subject: [PATCH 03/46] feat: add attachments support for single page exports (#1440) * feat: add attachments support for single page exports - Add includeAttachments option to page export modal and API - Fix internal page url in single page exports in cloud * remove redundant line * preserve export state --- .../src/components/common/export-modal.tsx | 23 +++- .../page/components/page-export-modal.tsx | 105 ------------------ .../src/features/page/types/page.types.ts | 1 + .../src/integrations/export/dto/export-dto.ts | 4 + .../integrations/export/export.controller.ts | 34 ++---- .../src/integrations/export/export.service.ts | 33 ++++-- 6 files changed, 58 insertions(+), 142 deletions(-) delete mode 100644 apps/client/src/features/page/components/page-export-modal.tsx diff --git a/apps/client/src/components/common/export-modal.tsx b/apps/client/src/components/common/export-modal.tsx index a53094d4..25f4d328 100644 --- a/apps/client/src/components/common/export-modal.tsx +++ b/apps/client/src/components/common/export-modal.tsx @@ -29,19 +29,22 @@ export default function ExportModal({ }: ExportModalProps) { const [format, setFormat] = useState(ExportFormat.Markdown); const [includeChildren, setIncludeChildren] = useState(false); - const [includeAttachments, setIncludeAttachments] = useState(true); + const [includeAttachments, setIncludeAttachments] = useState(false); const { t } = useTranslation(); const handleExport = async () => { try { if (type === "page") { - await exportPage({ pageId: id, format, includeChildren }); + await exportPage({ + pageId: id, + format, + includeChildren, + includeAttachments, + }); } if (type === "space") { await exportSpace({ spaceId: id, format, includeAttachments }); } - setIncludeChildren(false); - setIncludeAttachments(true); onClose(); } catch (err) { notifications.show({ @@ -96,6 +99,18 @@ export default function ExportModal({ checked={includeChildren} /> + + +
+ {t("Include attachments")} +
+ + setIncludeAttachments(event.currentTarget.checked) + } + checked={includeAttachments} + /> +
)} diff --git a/apps/client/src/features/page/components/page-export-modal.tsx b/apps/client/src/features/page/components/page-export-modal.tsx deleted file mode 100644 index 9b51910f..00000000 --- a/apps/client/src/features/page/components/page-export-modal.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { Modal, Button, Group, Text, Select, Switch } from "@mantine/core"; -import { exportPage } from "@/features/page/services/page-service.ts"; -import { useState } from "react"; -import * as React from "react"; -import { ExportFormat } from "@/features/page/types/page.types.ts"; -import { notifications } from "@mantine/notifications"; -import { useTranslation } from "react-i18next"; - -interface PageExportModalProps { - pageId: string; - open: boolean; - onClose: () => void; -} - -export default function PageExportModal({ - pageId, - open, - onClose, -}: PageExportModalProps) { - const { t } = useTranslation(); - const [format, setFormat] = useState(ExportFormat.Markdown); - - const handleExport = async () => { - try { - await exportPage({ pageId: pageId, format }); - onClose(); - } catch (err) { - notifications.show({ - message: t("Export failed:") + err.response?.data.message, - color: "red", - }); - console.error("export error", err); - } - }; - - const handleChange = (format: ExportFormat) => { - setFormat(format); - }; - - return ( - - - - - {t("Export page")} - - - - -
- {t("Format")} -
- -
- - -
- {t("Include subpages")} -
- -
- - - - - -
-
-
- ); -} - -interface ExportFormatSelection { - format: ExportFormat; - onChange: (value: string) => void; -} -function ExportFormatSelection({ format, onChange }: ExportFormatSelection) { - const { t } = useTranslation(); - - return ( -