Add managed content and keyboard usage insights
CI / verify (push) Has been cancelled
CI / publish (push) Has been cancelled

Introduce versioned official content workflows and privacy-safe keyboard analytics, while preventing repeat DeviceCheck sign-ins from incorrectly restricting eligible accounts.
This commit is contained in:
Rocky
2026-08-21 13:34:03 +08:00
parent b5212dcdc2
commit d0abe27623
55 changed files with 4690 additions and 111 deletions
+41
View File
@@ -3,6 +3,7 @@ import type {
AdminOperatorCreateRequest,
AdminOperatorProvisioning,
AdminSecuritySummary,
AdminHintPack,
AdminLoginResponse,
AuditQuery,
AuditLogEntry,
@@ -14,12 +15,17 @@ import type {
Overview,
PageResult,
ProductAnalyticsOverview,
CreateOfficialSkillRequest,
OfficialSkill,
OfficialSkillCatalog,
ReferralsQuery,
ReferralOverview,
SessionResponse,
UserDetail,
UsersQuery,
UserSummary,
UpdateHintPackRequest,
UpdateOfficialSkillRequest,
} from "./types";
const API_BASE = "/v1/admin";
@@ -73,6 +79,9 @@ function safeMessage(status: number, code?: string): string {
ADMIN_USERNAME_CONFLICT: "该管理员用户名已存在",
CANNOT_DISABLE_SELF: "不能停用当前登录的管理员",
LAST_SUPER_ADMIN_REQUIRED: "必须至少保留一名启用的超级管理员",
CONTENT_SKILL_NOT_FOUND: "未找到该官方 Skill",
CONTENT_SKILL_CONFLICT: "该官方 Skill ID 已存在",
CONTENT_HINT_PACK_NOT_FOUND: "该语言的 Hint pack 尚未发布",
RATE_LIMITED: "操作过于频繁,请稍后再试",
};
if (code && messages[code]) return messages[code];
@@ -187,6 +196,38 @@ export const adminApi = {
productAnalytics: (range: string) =>
request<ProductAnalyticsOverview>(`/analytics${encodeQuery({ range })}`),
contentSkills: () => request<OfficialSkillCatalog>("/content/skills"),
createContentSkill: (payload: CreateOfficialSkillRequest) =>
request<OfficialSkill>("/content/skills", {
method: "POST",
body: JSON.stringify(payload),
}),
updateContentSkill: (id: string, payload: UpdateOfficialSkillRequest) =>
request<OfficialSkill>(`/content/skills/${encodeURIComponent(id)}`, {
method: "PUT",
body: JSON.stringify(payload),
}),
setContentSkillEnabled: (id: string, enabled: boolean) =>
request<void>(
`/content/skills/${encodeURIComponent(id)}/${enabled ? "enable" : "disable"}`,
{ method: "POST" },
),
contentHintPack: (locale: "zh" | "en") =>
request<AdminHintPack>(`/content/hints/${locale}`),
updateContentHintPack: (
locale: "zh" | "en",
payload: UpdateHintPackRequest,
) =>
request<AdminHintPack>(`/content/hints/${locale}`, {
method: "PUT",
body: JSON.stringify(payload),
}),
users: (value: string | UsersQuery = "", legacyCursor?: string) => {
const params =
typeof value === "string"
+89 -1
View File
@@ -11,7 +11,76 @@ export type AdminAuditAction =
| "OPERATOR_UNLOCKED"
| "OPERATOR_CREDENTIALS_RESET"
| "OPERATOR_SESSIONS_REVOKED"
| "MANUAL_CREDIT_GRANTED";
| "MANUAL_CREDIT_GRANTED"
| "CONTENT_SKILL_CREATED"
| "CONTENT_SKILL_UPDATED"
| "CONTENT_SKILL_ENABLED"
| "CONTENT_SKILL_DISABLED"
| "CONTENT_HINT_PACK_PUBLISHED";
export interface SkillLocalization {
name: string;
summary: string;
prompt: string;
}
export interface OfficialSkill {
id: string;
systemImage: string;
sortOrder: number;
kind: "transform";
thinkingEnabled: boolean;
enabled: boolean;
localizations: {
"zh-Hans": SkillLocalization;
en: SkillLocalization;
};
}
export interface OfficialSkillCatalog {
revision: number;
generatedAt?: string;
skills: OfficialSkill[];
}
export type CreateOfficialSkillRequest = Omit<
OfficialSkill,
"kind" | "enabled"
>;
export type UpdateOfficialSkillRequest = Omit<
CreateOfficialSkillRequest,
"id"
>;
export interface AIHintCard {
id: string;
displayText?: string;
text?: string;
prompt: string;
category: string;
priority: number;
source: string;
locale: "zh" | "en";
conditions: string[];
metadata?: Record<string, unknown>;
}
export interface AdminHintPack {
locale: "zh" | "en";
generatedAt?: string;
expiresAt?: string;
intervalHours?: number;
version: number;
cards: AIHintCard[];
}
export interface UpdateHintPackRequest {
generatedAt?: string;
expiresAt?: string;
intervalHours?: number;
cards: AIHintCard[];
}
export interface CursorPageQuery {
cursor?: string;
@@ -176,6 +245,25 @@ export interface ProductAnalyticsOverview {
growthFunnel: FunnelStep[];
retention: AnalyticsCohort[];
aiFeatures: AnalyticsFeatureUsage[];
keyboardUsage: {
activeUsers: number;
activationToInput: AnalyticsRate;
chineseActiveUsers: number;
englishActiveUsers: number;
bilingualActiveUsers: number;
totalCharacters: number;
chineseCharacters: number;
englishCharacters: number;
otherCharacters: number;
chineseSharePercent?: number;
englishSharePercent?: number;
inputSessions: number;
averageCharactersPerInputSession?: number;
chineseOnlySessions: number;
englishOnlySessions: number;
mixedLanguageSessions: number;
otherOnlySessions: number;
};
referralFunnel: FunnelStep[];
guardrails: {
clientAiSuccessRate: AnalyticsRate;