Files
xiaoxia-saas/apps/web/src/api/auth.ts
T
灵应 4fe9fc0c95
CI/CD Pipeline / Deploy Staging (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Failing after 66h12m36s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 66h13m59s
feat: 登录持久化 + 片段逻辑重构
任务 1:登录持久化 + Token 刷新
- authStore: partialize 持久化 accessToken/refreshToken 到 localStorage
- auth.ts: 新增 refreshAccessToken API 函数
- client.ts: 401 拦截器实现刷新队列,无感续期

任务 2:片段逻辑重构(片段 = 时间规划 + 类型标记,不绑定素材)
- types.ts: 创建统一 ClipData/ClipType 类型定义
- EditingPlanner.tsx: handleAddClip 改为接收 type+duration,移除素材绑定
- TimelinePanel.tsx: "+" 卡片改为类型+时长选择器,移除素材拖拽
- ClipPropertiesPanel.tsx: 片段详情改为类型选择器+时长+startOffset
- PreviewPlayer.tsx: 适配新 ClipData 类型,用 type 显示图标
- EditingPlanner.css: 新增类型选择器样式,移除旧素材选择器样式

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 18:40:27 +08:00

132 lines
3.0 KiB
TypeScript

/**
* 认证相关 API
*/
import axios from "axios";
import apiClient from "./client";
// 类型定义
export interface LoginRequest {
email: string;
password: string;
}
export interface LoginResponse {
access_token: string;
refresh_token?: string | null;
token_type: string;
expires_in: number;
user_id: string;
email: string;
username: string;
display_name: string;
}
export interface RegisterRequest {
email: string;
password: string;
username: string;
display_name?: string;
}
export interface User {
id: string;
user_id: string;
email: string;
username: string;
display_name: string;
is_email_verified: boolean;
email_verified: boolean;
created_at?: string;
}
export interface UserResponse {
id?: string;
user_id?: string;
email: string;
username: string;
display_name: string;
is_email_verified?: boolean;
email_verified?: boolean;
created_at?: string;
}
export const normalizeUser = (data: UserResponse): User => {
const userId = data.id ?? data.user_id ?? "";
const emailVerified = data.is_email_verified ?? data.email_verified ?? false;
return {
id: userId,
user_id: userId,
email: data.email,
username: data.username,
display_name: data.display_name,
is_email_verified: emailVerified,
email_verified: emailVerified,
created_at: data.created_at,
};
};
// 登录
export const login = async (data: LoginRequest): Promise<LoginResponse> => {
const response = await apiClient.post("/auth/login", data);
return response.data;
};
// 刷新 access_token(使用裸 axios 避免拦截器递归)
export const refreshAccessToken = async (
refreshToken: string,
): Promise<LoginResponse> => {
const baseURL = apiClient.defaults.baseURL ?? "";
const response = await axios.post(`${baseURL}/auth/refresh`, {
refresh_token: refreshToken,
});
return response.data;
};
// 注册
export const register = async (
data: RegisterRequest,
): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/register", data);
return response.data;
};
// 登出
export const logout = async (): Promise<void> => {
await apiClient.post("/auth/logout");
};
// 获取当前用户
export const getCurrentUser = async (): Promise<User> => {
const response = await apiClient.get<UserResponse>("/auth/me");
return normalizeUser(response.data);
};
// 请求密码重置
export const requestPasswordReset = async (
email: string,
): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/forgot-password", { email });
return response.data;
};
// 重置密码
export const resetPassword = async (
token: string,
newPassword: string,
): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/reset-password", {
token,
new_password: newPassword,
});
return response.data;
};
// 验证邮箱
export const verifyEmail = async (
token: string,
): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/verify-email", { token });
return response.data;
};