@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
281 lines (259 loc) โข 7.44 kB
text/typescript
import qs from 'query-string';
import urlJoin from 'url-join';
import { BRANDING_EMAIL, BRANDING_NAME, SOCIAL_URL } from '@/const/branding';
import { DEFAULT_LANG } from '@/const/locale';
import { OFFICIAL_SITE, OFFICIAL_URL } from '@/const/url';
import { Locales } from '@/locales/resources';
import { getCanonicalUrl } from '@/server/utils/url';
import pkg from '../../package.json';
const LAST_MODIFIED = new Date().toISOString();
export const AUTHOR_LIST = {
arvinxx: {
avatar: 'https://avatars.githubusercontent.com/u/28616219?v=4',
desc: 'Founder, Design Engineer',
name: 'Arvin Xu',
url: 'https://github.com/arvinxx',
},
canisminor: {
avatar: 'https://avatars.githubusercontent.com/u/17870709?v=4',
desc: 'Founder, Design Engineer',
name: 'CanisMinor',
url: 'https://github.com/arvinxx',
},
lobehub: {
avatar: 'https://avatars.githubusercontent.com/u/131470832?v=4',
desc: 'Official Account',
name: 'LobeHub',
url: 'https://github.com/lobehub',
},
};
export class Ld {
generate({
image = '/og/cover.png',
article,
url,
title,
description,
date,
locale = DEFAULT_LANG,
webpage = {
enable: true,
},
}: {
article?: {
author: string[];
enable?: boolean;
identifier: string;
tags?: string[];
};
date?: string;
description: string;
image?: string;
locale?: Locales;
title: string;
url: string;
webpage?: {
enable?: boolean;
search?: string;
};
}) {
return {
'@context': 'https://schema.org',
'@graph': [
this.genWebSite(),
article?.enable && this.genArticle({ ...article, date, description, locale, title, url }),
webpage?.enable &&
this.genWebPage({
...webpage,
date,
description,
image,
locale,
title,
url,
}),
image && this.genImageObject({ image, url }),
this.genOrganization(),
].filter(Boolean),
};
}
genOrganization() {
return {
'@id': this.getId(OFFICIAL_URL, '#organization'),
'@type': 'Organization',
'alternateName': 'LobeChat',
'contactPoint': {
'@type': 'ContactPoint',
'contactType': 'customer support',
'email': BRANDING_EMAIL.support,
},
'description':
'We are a group of e/acc design-engineers, hoping to provide modern design components and tools for AIGC, and creating a technology-driven forum, fostering knowledge interaction and the exchange of ideas that may culminate in mutual inspiration and collaborative innovation.',
'email': BRANDING_EMAIL.business,
'founders': [this.getAuthors(['arvinxx']), this.getAuthors(['canisminor'])],
'image': urlJoin(OFFICIAL_SITE, '/icon-512x512.png'),
'logo': {
'@type': 'ImageObject',
'height': 512,
'url': urlJoin(OFFICIAL_SITE, '/icon-512x512.png'),
'width': 512,
},
'name': 'LobeHub',
'sameAs': [SOCIAL_URL.x, SOCIAL_URL.github, SOCIAL_URL.medium, SOCIAL_URL.youtube],
'url': OFFICIAL_SITE,
};
}
getAuthors(ids: string[] = []) {
const defaultAuthor = {
'@id': this.getId(OFFICIAL_URL, '#organization'),
'@type': 'Organization',
};
if (!ids || ids.length === 0) return defaultAuthor;
if (ids.length === 1 && ids[0] === 'lobehub') return defaultAuthor;
const personId = ids.find((id) => id !== 'lobehub');
if (!personId) return defaultAuthor;
const person = (AUTHOR_LIST as any)?.[personId];
if (!person) return defaultAuthor;
return {
'@type': 'Person',
'name': person.name,
'url': person.url,
};
}
genWebPage({
date,
image,
search,
description,
title,
locale = DEFAULT_LANG,
url,
}: {
breadcrumbs?: { title: string; url: string }[];
date?: string;
description: string;
image?: string;
locale?: Locales;
search?: string;
title: string;
url: string;
}) {
const fixedUrl = getCanonicalUrl(url);
const dateCreated = date ? new Date(date).toISOString() : LAST_MODIFIED;
const dateModified = date ? new Date(date).toISOString() : LAST_MODIFIED;
const baseInfo: any = {
'@id': fixedUrl,
'@type': 'WebPage',
'about': {
'@id': this.getId(OFFICIAL_URL, '#organization'),
},
'breadcrumbs': {
'@id': this.getId(fixedUrl, '#breadcrumb'),
},
'dateModified': dateModified,
'datePublished': dateCreated,
'description': description,
'image': {
'@id': this.getId(fixedUrl, '#primaryimage'),
},
'inLanguage': locale,
'isPartOf': {
'@id': this.getId(OFFICIAL_URL, '#website'),
},
'name': this.fixTitle(title),
'primaryImageOfPage': {
'@id': this.getId(fixedUrl, '#primaryimage'),
},
'thumbnailUrl': image,
};
if (search)
baseInfo.potentialAction = {
'@type': 'SearchAction',
'query-input': 'required name=search_term_string',
'target': qs.stringifyUrl({
query: { q: '{search_term_string}' },
url: getCanonicalUrl(search),
}),
};
return baseInfo;
}
genImageObject({ image, url }: { image: string; url: string }) {
const fixedUrl = getCanonicalUrl(url);
return {
'@id': this.getId(fixedUrl, '#primaryimage'),
'@type': 'ImageObject',
'contentUrl': image,
'inLanguage': DEFAULT_LANG,
'url': image,
};
}
genWebSite() {
const baseInfo: any = {
'@id': this.getId(OFFICIAL_URL, '#website'),
'@type': 'WebSite',
'description': pkg.description,
'inLanguage': DEFAULT_LANG,
'name': BRANDING_NAME,
'publisher': {
'@id': this.getId(OFFICIAL_URL, '#organization'),
},
'url': OFFICIAL_URL,
};
return baseInfo;
}
genArticle({
description,
title,
url,
author,
date,
locale = DEFAULT_LANG,
tags,
identifier,
}: {
author: string[];
date?: string;
description: string;
identifier: string;
locale: Locales;
tags?: string[];
title: string;
url: string;
}) {
const fixedUrl = getCanonicalUrl(url);
const dateCreated = date ? new Date(date).toISOString() : LAST_MODIFIED;
const dateModified = date ? new Date(date).toISOString() : LAST_MODIFIED;
const baseInfo: any = {
'@type': 'Article',
'author': this.getAuthors(author),
'creator': author,
'dateCreated': dateCreated,
'dateModified': dateModified,
'datePublished': dateCreated,
'description': description,
'headline': this.fixTitle(title),
'identifier': identifier,
'image': {
'@id': this.getId(fixedUrl, '#primaryimage'),
},
'inLanguage': locale,
'keywords': tags?.join(' ') || 'LobeHub LobeChat',
'mainEntityOfPage': fixedUrl,
'name': title,
'publisher': {
'@id': this.getId(OFFICIAL_URL, '#organization'),
},
'url': fixedUrl,
};
return {
...baseInfo,
};
}
private getId(url: string, id: string) {
return [url, id].join('/');
}
private fixTitle(title: string) {
return title.includes(BRANDING_NAME) ? title : `${title} ยท ${BRANDING_NAME}`;
}
}
export const ldModule = new Ld();