feat: 建立 DOCX 共享协议与准备服务
This commit is contained in:
@@ -2,6 +2,12 @@ import {
|
||||
MarkdownDocumentParseError,
|
||||
renderMarkdown
|
||||
} from "@md-to-pdf/renderer";
|
||||
import {
|
||||
docxExportRequestSchema,
|
||||
type DocxExportRequest,
|
||||
type RenderedMarkdownDocument,
|
||||
type ThemeManifest
|
||||
} from "@md-to-pdf/core";
|
||||
import {
|
||||
createThemeRegistry,
|
||||
type ThemeRegistryOptions
|
||||
@@ -35,6 +41,16 @@ export interface ApplicationServiceOptions
|
||||
extends ThemeRegistryOptions,
|
||||
ImageResourceResolverOptions {}
|
||||
|
||||
export interface PreparedDocxExport {
|
||||
request: DocxExportRequest;
|
||||
document: RenderedMarkdownDocument;
|
||||
theme: {
|
||||
manifest: ThemeManifest;
|
||||
source: "bundled" | "local";
|
||||
css: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function createApplicationService(
|
||||
options: ApplicationServiceOptions
|
||||
) {
|
||||
@@ -113,11 +129,92 @@ export function createApplicationService(
|
||||
};
|
||||
}
|
||||
|
||||
async function prepareDocxExport(
|
||||
request: unknown,
|
||||
context: ImageResolutionContext = {}
|
||||
): Promise<PreparedDocxExport> {
|
||||
const parsed = docxExportRequestSchema.safeParse(request);
|
||||
if (!parsed.success) {
|
||||
const firstField = parsed.error.issues[0]?.path[0];
|
||||
if (firstField === "exportConfig") {
|
||||
throw new ApplicationRequestError(
|
||||
400,
|
||||
"INVALID_EXPORT_CONFIG",
|
||||
"导出配置无效"
|
||||
);
|
||||
}
|
||||
if (firstField === "fileName") {
|
||||
throw new ApplicationRequestError(
|
||||
400,
|
||||
"INVALID_FILE_NAME",
|
||||
"fileName 必须是长度不超过 500 的字符串"
|
||||
);
|
||||
}
|
||||
if (firstField === "markdown") {
|
||||
const markdownTooLarge = parsed.error.issues.some(
|
||||
(issue) =>
|
||||
issue.path[0] === "markdown" && issue.code === "too_big"
|
||||
);
|
||||
throw new ApplicationRequestError(
|
||||
markdownTooLarge ? 413 : 400,
|
||||
markdownTooLarge ? "MARKDOWN_TOO_LARGE" : "INVALID_MARKDOWN",
|
||||
markdownTooLarge
|
||||
? "Markdown 内容不能超过 1.5 MB"
|
||||
: "markdown 必须是字符串"
|
||||
);
|
||||
}
|
||||
if (firstField === "resources") {
|
||||
throw new ApplicationRequestError(
|
||||
400,
|
||||
"INVALID_IMAGE_RESOURCES",
|
||||
"图片资源参数无效"
|
||||
);
|
||||
}
|
||||
throw new ApplicationRequestError(
|
||||
400,
|
||||
"INVALID_DOCX_REQUEST",
|
||||
"DOCX 导出请求无效"
|
||||
);
|
||||
}
|
||||
|
||||
const theme = await themes.get(parsed.data.exportConfig.themeId);
|
||||
if (!theme) {
|
||||
throw new ApplicationRequestError(
|
||||
404,
|
||||
"THEME_NOT_FOUND",
|
||||
"未找到指定主题"
|
||||
);
|
||||
}
|
||||
|
||||
const [document, themeCss] = await Promise.all([
|
||||
render(parsed.data, context),
|
||||
themes.getCss(theme.manifest.id)
|
||||
]);
|
||||
if (themeCss === undefined) {
|
||||
throw new ApplicationRequestError(
|
||||
404,
|
||||
"THEME_NOT_FOUND",
|
||||
"未找到指定主题"
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
request: parsed.data,
|
||||
document,
|
||||
theme: {
|
||||
manifest: theme.manifest,
|
||||
source: theme.source,
|
||||
css: themeCss
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
getThemeAsset: themes.getAsset,
|
||||
getThemeCss: themes.getCss,
|
||||
invalidateThemes: themes.invalidate,
|
||||
listThemes,
|
||||
prepareDocxExport,
|
||||
render
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user