refactor(import): remove non-functional DOCX/PDF/Confluence import stubs
These import paths relied on the private EE module that was deleted from the repo. In the community build they either threw 'enterprise license' (DOCX/PDF) or silently no-op'd (Confluence). The frontend buttons were already removed in 38064064; this cleans up the dead backend stubs. - import.service.ts: drop processDocx/processPdf methods, their dispatcher branches, the pageId computation + insertPage spread, and the now-unused moduleRef param/ModuleRef import - file-import-task.service.ts: drop the Confluence branch and the now-unused moduleRef param/ModuleRef import - import.controller.ts: restrict file extensions to .md/.html and zip sources to generic/notion; update the error message accordingly - file.utils.ts: remove Confluence from the FileImportSource enum - features.ts: remove the unused CONFLUENCE_IMPORT/DOCX_IMPORT/PDF_IMPORT feature keys The isConfluenceImport logic in import-attachment.service.ts is intentionally left in place (real shared attachment-parsing code, not a stub); its removal is a separate, riskier refactor.
This commit is contained in:
@@ -6,9 +6,6 @@ export const Feature = {
|
|||||||
COMMENT_RESOLUTION: 'comment:resolution',
|
COMMENT_RESOLUTION: 'comment:resolution',
|
||||||
PAGE_PERMISSIONS: 'page:permissions',
|
PAGE_PERMISSIONS: 'page:permissions',
|
||||||
AI: 'ai',
|
AI: 'ai',
|
||||||
CONFLUENCE_IMPORT: 'import:confluence',
|
|
||||||
DOCX_IMPORT: 'import:docx',
|
|
||||||
PDF_IMPORT: 'import:pdf',
|
|
||||||
ATTACHMENT_INDEXING: 'attachment:indexing',
|
ATTACHMENT_INDEXING: 'attachment:indexing',
|
||||||
SECURITY_SETTINGS: 'security:settings',
|
SECURITY_SETTINGS: 'security:settings',
|
||||||
MCP: 'mcp',
|
MCP: 'mcp',
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export class ImportController {
|
|||||||
@AuthUser() user: User,
|
@AuthUser() user: User,
|
||||||
@AuthWorkspace() workspace: Workspace,
|
@AuthWorkspace() workspace: Workspace,
|
||||||
) {
|
) {
|
||||||
const validFileExtensions = ['.md', '.html', '.docx', '.pdf'];
|
const validFileExtensions = ['.md', '.html'];
|
||||||
|
|
||||||
const maxFileSize = bytes('30mb');
|
const maxFileSize = bytes('30mb');
|
||||||
|
|
||||||
@@ -101,8 +101,6 @@ export class ImportController {
|
|||||||
const sourceMap: Record<string, string> = {
|
const sourceMap: Record<string, string> = {
|
||||||
'.md': 'markdown',
|
'.md': 'markdown',
|
||||||
'.html': 'html',
|
'.html': 'html',
|
||||||
'.docx': 'docx',
|
|
||||||
'.pdf': 'pdf',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (createdPage) {
|
if (createdPage) {
|
||||||
@@ -161,10 +159,10 @@ export class ImportController {
|
|||||||
const spaceId = file.fields?.spaceId?.value;
|
const spaceId = file.fields?.spaceId?.value;
|
||||||
const source = file.fields?.source?.value;
|
const source = file.fields?.source?.value;
|
||||||
|
|
||||||
const validZipSources = ['generic', 'notion', 'confluence'];
|
const validZipSources = ['generic', 'notion'];
|
||||||
if (!validZipSources.includes(source)) {
|
if (!validZipSources.includes(source)) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
'Invalid import source. Import source must either be generic, notion or confluence.',
|
'Invalid import source. Import source must either be generic or notion.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import {
|
|||||||
import { executeTx } from '@docmost/db/utils';
|
import { executeTx } from '@docmost/db/utils';
|
||||||
import { BacklinkRepo } from '@docmost/db/repos/backlink/backlink.repo';
|
import { BacklinkRepo } from '@docmost/db/repos/backlink/backlink.repo';
|
||||||
import { ImportAttachmentService } from './import-attachment.service';
|
import { ImportAttachmentService } from './import-attachment.service';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
|
||||||
import { PageService } from '../../../core/page/services/page.service';
|
import { PageService } from '../../../core/page/services/page.service';
|
||||||
import { ImportPageNode } from '../dto/file-task-dto';
|
import { ImportPageNode } from '../dto/file-task-dto';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
@@ -54,7 +53,6 @@ export class FileImportTaskService {
|
|||||||
private readonly backlinkRepo: BacklinkRepo,
|
private readonly backlinkRepo: BacklinkRepo,
|
||||||
@InjectKysely() private readonly db: KyselyDB,
|
@InjectKysely() private readonly db: KyselyDB,
|
||||||
private readonly importAttachmentService: ImportAttachmentService,
|
private readonly importAttachmentService: ImportAttachmentService,
|
||||||
private moduleRef: ModuleRef,
|
|
||||||
private eventEmitter: EventEmitter2,
|
private eventEmitter: EventEmitter2,
|
||||||
@Inject(AUDIT_SERVICE) private readonly auditService: IAuditService,
|
@Inject(AUDIT_SERVICE) private readonly auditService: IAuditService,
|
||||||
) {}
|
) {}
|
||||||
@@ -115,27 +113,6 @@ export class FileImportTaskService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileTask.source === FileImportSource.Confluence) {
|
|
||||||
let ConfluenceModule: any;
|
|
||||||
try {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
||||||
ConfluenceModule = require('./../../../ee/confluence-import/confluence-import.service');
|
|
||||||
} catch (err) {
|
|
||||||
this.logger.error(
|
|
||||||
'Confluence import requested but EE module not bundled in this build',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const confluenceImportService = this.moduleRef.get(
|
|
||||||
ConfluenceModule.ConfluenceImportService,
|
|
||||||
{ strict: false },
|
|
||||||
);
|
|
||||||
|
|
||||||
await confluenceImportService.processConfluenceImport({
|
|
||||||
extractDir: tmpExtractDir,
|
|
||||||
fileTask,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await this.updateTaskStatus(fileTaskId, FileTaskStatus.Success, null);
|
await this.updateTaskStatus(fileTaskId, FileTaskStatus.Success, null);
|
||||||
await cleanupTmpFile();
|
await cleanupTmpFile();
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import { StorageService } from '../../storage/storage.service';
|
|||||||
import { InjectQueue } from '@nestjs/bullmq';
|
import { InjectQueue } from '@nestjs/bullmq';
|
||||||
import { Queue } from 'bullmq';
|
import { Queue } from 'bullmq';
|
||||||
import { QueueJob, QueueName } from '../../queue/constants';
|
import { QueueJob, QueueName } from '../../queue/constants';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
|
||||||
import { load } from 'cheerio';
|
import { load } from 'cheerio';
|
||||||
import { normalizeImportHtml } from '../utils/import-formatter';
|
import { normalizeImportHtml } from '../utils/import-formatter';
|
||||||
|
|
||||||
@@ -42,7 +41,6 @@ export class ImportService {
|
|||||||
@InjectKysely() private readonly db: KyselyDB,
|
@InjectKysely() private readonly db: KyselyDB,
|
||||||
@InjectQueue(QueueName.FILE_TASK_QUEUE)
|
@InjectQueue(QueueName.FILE_TASK_QUEUE)
|
||||||
private readonly fileTaskQueue: Queue,
|
private readonly fileTaskQueue: Queue,
|
||||||
private moduleRef: ModuleRef,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async importPage(
|
async importPage(
|
||||||
@@ -62,33 +60,11 @@ export class ImportService {
|
|||||||
let prosemirrorState = null;
|
let prosemirrorState = null;
|
||||||
let createdPage = null;
|
let createdPage = null;
|
||||||
|
|
||||||
// For DOCX, we need the page ID upfront so images can reference it
|
|
||||||
const pageId =
|
|
||||||
fileExtension === '.docx' || fileExtension === '.pdf'
|
|
||||||
? uuid7()
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (fileExtension.endsWith('.md')) {
|
if (fileExtension.endsWith('.md')) {
|
||||||
prosemirrorState = await this.processMarkdown(fileContent);
|
prosemirrorState = await this.processMarkdown(fileContent);
|
||||||
} else if (fileExtension.endsWith('.html')) {
|
} else if (fileExtension.endsWith('.html')) {
|
||||||
prosemirrorState = await this.processHTML(fileContent);
|
prosemirrorState = await this.processHTML(fileContent);
|
||||||
} else if (fileExtension.endsWith('.docx')) {
|
|
||||||
prosemirrorState = await this.processDocx(
|
|
||||||
fileBuffer,
|
|
||||||
workspaceId,
|
|
||||||
spaceId,
|
|
||||||
pageId,
|
|
||||||
userId,
|
|
||||||
);
|
|
||||||
} else if (fileExtension.endsWith('.pdf')) {
|
|
||||||
prosemirrorState = await this.processPdf(
|
|
||||||
fileBuffer,
|
|
||||||
workspaceId,
|
|
||||||
spaceId,
|
|
||||||
pageId,
|
|
||||||
userId,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Surface the real cause instead of a generic mask, so the failure is
|
// Surface the real cause instead of a generic mask, so the failure is
|
||||||
@@ -117,7 +93,6 @@ export class ImportService {
|
|||||||
const pagePosition = await this.getNewPagePosition(spaceId);
|
const pagePosition = await this.getNewPagePosition(spaceId);
|
||||||
|
|
||||||
createdPage = await this.pageRepo.insertPage({
|
createdPage = await this.pageRepo.insertPage({
|
||||||
...(pageId ? { id: pageId } : {}),
|
|
||||||
slugId: generateSlugId(),
|
slugId: generateSlugId(),
|
||||||
title: pageTitle,
|
title: pageTitle,
|
||||||
content: prosemirrorJson,
|
content: prosemirrorJson,
|
||||||
@@ -165,78 +140,6 @@ export class ImportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async processDocx(
|
|
||||||
fileBuffer: Buffer,
|
|
||||||
workspaceId: string,
|
|
||||||
spaceId: string,
|
|
||||||
pageId: string,
|
|
||||||
userId: string,
|
|
||||||
): Promise<any> {
|
|
||||||
let DocxImportModule: any;
|
|
||||||
try {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
||||||
DocxImportModule = require('./../../../ee/document-import/docx-import.service');
|
|
||||||
} catch (err) {
|
|
||||||
this.logger.error(
|
|
||||||
'DOCX import requested but EE module not bundled in this build',
|
|
||||||
);
|
|
||||||
throw new BadRequestException(
|
|
||||||
'This feature requires a valid enterprise license.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const docxImportService = this.moduleRef.get(
|
|
||||||
DocxImportModule.DocxImportService,
|
|
||||||
{ strict: false },
|
|
||||||
);
|
|
||||||
|
|
||||||
const html = await docxImportService.convertDocxToHtml(
|
|
||||||
fileBuffer,
|
|
||||||
workspaceId,
|
|
||||||
spaceId,
|
|
||||||
pageId,
|
|
||||||
userId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.processHTML(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
async processPdf(
|
|
||||||
fileBuffer: Buffer,
|
|
||||||
workspaceId: string,
|
|
||||||
spaceId: string,
|
|
||||||
pageId: string,
|
|
||||||
userId: string,
|
|
||||||
): Promise<any> {
|
|
||||||
let PdfImportModule: any;
|
|
||||||
try {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
||||||
PdfImportModule = require('./../../../ee/document-import/pdf-import.service');
|
|
||||||
} catch (err) {
|
|
||||||
this.logger.error(
|
|
||||||
'PDF import requested but EE module not bundled in this build',
|
|
||||||
);
|
|
||||||
throw new BadRequestException(
|
|
||||||
'This feature requires a valid enterprise license.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pdfImportService = this.moduleRef.get(
|
|
||||||
PdfImportModule.PdfImportService,
|
|
||||||
{ strict: false },
|
|
||||||
);
|
|
||||||
|
|
||||||
const html = await pdfImportService.convertPdfToHtml(
|
|
||||||
fileBuffer,
|
|
||||||
workspaceId,
|
|
||||||
spaceId,
|
|
||||||
pageId,
|
|
||||||
userId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.processHTML(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
async createYdoc(prosemirrorJson: any): Promise<Buffer | null> {
|
async createYdoc(prosemirrorJson: any): Promise<Buffer | null> {
|
||||||
if (prosemirrorJson) {
|
if (prosemirrorJson) {
|
||||||
// this.logger.debug(`Converting prosemirror json state to ydoc`);
|
// this.logger.debug(`Converting prosemirror json state to ydoc`);
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export enum FileTaskType {
|
|||||||
export enum FileImportSource {
|
export enum FileImportSource {
|
||||||
Generic = 'generic',
|
Generic = 'generic',
|
||||||
Notion = 'notion',
|
Notion = 'notion',
|
||||||
Confluence = 'confluence',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FileTaskStatus {
|
export enum FileTaskStatus {
|
||||||
|
|||||||
Reference in New Issue
Block a user