fix(api): propagate requestId for register and reset-password

Backend requires X-Request-Id header from the verification code send
response. Wire requestId through email/auth-extra API returns, register
and forgot-password pages, and auth API request headers.
This commit is contained in:
zetaloop
2026-04-24 05:06:03 +08:00
parent e5fa8aa38b
commit 2ab075d173
5 changed files with 41 additions and 7 deletions
+9 -2
View File
@@ -29,6 +29,7 @@ const forgotSchema = z
export default function ForgotPasswordPage() { export default function ForgotPasswordPage() {
const router = useRouter() const router = useRouter()
const [countdown, setCountdown] = useState(0) const [countdown, setCountdown] = useState(0)
const [requestId, setRequestId] = useState("")
const { const {
register, register,
@@ -52,7 +53,8 @@ export default function ForgotPasswordPage() {
try { try {
const email = getValues("email") const email = getValues("email")
await sendForgotPasswordCode(email) const rid = await sendForgotPasswordCode(email)
setRequestId(rid)
setCountdown(60) setCountdown(60)
notifySuccess("验证码已发送到你的邮箱") notifySuccess("验证码已发送到你的邮箱")
} catch (err) { } catch (err) {
@@ -62,7 +64,12 @@ export default function ForgotPasswordPage() {
const onSubmit = async (data: z.infer<typeof forgotSchema>) => { const onSubmit = async (data: z.infer<typeof forgotSchema>) => {
try { try {
await resetPassword({ email: data.email, vcode: data.vcode, newPassword: data.newPassword }) await resetPassword({
email: data.email,
vcode: data.vcode,
newPassword: data.newPassword,
requestId,
})
notifySuccess("密码已重置") notifySuccess("密码已重置")
router.push("/login") router.push("/login")
} catch (err) { } catch (err) {
+4 -1
View File
@@ -40,6 +40,7 @@ export default function RegisterPage() {
const [showPassword, setShowPassword] = useState(false) const [showPassword, setShowPassword] = useState(false)
const [showConfirmPassword, setShowConfirmPassword] = useState(false) const [showConfirmPassword, setShowConfirmPassword] = useState(false)
const [countdown, setCountdown] = useState(0) const [countdown, setCountdown] = useState(0)
const [requestId, setRequestId] = useState("")
const { const {
register, register,
handleSubmit, handleSubmit,
@@ -65,7 +66,8 @@ export default function RegisterPage() {
const email = String(getValues("email") ?? "") const email = String(getValues("email") ?? "")
try { try {
await sendEmailVerificationCode({ email, scene: "register" }) const rid = await sendEmailVerificationCode({ email, scene: "register" })
setRequestId(rid)
setCountdown(60) setCountdown(60)
notifySuccess("验证码已发送到你的邮箱") notifySuccess("验证码已发送到你的邮箱")
} catch (err) { } catch (err) {
@@ -80,6 +82,7 @@ export default function RegisterPage() {
email: data.email, email: data.email,
password: data.password, password: data.password,
vcode: data.vcode, vcode: data.vcode,
requestId,
}) })
storeLogin(user, ["consumer"]) storeLogin(user, ["consumer"])
router.push("/") router.push("/")
+9 -2
View File
@@ -1,8 +1,15 @@
import { httpJson } from "./http" import { httpJson } from "./http"
export async function sendForgotPasswordCode(email: string): Promise<void> { interface SendVerificationCodeResp {
await httpJson<unknown>("/api/v1/auth/forgot-password/send", { requestId: string
expireInSec: number
message: string
}
export async function sendForgotPasswordCode(email: string): Promise<string> {
const res = await httpJson<SendVerificationCodeResp>("/api/v1/auth/forgot-password/send", {
method: "POST", method: "POST",
json: { email }, json: { email },
}) })
return res.requestId
} }
+10
View File
@@ -7,6 +7,7 @@ export type RegisterInput = {
email: string email: string
password: string password: string
vcode?: string vcode?: string
requestId?: string
} }
export type LoginInput = { export type LoginInput = {
@@ -28,9 +29,13 @@ export async function register(input: RegisterInput): Promise<User> {
if (!email) throwApiError(400, "请输入邮箱") if (!email) throwApiError(400, "请输入邮箱")
if (!password || password.length < 6) throwApiError(400, "密码至少6位") if (!password || password.length < 6) throwApiError(400, "密码至少6位")
const headers: Record<string, string> = {}
if (input.requestId) headers["X-Request-Id"] = input.requestId
const res = await httpJson<{ user: User }>("/api/v1/auth/register", { const res = await httpJson<{ user: User }>("/api/v1/auth/register", {
method: "POST", method: "POST",
json: { username, email, password, vcode }, json: { username, email, password, vcode },
headers,
}) })
return res.user return res.user
} }
@@ -53,6 +58,7 @@ export async function resetPassword(input: {
email: string email: string
vcode: string vcode: string
newPassword: string newPassword: string
requestId?: string
}): Promise<void> { }): Promise<void> {
const email = input.email.trim() const email = input.email.trim()
const vcode = input.vcode.trim() const vcode = input.vcode.trim()
@@ -62,8 +68,12 @@ export async function resetPassword(input: {
if (!vcode) throwApiError(400, "请输入验证码") if (!vcode) throwApiError(400, "请输入验证码")
if (!newPassword || newPassword.length < 8) throwApiError(400, "密码至少8位") if (!newPassword || newPassword.length < 8) throwApiError(400, "密码至少8位")
const headers: Record<string, string> = {}
if (input.requestId) headers["X-Request-Id"] = input.requestId
await httpJson<unknown>("/api/v1/auth/reset-password", { await httpJson<unknown>("/api/v1/auth/reset-password", {
method: "POST", method: "POST",
json: { email, vcode, newPassword }, json: { email, vcode, newPassword },
headers,
}) })
} }
+9 -2
View File
@@ -1,11 +1,18 @@
import { httpJson } from "./http" import { httpJson } from "./http"
interface SendVerificationCodeResp {
requestId: string
expireInSec: number
message: string
}
export async function sendEmailVerificationCode(input: { export async function sendEmailVerificationCode(input: {
email: string email: string
scene: string scene: string
}): Promise<void> { }): Promise<string> {
await httpJson<unknown>("/api/v1/email/verification-code/send", { const res = await httpJson<SendVerificationCodeResp>("/api/v1/email/verification-code/send", {
method: "POST", method: "POST",
json: { email: input.email, scene: input.scene }, json: { email: input.email, scene: input.scene },
}) })
return res.requestId
} }