chore: prepare project for server deployment
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
import { Entity, DirectedRelationship, Content, Page, Article, Case, FAQ, Capability, Service, PaginatedResponse } from '@/types';
|
||||
|
||||
const API_BASE_URL = process.env.API_URL || 'http://localhost:3000';
|
||||
|
||||
// 通用请求函数
|
||||
async function fetchApi<T>(endpoint: string, options?: RequestInit): Promise<T> {
|
||||
const url = `${API_BASE_URL}${endpoint}`;
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// 实体相关API
|
||||
export const entityApi = {
|
||||
// 获取实体列表
|
||||
getList: (params?: { page?: number; pageSize?: number; orderBy?: string }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.page) searchParams.set('page', params.page.toString());
|
||||
if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString());
|
||||
if (params?.orderBy) searchParams.set('orderBy', params.orderBy);
|
||||
return fetchApi<PaginatedResponse<Entity>>(`/api/entities?${searchParams.toString()}`);
|
||||
},
|
||||
|
||||
// 获取实体详情
|
||||
getById: (id: string) => fetchApi<Entity>(`/api/entities/${id}`),
|
||||
|
||||
// 公开接口:获取实体列表
|
||||
getPublicList: (params?: { page?: number; pageSize?: number }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.page) searchParams.set('page', params.page.toString());
|
||||
if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString());
|
||||
return fetchApi<Entity[]>(`/api/public/entities?${searchParams.toString()}`);
|
||||
},
|
||||
|
||||
// 公开接口:获取实体详情
|
||||
getPublicById: (id: string) => fetchApi<Entity>(`/api/public/entities/${id}`),
|
||||
|
||||
// 公开接口:获取实体的关系
|
||||
getPublicRelationships: (id: string) => fetchApi<{ source: DirectedRelationship[]; target: DirectedRelationship[] }>(`/api/public/entities/${id}/relationships`),
|
||||
|
||||
// 公开接口:获取实体的内容
|
||||
getPublicContents: (id: string) => fetchApi<Content[]>(`/api/public/entities/${id}/contents`),
|
||||
};
|
||||
|
||||
// 关系相关API
|
||||
export const relationshipApi = {
|
||||
// 获取关系列表
|
||||
getList: (params?: { page?: number; pageSize?: number }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.page) searchParams.set('page', params.page.toString());
|
||||
if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString());
|
||||
return fetchApi<PaginatedResponse<DirectedRelationship>>(`/api/relationships?${searchParams.toString()}`);
|
||||
},
|
||||
|
||||
// 获取关系详情
|
||||
getById: (id: string) => fetchApi<DirectedRelationship>(`/api/relationships/${id}`),
|
||||
|
||||
// 获取实体的出向关系
|
||||
getBySourceEntity: (entityId: string) => fetchApi<DirectedRelationship[]>(`/api/relationships/source/${entityId}`),
|
||||
|
||||
// 获取实体的入向关系
|
||||
getByTargetEntity: (entityId: string) => fetchApi<DirectedRelationship[]>(`/api/relationships/target/${entityId}`),
|
||||
};
|
||||
|
||||
// 内容相关API
|
||||
export const contentApi = {
|
||||
// 获取内容列表
|
||||
getList: (type: string, params?: { page?: number; pageSize?: number }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.page) searchParams.set('page', params.page.toString());
|
||||
if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString());
|
||||
return fetchApi<PaginatedResponse<Content>>(`/api/contents/${type}?${searchParams.toString()}`);
|
||||
},
|
||||
|
||||
// 获取内容详情
|
||||
getById: (type: string, id: string) => fetchApi<Content>(`/api/contents/${type}/${id}`),
|
||||
|
||||
// 公开接口:获取内容列表
|
||||
getPublicList: (type?: string, params?: { page?: number; pageSize?: number }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (type) searchParams.set('type', type);
|
||||
if (params?.page) searchParams.set('page', params.page.toString());
|
||||
if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString());
|
||||
return fetchApi<Content[]>(`/api/public/contents?${searchParams.toString()}`);
|
||||
},
|
||||
|
||||
// 公开接口:获取内容详情
|
||||
getPublicById: (id: string) => fetchApi<Content>(`/api/public/contents/${id}`),
|
||||
|
||||
// 公开接口:获取内容的实体关联
|
||||
getPublicEntities: (id: string) => fetchApi<Entity[]>(`/api/public/contents/${id}/entities`),
|
||||
};
|
||||
|
||||
// 页面相关API
|
||||
export const pageApi = {
|
||||
// 获取页面列表
|
||||
getList: (params?: { page?: number; pageSize?: number }) => contentApi.getList('page', params),
|
||||
|
||||
// 获取页面详情
|
||||
getById: (id: string) => contentApi.getById('page', id),
|
||||
|
||||
// 公开接口:获取页面列表
|
||||
getPublicList: (params?: { page?: number; pageSize?: number }) => contentApi.getPublicList('page', params),
|
||||
|
||||
// 公开接口:获取页面详情
|
||||
getPublicById: (id: string) => contentApi.getPublicById(id),
|
||||
};
|
||||
|
||||
// 文章相关API
|
||||
export const articleApi = {
|
||||
// 获取文章列表
|
||||
getList: (params?: { page?: number; pageSize?: number }) => contentApi.getList('article', params),
|
||||
|
||||
// 获取文章详情
|
||||
getById: (id: string) => contentApi.getById('article', id),
|
||||
|
||||
// 公开接口:获取文章列表
|
||||
getPublicList: (params?: { page?: number; pageSize?: number }) => contentApi.getPublicList('article', params),
|
||||
|
||||
// 公开接口:获取文章详情
|
||||
getPublicById: (id: string) => contentApi.getPublicById(id),
|
||||
};
|
||||
|
||||
// 案例相关API
|
||||
export const caseApi = {
|
||||
// 获取案例列表
|
||||
getList: (params?: { page?: number; pageSize?: number }) => contentApi.getList('case', params),
|
||||
|
||||
// 获取案例详情
|
||||
getById: (id: string) => contentApi.getById('case', id),
|
||||
|
||||
// 公开接口:获取案例列表
|
||||
getPublicList: (params?: { page?: number; pageSize?: number }) => contentApi.getPublicList('case', params),
|
||||
|
||||
// 公开接口:获取案例详情
|
||||
getPublicById: (id: string) => contentApi.getPublicById(id),
|
||||
};
|
||||
|
||||
// FAQ相关API
|
||||
export const faqApi = {
|
||||
// 获取FAQ列表
|
||||
getList: (params?: { page?: number; pageSize?: number }) => contentApi.getList('faq', params),
|
||||
|
||||
// 获取FAQ详情
|
||||
getById: (id: string) => contentApi.getById('faq', id),
|
||||
|
||||
// 公开接口:获取FAQ列表
|
||||
getPublicList: (params?: { page?: number; pageSize?: number }) => contentApi.getPublicList('faq', params),
|
||||
|
||||
// 公开接口:获取FAQ详情
|
||||
getPublicById: (id: string) => contentApi.getPublicById(id),
|
||||
};
|
||||
|
||||
// 能力相关API
|
||||
export const capabilityApi = {
|
||||
// 获取能力列表
|
||||
getList: (params?: { page?: number; pageSize?: number }) => contentApi.getList('capability', params),
|
||||
|
||||
// 获取能力详情
|
||||
getById: (id: string) => contentApi.getById('capability', id),
|
||||
|
||||
// 公开接口:获取能力列表
|
||||
getPublicList: (params?: { page?: number; pageSize?: number }) => contentApi.getPublicList('capability', params),
|
||||
|
||||
// 公开接口:获取能力详情
|
||||
getPublicById: (id: string) => contentApi.getPublicById(id),
|
||||
};
|
||||
|
||||
// 服务相关API
|
||||
export const serviceApi = {
|
||||
// 获取服务列表
|
||||
getList: (params?: { page?: number; pageSize?: number }) => contentApi.getList('service', params),
|
||||
|
||||
// 获取服务详情
|
||||
getById: (id: string) => contentApi.getById('service', id),
|
||||
|
||||
// 公开接口:获取服务列表
|
||||
getPublicList: (params?: { page?: number; pageSize?: number }) => contentApi.getPublicList('service', params),
|
||||
|
||||
// 公开接口:获取服务详情
|
||||
getPublicById: (id: string) => contentApi.getPublicById(id),
|
||||
};
|
||||
|
||||
// 关联相关API
|
||||
export const joinApi = {
|
||||
// 获取关联列表
|
||||
getList: (params?: { page?: number; pageSize?: number }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.page) searchParams.set('page', params.page.toString());
|
||||
if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString());
|
||||
return fetchApi<PaginatedResponse<{ join_id: string; entity_id: string; content_id: string; join_note?: string; created_at: string }>>(`/api/joins?${searchParams.toString()}`);
|
||||
},
|
||||
|
||||
// 获取实体的所有关联
|
||||
getByEntityId: (entityId: string) => fetchApi<{ join_id: string; entity_id: string; content_id: string; join_note?: string; created_at: string }[]>(`/api/joins/entity/${entityId}`),
|
||||
|
||||
// 获取内容的所有关联
|
||||
getByContentId: (contentId: string) => fetchApi<{ join_id: string; entity_id: string; content_id: string; join_note?: string; created_at: string }[]>(`/api/joins/content/${contentId}`),
|
||||
};
|
||||
|
||||
// 搜索相关API
|
||||
export const searchApi = {
|
||||
// 搜索实体
|
||||
entities: (keyword: string, params?: { page?: number; pageSize?: number }) => {
|
||||
const searchParams = new URLSearchParams({ keyword });
|
||||
if (params?.page) searchParams.set('page', params.page.toString());
|
||||
if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString());
|
||||
return fetchApi<Entity[]>(`/api/public/search/entities?${searchParams.toString()}`);
|
||||
},
|
||||
|
||||
// 搜索内容
|
||||
contents: (keyword: string, type?: string, params?: { page?: number; pageSize?: number }) => {
|
||||
const searchParams = new URLSearchParams({ keyword });
|
||||
if (type) searchParams.set('type', type);
|
||||
if (params?.page) searchParams.set('page', params.page.toString());
|
||||
if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString());
|
||||
return fetchApi<Content[]>(`/api/public/search/contents?${searchParams.toString()}`);
|
||||
},
|
||||
};
|
||||
|
||||
// JSON-LD相关API
|
||||
export const jsonLdApi = {
|
||||
// 获取实体的JSON-LD
|
||||
getEntityJsonLd: (id: string) => fetchApi<Record<string, any>>(`/api/jsonld/entities/${id}`),
|
||||
|
||||
// 获取内容的JSON-LD
|
||||
getContentJsonLd: (id: string) => fetchApi<Record<string, any>>(`/api/jsonld/contents/${id}`),
|
||||
|
||||
// 获取所有实体的JSON-LD列表
|
||||
getAllEntitiesJsonLd: () => fetchApi<Record<string, any>[]>('/api/jsonld/entities'),
|
||||
|
||||
// 获取所有内容的JSON-LD列表
|
||||
getAllContentsJsonLd: (type?: string) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (type) searchParams.set('type', type);
|
||||
return fetchApi<Record<string, any>[]>(`/api/jsonld/contents?${searchParams.toString()}`);
|
||||
},
|
||||
|
||||
// 获取站点地图的JSON-LD
|
||||
getSitemapJsonLd: () => fetchApi<Record<string, any>>('/api/jsonld/sitemap'),
|
||||
};
|
||||
@@ -0,0 +1,160 @@
|
||||
import { SEOData, Breadcrumb, JsonLdData } from '@/types';
|
||||
|
||||
const SITE_NAME = '阿美巴AI官网';
|
||||
const SITE_DESCRIPTION = '阿美巴AI - AI时代企业进化平台';
|
||||
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://ameiba.com';
|
||||
|
||||
// 生成SEO数据
|
||||
export function generateSEO(data: SEOData): Record<string, string> {
|
||||
const title = data.title ? `${data.title} - ${SITE_NAME}` : SITE_NAME;
|
||||
const description = data.description || SITE_DESCRIPTION;
|
||||
const canonical = data.canonical ? `${SITE_URL}${data.canonical}` : SITE_URL;
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
canonical,
|
||||
'og:title': data.ogTitle || title,
|
||||
'og:description': data.ogDescription || description,
|
||||
'og:image': data.ogImage || `${SITE_URL}/og-default.jpg`,
|
||||
'og:type': 'website',
|
||||
'og:url': canonical,
|
||||
'twitter:card': 'summary_large_image',
|
||||
'twitter:title': data.ogTitle || title,
|
||||
'twitter:description': data.ogDescription || description,
|
||||
'twitter:image': data.ogImage || `${SITE_URL}/og-default.jpg`,
|
||||
};
|
||||
}
|
||||
|
||||
// 生成面包屑Schema
|
||||
export function generateBreadcrumbSchema(breadcrumbs: Breadcrumb[]): JsonLdData {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: breadcrumbs.map((item, index) => ({
|
||||
'@type': 'ListItem',
|
||||
position: index + 1,
|
||||
name: item.name,
|
||||
item: `${SITE_URL}${item.url}`,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// 生成组织Schema
|
||||
export function generateOrganizationSchema(): JsonLdData {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Organization',
|
||||
name: '阿美巴AI',
|
||||
description: 'AI时代企业进化平台',
|
||||
url: SITE_URL,
|
||||
logo: `${SITE_URL}/logo.png`,
|
||||
sameAs: [],
|
||||
};
|
||||
}
|
||||
|
||||
// 生成文章Schema
|
||||
export function generateArticleSchema(data: {
|
||||
title: string;
|
||||
description: string;
|
||||
url: string;
|
||||
image?: string;
|
||||
author?: string;
|
||||
datePublished?: string;
|
||||
dateModified?: string;
|
||||
}): JsonLdData {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Article',
|
||||
headline: data.title,
|
||||
description: data.description,
|
||||
image: data.image || `${SITE_URL}/og-default.jpg`,
|
||||
url: `${SITE_URL}${data.url}`,
|
||||
author: data.author ? {
|
||||
'@type': 'Person',
|
||||
name: data.author,
|
||||
} : undefined,
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: SITE_NAME,
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: `${SITE_URL}/logo.png`,
|
||||
},
|
||||
},
|
||||
datePublished: data.datePublished,
|
||||
dateModified: data.dateModified,
|
||||
};
|
||||
}
|
||||
|
||||
// 生成FAQ Schema
|
||||
export function generateFAQSchema(faqs: Array<{ question: string; answer: string }>): JsonLdData {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: faqs.map(faq => ({
|
||||
'@type': 'Question',
|
||||
name: faq.question,
|
||||
acceptedAnswer: {
|
||||
'@type': 'Answer',
|
||||
text: faq.answer,
|
||||
},
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// 生成服务Schema
|
||||
export function generateServiceSchema(data: {
|
||||
name: string;
|
||||
description: string;
|
||||
url: string;
|
||||
provider?: string;
|
||||
}): JsonLdData {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Service',
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
url: `${SITE_URL}${data.url}`,
|
||||
provider: data.provider ? {
|
||||
'@type': 'Organization',
|
||||
name: data.provider,
|
||||
} : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// 生成案例Schema
|
||||
export function generateCaseSchema(data: {
|
||||
title: string;
|
||||
description: string;
|
||||
url: string;
|
||||
image?: string;
|
||||
}): JsonLdData {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Article',
|
||||
headline: data.title,
|
||||
description: data.description,
|
||||
image: data.image || `${SITE_URL}/og-default.jpg`,
|
||||
url: `${SITE_URL}${data.url}`,
|
||||
articleSection: '案例',
|
||||
};
|
||||
}
|
||||
|
||||
// 生成网站搜索Schema
|
||||
export function generateWebSiteSearchAction(): JsonLdData {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebSite',
|
||||
name: SITE_NAME,
|
||||
url: SITE_URL,
|
||||
potentialAction: {
|
||||
'@type': 'SearchAction',
|
||||
target: {
|
||||
'@type': 'EntryPoint',
|
||||
urlTemplate: `${SITE_URL}/search?q={search_term_string}`,
|
||||
},
|
||||
'query-input': 'required name=search_term_string',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
const API_URL = process.env.API_URL || 'http://localhost:24731';
|
||||
|
||||
export type SiteSettings = Record<string, string>;
|
||||
|
||||
export async function getSiteSettings(): Promise<SiteSettings> {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/api/public/settings`, { cache: 'no-store' });
|
||||
if (!response.ok) return {};
|
||||
return response.json();
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user