import path from 'node:path'; import { normalizeWorkspaceRelativePath } from './mindspace-workspace-relative-path.mjs'; import { validatePageForWechatDraft } from './mindspace-wechat-page-draft.mjs'; function normalizePublicHtmlRelativePath(relativePath) { const normalized = normalizeWorkspaceRelativePath(relativePath); if (!normalized || !normalized.startsWith('public/') || !normalized.toLowerCase().endsWith('.html')) { throw Object.assign(new Error('仅支持 workspace 公开 HTML 页面'), { code: 'invalid_public_html_path' }); } return normalized; } function titleFromPublicHtml(html, relativePath) { const match = String(html ?? '').match(/]*>([\s\S]*?)<\/title>/i); const fromTitle = match?.[1]?.replace(/\s+/g, ' ').trim(); if (fromTitle) return fromTitle; const basename = path.basename(String(relativePath ?? ''), '.html'); return basename || 'MindSpace 页面'; } async function resolvePublicHtmlContext({ user, relativePath, mindSpacePages, mindSpacePublications, readWorkspaceHtml, }) { if (!mindSpacePages) { throw Object.assign(new Error('MindSpace 页面服务未启用'), { code: 'mindspace_unavailable' }); } if (typeof readWorkspaceHtml !== 'function') { throw Object.assign(new Error('MindSpace 页面读取服务不可用'), { code: 'mindspace_read_unavailable' }); } const normalized = normalizePublicHtmlRelativePath(relativePath); const { html: content } = await readWorkspaceHtml({ userId: user.id, relativePath: normalized, }); const page = await mindSpacePages.findPageByRelativePath(user.id, normalized); if (!page?.id) { throw Object.assign(new Error('无法识别页面对应的 MindSpace 记录'), { code: 'page_not_found' }); } let publicUrl = ''; if (mindSpacePublications?.getCurrent) { const publication = await mindSpacePublications.getCurrent(user.id, page.id); publicUrl = publication?.publicUrl ?? ''; } return { normalized, content, page, pageTitle: page.title || titleFromPublicHtml(content, normalized), pageSummary: page.summary ?? '', publicUrl, }; } export async function getQuickWechatDraftFromPublicHtmlStatus({ user, relativePath, mindSpacePages, mindSpacePublications, readWorkspaceHtml, getWechatMpConfig, getWechatPageDraft, }) { const configService = getWechatMpConfig?.(); const config = configService ? await configService.getConfig(user.id) : { configured: false }; let context = null; let validation = null; try { context = await resolvePublicHtmlContext({ user, relativePath, mindSpacePages, mindSpacePublications, readWorkspaceHtml, }); validation = validatePageForWechatDraft(context.content, { pageTitle: context.pageTitle, pageSummary: context.pageSummary, publicUrl: context.publicUrl, }); } catch (error) { if (error?.code === 'page_not_found' || error?.code === 'invalid_public_html_path') { return { configured: Boolean(config.configured), pageId: null, canPush: false, validation: { ok: false, profile: 'unknown', blockers: [error.message], warnings: [], estimatedContentLength: 0, }, }; } throw error; } let latestRun = null; const draftService = getWechatPageDraft?.(); if (draftService?.listRuns) { const runs = await draftService.listRuns(user.id, { pageId: context.page.id, limit: 1 }); latestRun = runs[0] ?? null; } return { configured: Boolean(config.configured), pageId: context.page.id, pageTitle: context.pageTitle, canPush: Boolean(config.configured) && validation.ok, validation, latestRun, }; } export async function quickWechatDraftFromPublicHtml({ user, relativePath, mindSpacePages, mindSpacePublications, readWorkspaceHtml, getWechatPageDraft, }) { const draftService = getWechatPageDraft?.(); if (!draftService?.pushPageDraft) { throw Object.assign(new Error('公众号草稿推送未启用'), { code: 'wechat_draft_unavailable' }); } const context = await resolvePublicHtmlContext({ user, relativePath, mindSpacePages, mindSpacePublications, readWorkspaceHtml, }); const validation = validatePageForWechatDraft(context.content, { pageTitle: context.pageTitle, pageSummary: context.pageSummary, publicUrl: context.publicUrl, }); if (!validation.ok) { throw Object.assign(new Error(validation.blockers[0] ?? '页面不符合公众号草稿推送要求'), { code: 'wechat_draft_validation_failed', validation, }); } return draftService.pushPageDraft(user.id, context.page.id, { triggeredBy: user.id, skipValidation: true, }); }