feat(wechat): MindSpace public draft push with 143 host defaults
Add public-page WeChat draft button, publication standard conversion for poem/prose/generic pages, and quick push APIs. Default Studio release SSH targets to john@180.159.29.143 with 105 edge upstream via 10.10.0.2. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 公众号草稿统一 layout:阅读原文 + 底部关注区。
|
||||
* 标准定义见 wechat-draft-publication-standard.mjs 与 docs/wechat-draft-publication-standard.md
|
||||
*/
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fetch as undiciFetch } from 'undici';
|
||||
import { PLATFORM_BRAND_ICON_PATH } from './mindspace-og-tags.mjs';
|
||||
import {
|
||||
resolveMpFollowQrcodePath,
|
||||
uploadWechatArticleContentImage,
|
||||
} from './wechat-news-morning-draft.mjs';
|
||||
|
||||
export function renderReadOriginalLink(publicUrl) {
|
||||
if (!publicUrl) return '';
|
||||
return [
|
||||
'<p style="margin:18px 0 10px;text-align:center;line-height:1.6;">',
|
||||
`<a href="${publicUrl}" style="display:inline-block;padding:5px 14px;border:1px solid #e2e8f0;border-radius:999px;color:#576b95;font-size:12px;text-decoration:none;background:#fafafa;">📖 阅读原文</a>`,
|
||||
'</p>',
|
||||
].join('');
|
||||
}
|
||||
|
||||
/** @deprecated 保留别名,避免外部引用断裂 */
|
||||
export const renderReadOriginalTop = renderReadOriginalLink;
|
||||
|
||||
export function renderFollowTkMindSection({
|
||||
qrcodeImageUrl = '',
|
||||
} = {}) {
|
||||
const qrcodeBlock = qrcodeImageUrl
|
||||
? `<img src="${qrcodeImageUrl}" alt="TKMind 服务号二维码" style="width:168px;height:168px;display:block;margin:14px auto 0;border-radius:10px;border:1px solid #eee;" />`
|
||||
: '';
|
||||
return [
|
||||
'<section style="text-align:center;padding:22px 16px 18px;margin:18px 0 8px;background:linear-gradient(180deg,#fff8f8,#fff);border:1px solid #ffcdd2;border-radius:12px;">',
|
||||
'<p style="margin:0 0 6px;font-size:16px;font-weight:700;color:#b71c1c;line-height:1.5;">欢迎关注 TKMind 智趣</p>',
|
||||
qrcodeBlock,
|
||||
qrcodeImageUrl
|
||||
? '<p style="margin:12px 0 0;font-size:12px;color:#a0aec0;">长按扫描关注</p>'
|
||||
: '<p style="margin:12px 0 0;font-size:12px;color:#a0aec0;">关注 TKMind 服务号</p>',
|
||||
'</section>',
|
||||
].filter(Boolean).join('');
|
||||
}
|
||||
|
||||
export function applyWechatDraftArticleLayout(
|
||||
content,
|
||||
{ publicUrl = '', qrcodeImageUrl = '' } = {},
|
||||
) {
|
||||
const parts = [String(content ?? '').trim()];
|
||||
if (publicUrl) {
|
||||
parts.push(renderReadOriginalLink(publicUrl));
|
||||
}
|
||||
parts.push(renderFollowTkMindSection({ qrcodeImageUrl }));
|
||||
return parts.filter(Boolean).join('\n').slice(0, 20000);
|
||||
}
|
||||
|
||||
export function resolveTkMindBrandIconPath(memindLibRoot = process.cwd()) {
|
||||
const candidates = [
|
||||
path.join(memindLibRoot, 'public', PLATFORM_BRAND_ICON_PATH.replace(/^\//, '')),
|
||||
path.join(memindLibRoot, 'dist', PLATFORM_BRAND_ICON_PATH.replace(/^\//, '')),
|
||||
path.join(process.cwd(), 'public', PLATFORM_BRAND_ICON_PATH.replace(/^\//, '')),
|
||||
];
|
||||
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;
|
||||
}
|
||||
|
||||
export async function uploadWechatDraftBrandAssets({
|
||||
accessToken,
|
||||
wechatFetch = undiciFetch,
|
||||
memindLibRoot = process.cwd(),
|
||||
} = {}) {
|
||||
let qrcodeImageUrl = '';
|
||||
if (!accessToken) {
|
||||
return { qrcodeImageUrl };
|
||||
}
|
||||
|
||||
const qrcodePath = resolveMpFollowQrcodePath(memindLibRoot);
|
||||
if (qrcodePath) {
|
||||
qrcodeImageUrl = await uploadWechatArticleContentImage(
|
||||
accessToken,
|
||||
fs.readFileSync(qrcodePath),
|
||||
{ wechatFetch, filename: 'mp-follow-qrcode.png' },
|
||||
);
|
||||
}
|
||||
|
||||
return { qrcodeImageUrl };
|
||||
}
|
||||
|
||||
export async function finalizeWechatDraftArticleForPush({
|
||||
content,
|
||||
publicUrl = '',
|
||||
accessToken = null,
|
||||
wechatFetch = undiciFetch,
|
||||
memindLibRoot = process.cwd(),
|
||||
} = {}) {
|
||||
const brandAssets = await uploadWechatDraftBrandAssets({
|
||||
accessToken,
|
||||
wechatFetch,
|
||||
memindLibRoot,
|
||||
});
|
||||
return {
|
||||
content: applyWechatDraftArticleLayout(content, {
|
||||
publicUrl,
|
||||
...brandAssets,
|
||||
}),
|
||||
...brandAssets,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user