feat: add SharePanel component for social sharing with QR code support
- Implemented SharePanel component in `SharePanel.astro` for sharing content on social media platforms. - Integrated QR code generation for WeChat sharing using the `qrcode` library. - Added localization support for English and Chinese languages. - Created utility functions in `seo.ts` for building article summaries and FAQs. - Introduced API routes for serving IndexNow key and generating full LLM catalog and summaries. - Enhanced SEO capabilities with structured data for articles and pages.
This commit is contained in:
89
frontend/src/components/seo/PageViewTracker.astro
Normal file
89
frontend/src/components/seo/PageViewTracker.astro
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
interface Props {
|
||||
pageType: string;
|
||||
entityId?: string;
|
||||
postSlug?: string;
|
||||
}
|
||||
|
||||
const props = Astro.props;
|
||||
const pageType = props.pageType;
|
||||
const entityId = props.entityId ?? '';
|
||||
const postSlug = props.postSlug ?? '';
|
||||
---
|
||||
|
||||
<script is:inline define:vars={{ pageType, entityId, postSlug }}>
|
||||
(() => {
|
||||
const endpoint = '/api/analytics/content';
|
||||
const storageKey = `termi:pageview:${pageType}:${entityId || postSlug || 'root'}`;
|
||||
|
||||
function ensureSessionId() {
|
||||
try {
|
||||
const existing = window.sessionStorage.getItem(storageKey);
|
||||
if (existing) return existing;
|
||||
const nextId = crypto.randomUUID();
|
||||
window.sessionStorage.setItem(storageKey, nextId);
|
||||
return nextId;
|
||||
} catch {
|
||||
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
}
|
||||
}
|
||||
|
||||
function getReferrerHost() {
|
||||
try {
|
||||
return document.referrer ? new URL(document.referrer).host : '';
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeSource(value) {
|
||||
const source = String(value || '').trim().toLowerCase();
|
||||
if (!source) return 'direct';
|
||||
if (source.includes('chatgpt') || source.includes('openai')) return 'chatgpt-search';
|
||||
if (source.includes('perplexity')) return 'perplexity';
|
||||
if (source.includes('copilot') || source.includes('bing')) return 'copilot-bing';
|
||||
if (source.includes('gemini')) return 'gemini';
|
||||
if (source.includes('google')) return 'google';
|
||||
if (source.includes('claude')) return 'claude';
|
||||
if (source.includes('duckduckgo')) return 'duckduckgo';
|
||||
if (source.includes('kagi')) return 'kagi';
|
||||
return source;
|
||||
}
|
||||
|
||||
function buildMetadata() {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const utmSource = currentUrl.searchParams.get('utm_source')?.trim() || '';
|
||||
const utmMedium = currentUrl.searchParams.get('utm_medium')?.trim() || '';
|
||||
const utmCampaign = currentUrl.searchParams.get('utm_campaign')?.trim() || '';
|
||||
const utmTerm = currentUrl.searchParams.get('utm_term')?.trim() || '';
|
||||
const utmContent = currentUrl.searchParams.get('utm_content')?.trim() || '';
|
||||
const referrerHost = getReferrerHost();
|
||||
|
||||
return {
|
||||
pageType,
|
||||
entityId: entityId || undefined,
|
||||
referrerHost: referrerHost || undefined,
|
||||
utmSource: utmSource || undefined,
|
||||
utmMedium: utmMedium || undefined,
|
||||
utmCampaign: utmCampaign || undefined,
|
||||
utmTerm: utmTerm || undefined,
|
||||
utmContent: utmContent || undefined,
|
||||
landingSource: normalizeSource(utmSource || referrerHost),
|
||||
};
|
||||
}
|
||||
|
||||
fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
keepalive: true,
|
||||
body: JSON.stringify({
|
||||
event_type: 'page_view',
|
||||
path: `${window.location.pathname}${window.location.search}`,
|
||||
post_slug: postSlug || undefined,
|
||||
session_id: ensureSessionId(),
|
||||
referrer: document.referrer || undefined,
|
||||
metadata: buildMetadata(),
|
||||
}),
|
||||
}).catch(() => undefined);
|
||||
})();
|
||||
</script>
|
||||
Reference in New Issue
Block a user