feat: 实现多站点数据隔离和管理功能

- 新增站点管理功能,替换原有的域名管理
- 添加数据库迁移工具,为现有表增加 site_id 字段
- 创建 useSite 组合式 API 统一管理站点状态
- 修改前后端 API 以支持站点筛选参数
- 更新管理界面,将域名筛选改为站点筛选
- 优化数据导出功能,支持按站点筛选导出
- 更新文档,调整站点 ID 默认值为空字符串
- 升级 wrangler 依赖至最新版本
This commit is contained in:
anghunk
2026-02-09 15:31:47 +08:00
parent 3b429bf82c
commit 098d668a15
22 changed files with 431 additions and 412 deletions

View File

@@ -23,6 +23,7 @@ export type CommentItem = {
likes?: number;
ua?: string | null;
isAdmin?: boolean;
siteId?: string;
};
export type CommentListResponse = {
@@ -115,8 +116,8 @@ export type VisitPagesResponse = {
itemsByLatest?: VisitPageItem[];
};
export type DomainListResponse = {
domains: string[];
export type SiteListResponse = {
sites: string[];
};
export type LikeStatsItem = {
@@ -153,11 +154,11 @@ export function logoutAdmin(): void {
localStorage.removeItem('cwd_admin_token');
}
export function fetchComments(page: number, domain?: string): Promise<CommentListResponse> {
export function fetchComments(page: number, siteId?: string): Promise<CommentListResponse> {
const searchParams = new URLSearchParams();
searchParams.set('page', String(page));
if (domain) {
searchParams.set('domain', domain);
if (siteId && siteId !== 'default') {
searchParams.set('siteId', siteId);
}
return get<CommentListResponse>(`/admin/comments/list?${searchParams.toString()}`);
}
@@ -262,8 +263,13 @@ export function blockEmail(email: string): Promise<{ message: string }> {
return post<{ message: string }>('/admin/comments/block-email', { email });
}
export function exportComments(): Promise<any[]> {
return get<any[]>('/admin/comments/export');
export function exportComments(siteId?: string): Promise<any[]> {
const searchParams = new URLSearchParams();
if (siteId && siteId !== 'default') {
searchParams.set('siteId', siteId);
}
const query = searchParams.toString();
return get<any[]>(query ? `/admin/comments/export?${query}` : '/admin/comments/export');
}
export function importComments(data: any[]): Promise<{ message: string }> {
@@ -278,8 +284,13 @@ export function importConfig(data: any[]): Promise<{ message: string }> {
return post<{ message: string }>('/admin/import/config', data);
}
export function exportStats(): Promise<any> {
return get<any>('/admin/export/stats');
export function exportStats(siteId?: string): Promise<any> {
const searchParams = new URLSearchParams();
if (siteId && siteId !== 'default') {
searchParams.set('siteId', siteId);
}
const query = searchParams.toString();
return get<any>(query ? `/admin/export/stats?${query}` : '/admin/export/stats');
}
export function importStats(data: any): Promise<{ message: string }> {
@@ -294,30 +305,30 @@ export function importBackup(data: any): Promise<{ message: string }> {
return post<{ message: string }>('/admin/import/backup', data);
}
export function fetchCommentStats(domain?: string): Promise<CommentStatsResponse> {
export function fetchCommentStats(siteId?: string): Promise<CommentStatsResponse> {
const searchParams = new URLSearchParams();
if (domain) {
searchParams.set('domain', domain);
if (siteId && siteId !== 'default') {
searchParams.set('siteId', siteId);
}
const query = searchParams.toString();
const url = query ? `/admin/stats/comments?${query}` : '/admin/stats/comments';
return get<CommentStatsResponse>(url);
}
export function fetchVisitOverview(domain?: string): Promise<VisitOverviewResponse> {
export function fetchVisitOverview(siteId?: string): Promise<VisitOverviewResponse> {
const searchParams = new URLSearchParams();
if (domain) {
searchParams.set('domain', domain);
if (siteId && siteId !== 'default') {
searchParams.set('siteId', siteId);
}
const query = searchParams.toString();
const url = query ? `/admin/analytics/overview?${query}` : '/admin/analytics/overview';
return get<VisitOverviewResponse>(url);
}
export function fetchVisitPages(domain?: string, order?: 'pv' | 'latest'): Promise<VisitPagesResponse> {
export function fetchVisitPages(siteId?: string, order?: 'pv' | 'latest'): Promise<VisitPagesResponse> {
const searchParams = new URLSearchParams();
if (domain) {
searchParams.set('domain', domain);
if (siteId && siteId !== 'default') {
searchParams.set('siteId', siteId);
}
if (order) {
searchParams.set('order', order);
@@ -327,8 +338,8 @@ export function fetchVisitPages(domain?: string, order?: 'pv' | 'latest'): Promi
return get<VisitPagesResponse>(url);
}
export function fetchDomainList(): Promise<DomainListResponse> {
return get<DomainListResponse>('/admin/stats/domains');
export function fetchSiteList(): Promise<SiteListResponse> {
return get<SiteListResponse>('/admin/stats/sites');
}
export function fetchLikeStats(): Promise<LikeStatsResponse> {