feat: 同步管理端、用户端与后端业务,并接通线上 Web 接口

补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。

Co-authored-by: Cursor <cursoragent@cursor.com>
这个提交包含在:
wikiboss
2026-09-23 18:01:47 +08:00
父节点 8ea9c25386
当前提交 62e0ae4435
共有 227 个文件被更改,包括 21855 次插入 和 5264 次删除

查看文件

@@ -166,16 +166,18 @@ describe('Point 29-A: SEO & OpenGraph 社交预览元标签注入引擎', () =>
expect(metaNull['og:description']).toBe(
'OmniCard AI - Global Digital Business Card & Micro Portal'
);
expect(metaNull['og:image']).toBe('https://omnicard.ai/assets/og-default.png');
// 无真实头像/封面时不得填假 CDN / 占位图
expect(metaNull['og:image']).toBe('');
expect(metaNull['twitter:image']).toBe('');
expect(metaNull['og:type']).toBe('profile');
expect(metaNull['twitter:card']).toBe('summary_large_image');
const metaUndefined = seoService.generateOpenGraphMeta(undefined, {
siteName: 'Custom Brand',
defaultImage: 'https://example.com/fallback.jpg',
});
expect(metaUndefined['og:title']).toBe('Custom Brand');
expect(metaUndefined['og:image']).toBe('https://example.com/fallback.jpg');
expect(metaUndefined['og:image']).toBe('');
expect(metaUndefined['twitter:image']).toBe('');
});
it('当 template 不含标准 <head> 标签时,renderHtmlWithOgMeta 能够安全兜底注入', () => {
@@ -185,4 +187,112 @@ describe('Point 29-A: SEO & OpenGraph 社交预览元标签注入引擎', () =>
expect(result).toContain('<div>Raw Body Content</div>');
});
});
describe('6. 无真实图片时 og:image / twitter:image 必须为空(禁止假图)', () => {
it('generateOgMeta:无 avatarUrl 时 image 为空,不得回退到 default-card-og / og-default', () => {
const og = seoService.generateOgMeta(
{
id: 1,
name: 'No Avatar',
title: 'Engineer',
company: 'Omni',
status: 'active' as any,
bentoBlocks: [],
},
'https://omnicard.ai'
);
expect(og.image).toBe('');
expect(og.image).not.toMatch(/og-default|default-card-og|placeholder|example\.com|unsplash|picsum/i);
});
it('generateOgMeta:仅接受真实 http(s) 头像,相对路径与非 http 协议视为无图', () => {
const relative = seoService.generateOgMeta({
id: 2,
name: 'Rel',
title: 'T',
company: 'C',
avatarUrl: '/static/avatar.png',
status: 'active' as any,
bentoBlocks: [],
});
expect(relative.image).toBe('');
const blank = seoService.generateOgMeta({
id: 2,
name: 'Blank',
title: 'T',
company: 'C',
avatarUrl: ' ',
status: 'active' as any,
bentoBlocks: [],
});
expect(blank.image).toBe('');
const dataUri = seoService.generateOgMeta({
id: 2,
name: 'Data',
title: 'T',
company: 'C',
avatarUrl: 'data:image/png;base64,AAAA',
status: 'active' as any,
bentoBlocks: [],
});
expect(dataUri.image).toBe('');
const real = seoService.generateOgMeta({
id: 3,
name: 'Https',
title: 'T',
company: 'C',
avatarUrl: 'https://cdn.omnicard.ai/avatars/real.jpg',
status: 'active' as any,
bentoBlocks: [],
});
expect(real.image).toBe('https://cdn.omnicard.ai/avatars/real.jpg');
});
it('generateOpenGraphMeta:无头像时 og:image / twitter:image 为空,忽略假 defaultImage 占位', () => {
const meta = seoService.generateOpenGraphMeta(
{ name: 'Empty Pic', title: 'CTO', company: 'X' },
{
baseUrl: 'https://omnicard.ai',
defaultImage: 'https://omnicard.ai/assets/og-default.png',
}
);
expect(meta['og:image']).toBe('');
expect(meta['twitter:image']).toBe('');
expect(JSON.stringify(meta)).not.toMatch(
/og-default|default-card-og|placeholder|picsum|unsplash/i
);
});
it('renderHtmlWithOgMeta:无真实图片时不输出 og:image / twitter:image 标签', () => {
const template = `<!DOCTYPE html><html><head><title>t</title></head><body></body></html>`;
const html = seoService.renderHtmlWithOgMeta(
template,
{ name: 'No Image Card' },
{ baseUrl: 'https://omnicard.ai' }
);
expect(html).not.toContain('property="og:image"');
expect(html).not.toContain('name="twitter:image"');
expect(html).not.toMatch(/og-default|default-card-og/i);
});
it('renderCardHtml:无头像时 og:image / twitter:image content 为空', () => {
const html = seoService.renderCardHtml(
{
id: 9,
name: 'Blank',
title: 'Dev',
company: 'ACME',
status: 'active' as any,
bentoBlocks: [],
},
{ baseUrl: 'https://omnicard.ai' }
);
expect(html).toContain('<meta property="og:image" content="" />');
expect(html).toContain('<meta name="twitter:image" content="" />');
expect(html).not.toMatch(/og-default|default-card-og|placeholder\.com|example\.com\/.*\.(png|jpe?g)/i);
});
});
});