From cbf03fc956be5af4035f4d914101feb36eee7c31 Mon Sep 17 00:00:00 2001
From: Philipinho <16838612+Philipinho@users.noreply.github.com>
Date: Wed, 22 Apr 2026 12:58:19 +0100
Subject: [PATCH] fix
---
apps/client/src/App.tsx | 5 +
.../components/settings/settings-sidebar.tsx | 8 +
.../components/confluence-import-history.tsx | 205 ++++++++
.../components/confluence-import-modal.tsx | 441 ++++++++++++++++++
.../pages/confluence-import.tsx | 67 +++
.../queries/confluence-import-queries.ts | 17 +
.../services/confluence-import-service.ts | 64 +++
.../types/confluence-import.types.ts | 82 ++++
apps/client/vite.config.ts | 1 +
.../server/src/database/types/custom.types.ts | 1 +
apps/server/src/ee | 2 +-
.../import/processors/file-task.processor.ts | 18 +
.../queue/constants/queue.constants.ts | 1 +
13 files changed, 911 insertions(+), 1 deletion(-)
create mode 100644 apps/client/src/ee/confluence-import/components/confluence-import-history.tsx
create mode 100644 apps/client/src/ee/confluence-import/components/confluence-import-modal.tsx
create mode 100644 apps/client/src/ee/confluence-import/pages/confluence-import.tsx
create mode 100644 apps/client/src/ee/confluence-import/queries/confluence-import-queries.ts
create mode 100644 apps/client/src/ee/confluence-import/services/confluence-import-service.ts
create mode 100644 apps/client/src/ee/confluence-import/types/confluence-import.types.ts
diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx
index a75afc223..e5fae1bed 100644
--- a/apps/client/src/App.tsx
+++ b/apps/client/src/App.tsx
@@ -45,6 +45,7 @@ import TemplateEditor from "@/ee/template/pages/template-editor";
import FavoritesPage from "@/pages/favorites/favorites-page";
import AiChat from "@/ee/ai-chat/pages/ai-chat.tsx";
import VerifyEmail from "@/ee/pages/verify-email.tsx";
+import ConfluenceImportPage from "@/ee/confluence-import/pages/confluence-import.tsx";
export default function App() {
const { t } = useTranslation();
@@ -123,6 +124,10 @@ export default function App() {
} />
} />
} />
+ }
+ />
{!isCloud() && } />}
{isCloud() && } />}
diff --git a/apps/client/src/components/settings/settings-sidebar.tsx b/apps/client/src/components/settings/settings-sidebar.tsx
index 90d89d13e..6de6890e8 100644
--- a/apps/client/src/components/settings/settings-sidebar.tsx
+++ b/apps/client/src/components/settings/settings-sidebar.tsx
@@ -15,6 +15,7 @@ import {
IconSparkles,
IconHistory,
IconShieldCheck,
+ IconFileImport,
} from "@tabler/icons-react";
import { Link, useLocation } from "react-router-dom";
import classes from "./settings.module.css";
@@ -124,6 +125,13 @@ const groupedData: DataGroup[] = [
role: "owner",
env: "selfhosted",
},
+ {
+ label: "Import",
+ icon: IconFileImport,
+ path: "/settings/import/confluence",
+ feature: Feature.CONFLUENCE_IMPORT,
+ role: "admin",
+ },
],
},
{
diff --git a/apps/client/src/ee/confluence-import/components/confluence-import-history.tsx b/apps/client/src/ee/confluence-import/components/confluence-import-history.tsx
new file mode 100644
index 000000000..6c8acdc1d
--- /dev/null
+++ b/apps/client/src/ee/confluence-import/components/confluence-import-history.tsx
@@ -0,0 +1,205 @@
+import { useMemo } from "react";
+import {
+ Badge,
+ Group,
+ Loader,
+ Progress,
+ Skeleton,
+ Stack,
+ Table,
+ Text,
+ Tooltip,
+} from "@mantine/core";
+import { IconAlertCircle, IconCheck, IconX } from "@tabler/icons-react";
+import { useTranslation } from "react-i18next";
+import {
+ ConfluenceImportHistoryItem,
+ ConfluenceImportStatus,
+} from "@/ee/confluence-import/types/confluence-import.types";
+import { CustomAvatar } from "@/components/ui/custom-avatar";
+import { formattedDate } from "@/lib/time";
+import NoTableResults from "@/components/common/no-table-results";
+import { useConfluenceImportsQuery } from "@/ee/confluence-import/queries/confluence-import-queries";
+
+function statusBadge(status: ConfluenceImportStatus, cancelled: boolean) {
+ if (cancelled) {
+ return (
+ }>
+ Cancelled
+
+ );
+ }
+ if (status === "processing") {
+ return (
+ }>
+ Running
+
+ );
+ }
+ if (status === "success") {
+ return (
+ }>
+ Completed
+
+ );
+ }
+ return (
+ }
+ >
+ Failed
+
+ );
+}
+
+function phaseLabel(phase: string | null): string {
+ if (!phase) return "—";
+ return phase.charAt(0).toUpperCase() + phase.slice(1);
+}
+
+function progressValue(item: ConfluenceImportHistoryItem) {
+ if (item.status === "success") return 100;
+ if (item.totalPages > 0) {
+ return Math.min(
+ 100,
+ Math.round((item.importedPages / item.totalPages) * 100),
+ );
+ }
+ return item.status === "processing" ? 5 : 0;
+}
+
+function ProgressCell({ item }: { item: ConfluenceImportHistoryItem }) {
+ const value = progressValue(item);
+ const color =
+ item.status === "failed"
+ ? "red"
+ : item.status === "success"
+ ? "teal"
+ : "blue";
+
+ return (
+
+
+
+
+ {item.importedPages}/{item.totalPages || "?"} pages
+
+
+ · {item.importedSpaces}/{item.totalSpaces || "?"} spaces
+
+
+
+ );
+}
+
+function TableSkeleton() {
+ return (
+ <>
+ {Array.from({ length: 3 }).map((_, i) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+ >
+ );
+}
+
+export default function ConfluenceImportHistory() {
+ const { t } = useTranslation();
+ const { data, isLoading } = useConfluenceImportsQuery();
+
+ const items = useMemo(() => data?.items ?? [], [data]);
+
+ return (
+
+
+
+
+ {t("Status")}
+ {t("Site")}
+ {t("Phase")}
+ {t("Progress")}
+ {t("Started by")}
+ {t("Started at")}
+
+
+
+
+ {isLoading ? (
+
+ ) : items.length > 0 ? (
+ items.map((item) => (
+
+
+ {statusBadge(item.status, item.cancelled)}
+ {item.status === "failed" && item.errorMessage && (
+
+
+ {item.errorMessage}
+
+
+ )}
+
+
+
+ {item.siteUrl}
+
+
+
+ {phaseLabel(item.currentPhase)}
+
+
+
+
+
+ {item.creatorName ? (
+
+
+
+ {item.creatorName}
+
+
+ ) : (
+
+ —
+
+ )}
+
+
+
+ {formattedDate(new Date(item.createdAt))}
+
+
+
+ ))
+ ) : (
+
+ )}
+
+
+
+ );
+}
diff --git a/apps/client/src/ee/confluence-import/components/confluence-import-modal.tsx b/apps/client/src/ee/confluence-import/components/confluence-import-modal.tsx
new file mode 100644
index 000000000..39b3a502a
--- /dev/null
+++ b/apps/client/src/ee/confluence-import/components/confluence-import-modal.tsx
@@ -0,0 +1,441 @@
+import React, { useEffect, useMemo, useState } from "react";
+import {
+ Alert,
+ Button,
+ Checkbox,
+ Group,
+ Modal,
+ PasswordInput,
+ ScrollArea,
+ SegmentedControl,
+ Stack,
+ Stepper,
+ Text,
+ TextInput,
+} from "@mantine/core";
+import {
+ IconAlertCircle,
+ IconCheck,
+ IconCloudCheck,
+ IconPlug,
+} from "@tabler/icons-react";
+import { useTranslation } from "react-i18next";
+import { useForm } from "@mantine/form";
+import { notifications } from "@mantine/notifications";
+import { useQueryClient } from "@tanstack/react-query";
+import {
+ listConfluenceSpaces,
+ startConfluenceImport,
+ testConfluenceConnection,
+} from "@/ee/confluence-import/services/confluence-import-service";
+import {
+ ConfluenceAuthType,
+ ConfluenceCredentials,
+ ConfluenceSpaceSummary,
+} from "@/ee/confluence-import/types/confluence-import.types";
+import { confluenceImportsQueryKey } from "@/ee/confluence-import/queries/confluence-import-queries";
+
+type ConfluenceEditionChoice = "cloud" | "server";
+
+type CredentialsFormValues = {
+ edition: ConfluenceEditionChoice;
+ authType: ConfluenceAuthType;
+ siteUrl: string;
+ email: string;
+ token: string;
+ username: string;
+ password: string;
+};
+
+type Props = {
+ opened: boolean;
+ onClose: () => void;
+};
+
+export default function ConfluenceImportModal({ opened, onClose }: Props) {
+ const { t } = useTranslation();
+ const queryClient = useQueryClient();
+ const [active, setActive] = useState(0);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [spaces, setSpaces] = useState([]);
+ const [selectedKeys, setSelectedKeys] = useState([]);
+ const [importAll, setImportAll] = useState(true);
+
+ const form = useForm({
+ initialValues: {
+ edition: "server",
+ authType: "pat",
+ siteUrl: "",
+ email: "",
+ token: "",
+ username: "",
+ password: "",
+ },
+ validate: {
+ siteUrl: (value) =>
+ !value?.trim()
+ ? t("Site URL is required")
+ : !/^https?:\/\//i.test(value.trim())
+ ? t("Site URL must start with http:// or https://")
+ : null,
+ email: (value, values) =>
+ values.edition === "cloud" && !value?.trim()
+ ? t("Email is required")
+ : null,
+ token: (value, values) =>
+ (values.authType === "cloud_token" || values.authType === "pat") &&
+ !value?.trim()
+ ? t("API token is required")
+ : null,
+ username: (value, values) =>
+ values.authType === "basic" && !value?.trim()
+ ? t("Username is required")
+ : null,
+ password: (value, values) =>
+ values.authType === "basic" && !value?.trim()
+ ? t("Password is required")
+ : null,
+ },
+ });
+
+ useEffect(() => {
+ if (!opened) {
+ setActive(0);
+ setError(null);
+ setSpaces([]);
+ setSelectedKeys([]);
+ setImportAll(true);
+ setLoading(false);
+ form.reset();
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [opened]);
+
+ const credentials: ConfluenceCredentials = useMemo(() => {
+ const values = form.values;
+ return {
+ siteUrl: values.siteUrl.trim().replace(/\/+$/, ""),
+ authType: values.authType,
+ email: values.email?.trim() || undefined,
+ token: values.token?.trim() || undefined,
+ username: values.username?.trim() || undefined,
+ password: values.password || undefined,
+ };
+ }, [form.values]);
+
+ const handleEditionChange = (edition: ConfluenceEditionChoice) => {
+ form.setFieldValue("edition", edition);
+ if (edition === "cloud") {
+ form.setFieldValue("authType", "cloud_token");
+ } else if (form.values.authType === "cloud_token") {
+ form.setFieldValue("authType", "pat");
+ }
+ };
+
+ const handleNextFromCredentials = async () => {
+ if (form.validate().hasErrors) return;
+ setLoading(true);
+ setError(null);
+ try {
+ const test = await testConfluenceConnection(credentials);
+ if (!test.success) {
+ setError(test.error || t("Connection failed"));
+ return;
+ }
+ const list = await listConfluenceSpaces(credentials);
+ if (!list.success || !list.spaces) {
+ setError(list.error || t("Failed to load spaces"));
+ return;
+ }
+ setSpaces(list.spaces);
+ setSelectedKeys(list.spaces.map((s) => s.key));
+ setImportAll(true);
+ setActive(1);
+ } catch (err: any) {
+ setError(err?.response?.data?.message || err?.message || t("Unexpected error"));
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const toggleSpace = (key: string, checked: boolean) => {
+ setSelectedKeys((prev) =>
+ checked ? Array.from(new Set([...prev, key])) : prev.filter((k) => k !== key),
+ );
+ };
+
+ const toggleAll = (checked: boolean) => {
+ setImportAll(checked);
+ setSelectedKeys(checked ? spaces.map((s) => s.key) : []);
+ };
+
+ const handleStartImport = async () => {
+ const spaceKeys = importAll ? [] : selectedKeys;
+ if (!importAll && spaceKeys.length === 0) {
+ setError(t("Select at least one space to import"));
+ return;
+ }
+
+ setLoading(true);
+ setError(null);
+ try {
+ const result = await startConfluenceImport({
+ ...credentials,
+ spaceKeys,
+ });
+ if (!result.success || !result.fileTaskId) {
+ setError(result.error || t("Failed to start import"));
+ setLoading(false);
+ return;
+ }
+
+ await queryClient.invalidateQueries({
+ queryKey: confluenceImportsQueryKey,
+ });
+
+ notifications.show({
+ title: t("Confluence import started"),
+ message: t("Track progress below. This runs in the background."),
+ color: "blue",
+ icon: ,
+ autoClose: 4000,
+ });
+
+ onClose();
+ } catch (err: any) {
+ setError(
+ err?.response?.data?.message || err?.message || t("Unexpected error"),
+ );
+ setLoading(false);
+ }
+ };
+
+ const handleCancelFlow = async () => {
+ onClose();
+ };
+
+ const editionSegment = (
+ handleEditionChange(val as ConfluenceEditionChoice)}
+ data={[
+ { value: "server", label: t("Data Center / Server") },
+ { value: "cloud", label: t("Cloud") },
+ ]}
+ fullWidth
+ />
+ );
+
+ const authTypeSegment = form.values.edition === "server" && (
+
+ form.setFieldValue("authType", val as ConfluenceAuthType)
+ }
+ data={[
+ { value: "pat", label: t("Personal Access Token") },
+ { value: "basic", label: t("Username + password") },
+ ]}
+ fullWidth
+ />
+ );
+
+ const selectedCount = importAll ? spaces.length : selectedKeys.length;
+
+ return (
+
+
+ }
+ />
+ }
+ />
+
+
+ {active === 0 && (
+
+
+ {t(
+ "Enter your Confluence URL and credentials. We'll validate the connection before continuing.",
+ )}
+
+ {editionSegment}
+ {authTypeSegment}
+
+
+ {form.values.edition === "cloud" && (
+ <>
+
+
+ >
+ )}
+
+ {form.values.edition === "server" &&
+ form.values.authType === "pat" && (
+ <>
+
+
+ >
+ )}
+
+ {form.values.edition === "server" &&
+ form.values.authType === "basic" && (
+ <>
+
+
+
+ >
+ )}
+
+ {error && (
+ }>
+ {error}
+
+ )}
+
+
+
+
+
+
+ )}
+
+ {active === 1 && (
+
+
+ {t(
+ "Choose the spaces to import. Users, groups and permissions will be imported for the selected spaces.",
+ )}
+
+
+ toggleAll(e.currentTarget.checked)}
+ />
+
+
+
+ {spaces.map((space) => (
+
+ {space.name}
+
+ ({space.key})
+
+
+ }
+ checked={importAll || selectedKeys.includes(space.key)}
+ disabled={importAll}
+ onChange={(e) =>
+ toggleSpace(space.key, e.currentTarget.checked)
+ }
+ />
+ ))}
+ {spaces.length === 0 && (
+
+ {t("No spaces found for this account.")}
+
+ )}
+
+
+
+ {error && (
+ }>
+ {error}
+
+ )}
+
+
+
+ {t("{{count}} selected", { count: selectedCount })}
+
+
+
+
+
+
+
+ )}
+
+
+ );
+}
diff --git a/apps/client/src/ee/confluence-import/pages/confluence-import.tsx b/apps/client/src/ee/confluence-import/pages/confluence-import.tsx
new file mode 100644
index 000000000..9e48bd7e9
--- /dev/null
+++ b/apps/client/src/ee/confluence-import/pages/confluence-import.tsx
@@ -0,0 +1,67 @@
+import { Helmet } from "react-helmet-async";
+import { useTranslation } from "react-i18next";
+import {
+ Button,
+ Divider,
+ Group,
+ Paper,
+ Stack,
+ Text,
+ Tooltip,
+} from "@mantine/core";
+import { useDisclosure } from "@mantine/hooks";
+import SettingsTitle from "@/components/settings/settings-title";
+import { ConfluenceIcon } from "@/components/icons/confluence-icon";
+import ConfluenceImportModal from "@/ee/confluence-import/components/confluence-import-modal";
+import ConfluenceImportHistory from "@/ee/confluence-import/components/confluence-import-history";
+import { getAppName } from "@/lib/config";
+import { useHasFeature } from "@/ee/hooks/use-feature";
+import { Feature } from "@/ee/features";
+import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label";
+
+export default function ConfluenceImportPage() {
+ const { t } = useTranslation();
+ const [opened, { open, close }] = useDisclosure(false);
+ const hasConfluenceImport = useHasFeature(Feature.CONFLUENCE_IMPORT);
+ const upgradeLabel = useUpgradeLabel();
+
+ return (
+ <>
+
+
+ {t("Import from Confluence")} - {getAppName()}
+
+
+
+
+
+
+
+
+
+
+ {t("Confluence API import")}
+
+ {t(
+ "Connect to Confluence Cloud or Data Center to import spaces, pages, attachments, comments, users, groups and permissions directly via the API.",
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/client/src/ee/confluence-import/queries/confluence-import-queries.ts b/apps/client/src/ee/confluence-import/queries/confluence-import-queries.ts
new file mode 100644
index 000000000..6cf7921bb
--- /dev/null
+++ b/apps/client/src/ee/confluence-import/queries/confluence-import-queries.ts
@@ -0,0 +1,17 @@
+import { useQuery } from "@tanstack/react-query";
+import { listConfluenceImports } from "@/ee/confluence-import/services/confluence-import-service";
+
+export const confluenceImportsQueryKey = ["confluence-imports"] as const;
+
+export function useConfluenceImportsQuery() {
+ return useQuery({
+ queryKey: confluenceImportsQueryKey,
+ queryFn: listConfluenceImports,
+ refetchInterval: (query) => {
+ const hasRunning = query.state.data?.items?.some(
+ (i) => i.status === "processing",
+ );
+ return hasRunning ? 3000 : false;
+ },
+ });
+}
diff --git a/apps/client/src/ee/confluence-import/services/confluence-import-service.ts b/apps/client/src/ee/confluence-import/services/confluence-import-service.ts
new file mode 100644
index 000000000..14cc4619d
--- /dev/null
+++ b/apps/client/src/ee/confluence-import/services/confluence-import-service.ts
@@ -0,0 +1,64 @@
+import api from "@/lib/api-client";
+import {
+ ConfluenceCredentials,
+ ImportStatusResponse,
+ ListImportsResponse,
+ ListSpacesResponse,
+ StartImportResponse,
+ TestConnectionResponse,
+} from "@/ee/confluence-import/types/confluence-import.types";
+
+export async function testConfluenceConnection(
+ data: ConfluenceCredentials,
+): Promise {
+ const req = await api.post(
+ "/confluence-import/test-connection",
+ data,
+ );
+ return req.data;
+}
+
+export async function listConfluenceSpaces(
+ data: ConfluenceCredentials,
+): Promise {
+ const req = await api.post(
+ "/confluence-import/spaces",
+ data,
+ );
+ return req.data;
+}
+
+export async function startConfluenceImport(
+ data: ConfluenceCredentials & { spaceKeys?: string[] },
+): Promise {
+ const req = await api.post(
+ "/confluence-import/start",
+ data,
+ );
+ return req.data;
+}
+
+export async function getConfluenceImportStatus(
+ fileTaskId: string,
+): Promise {
+ const req = await api.post(
+ "/confluence-import/status",
+ { fileTaskId },
+ );
+ return req.data;
+}
+
+export async function listConfluenceImports(): Promise {
+ const req = await api.post("/confluence-import/list");
+ return req.data;
+}
+
+export async function cancelConfluenceImport(
+ fileTaskId: string,
+): Promise<{ success: boolean }> {
+ const req = await api.post<{ success: boolean }>(
+ "/confluence-import/cancel",
+ { fileTaskId },
+ );
+ return req.data;
+}
diff --git a/apps/client/src/ee/confluence-import/types/confluence-import.types.ts b/apps/client/src/ee/confluence-import/types/confluence-import.types.ts
new file mode 100644
index 000000000..d70ab5a3e
--- /dev/null
+++ b/apps/client/src/ee/confluence-import/types/confluence-import.types.ts
@@ -0,0 +1,82 @@
+export type ConfluenceAuthType = "cloud_token" | "pat" | "basic";
+
+export type ConfluenceCredentials = {
+ siteUrl: string;
+ authType: ConfluenceAuthType;
+ email?: string;
+ token?: string;
+ username?: string;
+ password?: string;
+};
+
+export type ConfluenceSpaceSummary = {
+ id: string;
+ key: string;
+ name: string;
+ type?: string;
+ status?: string;
+};
+
+export type TestConnectionResponse = {
+ success: boolean;
+ edition?: string;
+ spaceCount?: number;
+ error?: string;
+};
+
+export type ListSpacesResponse = {
+ success: boolean;
+ spaces?: ConfluenceSpaceSummary[];
+ error?: string;
+};
+
+export type StartImportResponse = {
+ success: boolean;
+ fileTaskId?: string;
+ error?: string;
+};
+
+export type ConfluenceImportStatus = "processing" | "success" | "failed";
+
+export type ImportStatusResponse = {
+ fileTaskId?: string;
+ status?: ConfluenceImportStatus;
+ errorMessage?: string | null;
+ currentPhase?: string | null;
+ totalSpaces?: number;
+ importedSpaces?: number;
+ totalPages?: number;
+ importedPages?: number;
+ totalUsers?: number;
+ importedUsers?: number;
+ warnings?: string[];
+ createdAt?: string;
+ updatedAt?: string;
+ error?: string;
+};
+
+export type ConfluenceImportHistoryItem = {
+ fileTaskId: string;
+ siteUrl: string;
+ status: ConfluenceImportStatus;
+ errorMessage: string | null;
+ currentPhase: string | null;
+ totalSpaces: number;
+ importedSpaces: number;
+ totalPages: number;
+ importedPages: number;
+ totalUsers: number;
+ importedUsers: number;
+ cancelled: boolean;
+ spaceKeys: string[];
+ warnings: string[];
+ createdAt: string;
+ updatedAt: string;
+ creatorId: string | null;
+ creatorName: string | null;
+ creatorAvatarUrl: string | null;
+};
+
+export type ListImportsResponse = {
+ items: ConfluenceImportHistoryItem[];
+};
diff --git a/apps/client/vite.config.ts b/apps/client/vite.config.ts
index e6f9de48c..48739f80f 100644
--- a/apps/client/vite.config.ts
+++ b/apps/client/vite.config.ts
@@ -55,6 +55,7 @@ export default defineConfig(({ mode }) => {
},
},
server: {
+ allowedHosts: ['docmost.nz'],
proxy: {
"/api": {
target: APP_URL,
diff --git a/apps/server/src/database/types/custom.types.ts b/apps/server/src/database/types/custom.types.ts
index 2f7acaef3..cde6ea7bf 100644
--- a/apps/server/src/database/types/custom.types.ts
+++ b/apps/server/src/database/types/custom.types.ts
@@ -18,6 +18,7 @@ export interface ConfluenceApiImports {
warnings: Generated;
currentPhase: string | null;
cancelled: Generated;
+ spaceKeys: Generated;
workspaceId: string;
creatorId: string | null;
createdAt: Generated;
diff --git a/apps/server/src/ee b/apps/server/src/ee
index 7d1b155d5..ae15159b8 160000
--- a/apps/server/src/ee
+++ b/apps/server/src/ee
@@ -1 +1 @@
-Subproject commit 7d1b155d5f8651c6297d08b200ca8b85502a827a
+Subproject commit ae15159b8d3811d85b2b52384ced21e0dc6e007d
diff --git a/apps/server/src/integrations/import/processors/file-task.processor.ts b/apps/server/src/integrations/import/processors/file-task.processor.ts
index 03527707b..35c722461 100644
--- a/apps/server/src/integrations/import/processors/file-task.processor.ts
+++ b/apps/server/src/integrations/import/processors/file-task.processor.ts
@@ -28,6 +28,9 @@ export class FileTaskProcessor extends WorkerHost implements OnModuleDestroy {
case QueueJob.IMPORT_TASK:
await this.fileTaskService.processZIpImport(job.data.fileTaskId);
break;
+ case QueueJob.CONFLUENCE_API_IMPORT:
+ await this.processConfluenceApiImport(job.data.fileTaskId);
+ break;
case QueueJob.PDF_EXPORT_TASK:
await this.processExportTask(job.data.fileTaskId);
break;
@@ -49,6 +52,19 @@ export class FileTaskProcessor extends WorkerHost implements OnModuleDestroy {
});
}
+ private getConfluenceApiImportService() {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const mod = require('./../../../ee/confluence-api-import/confluence-api-import.service');
+ return this.moduleRef.get(mod.ConfluenceApiImportService, {
+ strict: false,
+ });
+ }
+
+ private async processConfluenceApiImport(fileTaskId: string): Promise {
+ const service = this.getConfluenceApiImportService();
+ await service.processImport(fileTaskId);
+ }
+
private async processExportTask(fileTaskId: string): Promise {
const pdfExportService = this.getPdfExportService();
await pdfExportService.generateAndStorePdf(fileTaskId);
@@ -74,6 +90,8 @@ export class FileTaskProcessor extends WorkerHost implements OnModuleDestroy {
await this.handleFailedImportJob(job);
} else if (job.name === QueueJob.PDF_EXPORT_TASK) {
await this.handleFailedExportJob(job);
+ } else if (job.name === QueueJob.CONFLUENCE_API_IMPORT) {
+ await this.handleFailedExportJob(job);
}
}
diff --git a/apps/server/src/integrations/queue/constants/queue.constants.ts b/apps/server/src/integrations/queue/constants/queue.constants.ts
index c783ec05e..77d70adf4 100644
--- a/apps/server/src/integrations/queue/constants/queue.constants.ts
+++ b/apps/server/src/integrations/queue/constants/queue.constants.ts
@@ -30,6 +30,7 @@ export enum QueueJob {
FIRST_PAYMENT_EMAIL = 'first-payment-email',
IMPORT_TASK = 'import-task',
+ CONFLUENCE_API_IMPORT = 'confluence-api-import-task',
EXPORT_TASK = 'export-task',
SEARCH_INDEX_PAGE = 'search-index-page',