feat: 邮箱注册验证 + 9项功能修复与优化
【认证系统】 - 新增邮箱验证码注册/登录流程 (sendEmailCode / verifyEmail / forgotPassword / resetPassword) - register-email 现在需要验证码 - 服务端新增 email_verification_codes 表 + patch-email-verification.js - App.tsx 登录后 emailVerified 检查提醒 - keyServerClient token 显式传递修复 401 错误 【电商模块】 - 自动推进: 策划完成后自动生成分镜图/视频 - 模特图选项 (性别/年龄/种族/体型/场景) 注入 AI 提示词 - 任务持久化指纹修复 (图片数量替代 blob URL) - 新增「视频换装」入口 (happyhorse-1.0-video-edit) 【剧本评分】 - 新增 .docx/.doc Word 文档支持 (ZIP解压+XML提取) - 历史记录支持点击查看/恢复评测结果 【画布】 - ReactFlow 节点禁止内置拖拽避免冲突 - 连接线拖拽弹窗优化 (预览线不消失, 弹窗跟踪鼠标) 【页面修复】 - 首页轮播图改为 aspect-ratio: 16/9 解决尺寸问题 - 资产库新增悬停删除按钮 - scriptEvalClient 改用服务端 /api/ai/chat 端点 - TokenUsagePage 未登录跳过 API 调用
This commit is contained in:
@@ -30,9 +30,26 @@ interface EmailAuthInput {
|
||||
email: string;
|
||||
password: string;
|
||||
username?: string;
|
||||
code?: string;
|
||||
betaCode?: string;
|
||||
}
|
||||
|
||||
interface EmailCodeInput {
|
||||
email: string;
|
||||
code: string;
|
||||
purpose?: "register" | "login";
|
||||
}
|
||||
|
||||
interface ForgotPasswordInput {
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface ResetPasswordInput {
|
||||
email: string;
|
||||
code: string;
|
||||
newPassword: string;
|
||||
}
|
||||
|
||||
interface PhoneAuthInput {
|
||||
phone: string;
|
||||
code: string;
|
||||
@@ -52,6 +69,19 @@ interface DeleteProjectOptions {
|
||||
cleanupUserData?: boolean;
|
||||
}
|
||||
|
||||
export interface RechargeOrderInput {
|
||||
planId: string;
|
||||
paymentMethod: "wechat" | "alipay" | "bank";
|
||||
}
|
||||
|
||||
export interface RechargeOrderResult {
|
||||
orderId: string;
|
||||
status: string;
|
||||
payUrl?: string | null;
|
||||
qrCodeUrl?: string | null;
|
||||
message?: string | null;
|
||||
}
|
||||
|
||||
export interface WechatLoginTicket {
|
||||
configured: boolean;
|
||||
url?: string;
|
||||
@@ -624,6 +654,21 @@ function normalizeEnterpriseUsageSummary(payload: unknown): WebEnterpriseUsageSu
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeRechargeOrder(payload: unknown): RechargeOrderResult {
|
||||
const raw = unwrapApiPayload(payload);
|
||||
if (!isRecord(raw)) {
|
||||
return { orderId: `local-${Date.now()}`, status: "pending", message: "订单已提交,请联系客服确认到账。" };
|
||||
}
|
||||
|
||||
return {
|
||||
orderId: toStringValue(raw.orderId ?? raw.order_id ?? raw.id, `local-${Date.now()}`),
|
||||
status: toStringValue(raw.status, "pending"),
|
||||
payUrl: toNullableString(raw.payUrl ?? raw.pay_url ?? raw.checkoutUrl ?? raw.checkout_url),
|
||||
qrCodeUrl: toNullableString(raw.qrCodeUrl ?? raw.qr_code_url ?? raw.qrcodeUrl),
|
||||
message: toNullableString(raw.message ?? raw.notice),
|
||||
};
|
||||
}
|
||||
|
||||
function buildProjectUpsertPayload(workflow: WebCanvasWorkflow, session: WebUserSession): Record<string, unknown> {
|
||||
const userId = String(session.user.id).replace(/[^a-zA-Z0-9_-]/g, "");
|
||||
const projectId = workflow.id.trim();
|
||||
@@ -714,6 +759,7 @@ export const keyServerClient = {
|
||||
email: input.email.trim(),
|
||||
username: input.username?.trim() || undefined,
|
||||
password: input.password,
|
||||
code: input.code?.trim() || undefined,
|
||||
betaCode: input.betaCode?.trim() || undefined,
|
||||
},
|
||||
}),
|
||||
@@ -731,6 +777,30 @@ export const keyServerClient = {
|
||||
body: { phone: phone.trim(), purpose, betaCode: betaCode?.trim() || undefined },
|
||||
});
|
||||
},
|
||||
async sendEmailCode(email: string, purpose: "login" | "register" | "reset", betaCode?: string): Promise<{ cooldownSeconds?: number; ttlSeconds?: number; devCode?: string }> {
|
||||
return request<{ cooldownSeconds?: number; ttlSeconds?: number; devCode?: string }>("/auth/email/send-code", {
|
||||
method: "POST",
|
||||
body: { email: email.trim(), purpose, betaCode: betaCode?.trim() || undefined },
|
||||
});
|
||||
},
|
||||
async verifyEmail(input: EmailCodeInput): Promise<{ success: boolean }> {
|
||||
return request<{ success: boolean }>("/auth/email/verify", {
|
||||
method: "POST",
|
||||
body: { email: input.email.trim(), code: input.code.trim(), purpose: input.purpose || "register" },
|
||||
});
|
||||
},
|
||||
async forgotPassword(input: ForgotPasswordInput): Promise<{ success: boolean; message?: string }> {
|
||||
return request<{ success: boolean; message?: string }>("/auth/forgot-password", {
|
||||
method: "POST",
|
||||
body: { email: input.email.trim() },
|
||||
});
|
||||
},
|
||||
async resetPassword(input: ResetPasswordInput): Promise<{ success: boolean; message?: string }> {
|
||||
return request<{ success: boolean; message?: string }>("/auth/reset-password", {
|
||||
method: "POST",
|
||||
body: { email: input.email.trim(), code: input.code.trim(), newPassword: input.newPassword },
|
||||
});
|
||||
},
|
||||
async loginPhone(input: PhoneAuthInput): Promise<WebUserSession> {
|
||||
const session = normalizeLoginResult(
|
||||
await request<unknown>("/auth/login-phone", {
|
||||
@@ -855,13 +925,23 @@ export const keyServerClient = {
|
||||
return normalizeProjectContent(response, projectId);
|
||||
},
|
||||
async getUsageSummary(): Promise<WebUsageSummary> {
|
||||
return normalizeUsageSummary(await request<unknown>("/user/usage/summary"));
|
||||
const stored = readStoredSession();
|
||||
return normalizeUsageSummary(await request<unknown>("/user/usage/summary", { token: stored?.token }));
|
||||
},
|
||||
async getEnterpriseUsageSummary(): Promise<WebEnterpriseUsageSummary> {
|
||||
return normalizeEnterpriseUsageSummary(await request<unknown>("/enterprise/usage/summary"));
|
||||
const stored = readStoredSession();
|
||||
return normalizeEnterpriseUsageSummary(await request<unknown>("/enterprise/usage/summary", { token: stored?.token }));
|
||||
},
|
||||
async getPersonalUsageSummary(): Promise<WebEnterpriseUsageSummary> {
|
||||
return normalizeEnterpriseUsageSummary(await request<unknown>("/user/usage/credits"));
|
||||
const stored = readStoredSession();
|
||||
return normalizeEnterpriseUsageSummary(await request<unknown>("/user/usage/credits", { token: stored?.token }));
|
||||
},
|
||||
async createRechargeOrder(input: RechargeOrderInput): Promise<RechargeOrderResult> {
|
||||
const response = await request<unknown>("/payments/recharge-orders", {
|
||||
method: "POST",
|
||||
body: input,
|
||||
});
|
||||
return normalizeRechargeOrder(response);
|
||||
},
|
||||
async createProjectSpace(workflow: WebCanvasWorkflow): Promise<WebProjectSummary> {
|
||||
const stored = readStoredSession();
|
||||
@@ -929,8 +1009,8 @@ export const keyServerClient = {
|
||||
});
|
||||
},
|
||||
|
||||
async getClientErrors(page = 1): Promise<{ items: unknown[]; total: number }> {
|
||||
const data = await request<{ items: unknown[]; total: number }>(`/client-errors?page=${page}`);
|
||||
async getClientErrors(page = 1): Promise<{ items: import("../components/AdminMonitor").ClientErrorItem[]; total: number }> {
|
||||
const data = await request<{ items: import("../components/AdminMonitor").ClientErrorItem[]; total: number }>(`/client-errors?page=${page}`);
|
||||
return data;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user