feat(admin): 独立后台壳、站点分析与聊天监管,公开站拆出主题切换与访问埋点
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -609,6 +609,8 @@ export interface PublicSettings {
|
||||
anim_smooth_scroll: boolean;
|
||||
/** 装饰入场子开关;缺省 true */
|
||||
anim_chrome: boolean;
|
||||
/** 帖内 Markdown 外链是否新标签打开;缺省 true */
|
||||
post_link_new_tab: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_PUBLIC_SETTINGS: PublicSettings = {
|
||||
@@ -625,6 +627,7 @@ const DEFAULT_PUBLIC_SETTINGS: PublicSettings = {
|
||||
anim_code_fold: true,
|
||||
anim_smooth_scroll: true,
|
||||
anim_chrome: true,
|
||||
post_link_new_tab: true,
|
||||
};
|
||||
|
||||
function normalizePublicSettings(data: Partial<PublicSettings> | null | undefined): PublicSettings {
|
||||
@@ -653,6 +656,7 @@ function normalizePublicSettings(data: Partial<PublicSettings> | null | undefine
|
||||
anim_code_fold: data?.anim_code_fold !== false,
|
||||
anim_smooth_scroll: data?.anim_smooth_scroll !== false,
|
||||
anim_chrome: data?.anim_chrome !== false,
|
||||
post_link_new_tab: data?.post_link_new_tab !== false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -869,6 +873,133 @@ export interface EconomyStats {
|
||||
escrowed_points: number;
|
||||
posts_by_type: Record<string, number>;
|
||||
recent_ledger: PointLedgerItem[];
|
||||
checkin_series?: { date: string; count: number }[];
|
||||
ledger_by_reason?: { key: string; label: string; count: number }[];
|
||||
delta_series?: { date: string; granted: number; spent: number }[];
|
||||
net_delta_7d?: number;
|
||||
}
|
||||
|
||||
export interface AnalyticsDayCount {
|
||||
date: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface AnalyticsVisitDay {
|
||||
date: string;
|
||||
pv: number;
|
||||
uv: number;
|
||||
}
|
||||
|
||||
export interface AnalyticsNamedCount {
|
||||
key: string;
|
||||
label: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface AnalyticsTopViewPost {
|
||||
id: number;
|
||||
title: string;
|
||||
view_count: number;
|
||||
board_id: number;
|
||||
}
|
||||
|
||||
export interface AnalyticsOverview {
|
||||
range_days: number;
|
||||
today_pv: number;
|
||||
today_uv: number;
|
||||
visits: AnalyticsVisitDay[];
|
||||
posts_series: AnalyticsDayCount[];
|
||||
comments_series: AnalyticsDayCount[];
|
||||
registers_series: AnalyticsDayCount[];
|
||||
boards: AnalyticsNamedCount[];
|
||||
post_types: AnalyticsNamedCount[];
|
||||
top_viewed: AnalyticsTopViewPost[];
|
||||
}
|
||||
|
||||
export async function fetchAdminAnalytics(
|
||||
cookieHeader?: string,
|
||||
range: "7d" | "30d" = "7d"
|
||||
): Promise<AnalyticsOverview> {
|
||||
const res = await fetch(
|
||||
`${API_BASE}/api/admin/analytics/overview?range=${range}`,
|
||||
ssrInit(cookieHeaders(cookieHeader))
|
||||
);
|
||||
if (!res.ok) throw new Error("failed");
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function apiAdminAnalytics(range: "7d" | "30d" = "7d"): Promise<AnalyticsOverview> {
|
||||
const res = await fetchWithRefresh(`/api/admin/analytics/overview?range=${range}`, {
|
||||
method: "GET",
|
||||
headers: clientHeaders(),
|
||||
});
|
||||
if (!res.ok) throw new Error("failed");
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function apiTelemetryPageView(): Promise<void> {
|
||||
try {
|
||||
await fetchWithRefresh("/api/telemetry/pageview", {
|
||||
method: "POST",
|
||||
headers: clientHeaders(),
|
||||
});
|
||||
} catch {
|
||||
/* 埋点失败不影响浏览 */
|
||||
}
|
||||
}
|
||||
|
||||
export type AdminChatRoomType = "group" | "direct";
|
||||
|
||||
export interface AdminChatParticipant {
|
||||
id: number;
|
||||
name: string;
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
export interface AdminChatMessage {
|
||||
id: number;
|
||||
room_id: number;
|
||||
room_name: string;
|
||||
room_type?: AdminChatRoomType;
|
||||
is_default: boolean;
|
||||
sender_id: number;
|
||||
sender_name: string;
|
||||
sender_avatar: string;
|
||||
participants?: AdminChatParticipant[];
|
||||
content: string;
|
||||
recalled_at?: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export async function apiAdminChatMessages(
|
||||
page = 1,
|
||||
q = "",
|
||||
roomType: AdminChatRoomType = "group"
|
||||
): Promise<{
|
||||
messages: AdminChatMessage[];
|
||||
total: number;
|
||||
page: number;
|
||||
}> {
|
||||
const params = new URLSearchParams({
|
||||
page: String(page),
|
||||
room_type: roomType,
|
||||
});
|
||||
if (q.trim()) params.set("q", q.trim());
|
||||
const res = await fetchWithRefresh(`/api/admin/chat/messages?${params}`, {
|
||||
method: "GET",
|
||||
headers: clientHeaders(),
|
||||
});
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok) throw new Error(data.error || "获取聊天消息失败");
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function apiAdminRecallChatMessage(roomId: number, mid: number) {
|
||||
const res = await fetchWithRefresh(`/api/admin/chat/rooms/${roomId}/messages/${mid}`, {
|
||||
method: "DELETE",
|
||||
headers: clientHeaders(),
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function apiAdminEconomyStats(): Promise<EconomyStats & { error?: string }> {
|
||||
@@ -1325,6 +1456,7 @@ export interface AdminUser {
|
||||
role: string; // user | board_admin | admin | super_admin | owner
|
||||
board_ids: number[]; // 板块管理员的授权板块
|
||||
banned: boolean;
|
||||
points?: number;
|
||||
post_count: number;
|
||||
comment_count: number;
|
||||
created_at: string;
|
||||
@@ -1619,6 +1751,7 @@ export type UpdateSiteSettingsBody = {
|
||||
anim_code_fold?: boolean;
|
||||
anim_smooth_scroll?: boolean;
|
||||
anim_chrome?: boolean;
|
||||
post_link_new_tab?: boolean;
|
||||
};
|
||||
|
||||
// 更新站点设置(字段可选);accent 传空串恢复默认主题色
|
||||
|
||||
@@ -52,6 +52,7 @@ export interface RtSettingsData {
|
||||
anim_code_fold?: boolean;
|
||||
anim_smooth_scroll?: boolean;
|
||||
anim_chrome?: boolean;
|
||||
post_link_new_tab?: boolean;
|
||||
}
|
||||
|
||||
// 群聊新消息:data 为落库的 ChatMessage(含 sender)
|
||||
|
||||
Reference in New Issue
Block a user