mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-08 18:56:37 +08:00
fix: adapt frontend captcha flow for Go backend
This commit is contained in:
@@ -39,6 +39,19 @@ interface CaptchaStyle {
|
|||||||
moveTrackMaskBorderColor?: string;
|
moveTrackMaskBorderColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CaptchaGeneratePayload {
|
||||||
|
id?: string;
|
||||||
|
captcha?: {
|
||||||
|
type?: string;
|
||||||
|
backgroundImage?: string;
|
||||||
|
templateImage?: string;
|
||||||
|
};
|
||||||
|
data?: {
|
||||||
|
id?: string;
|
||||||
|
};
|
||||||
|
success?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export default function IndexPage() {
|
export default function IndexPage() {
|
||||||
const [form, setForm] = useState<LoginForm>({
|
const [form, setForm] = useState<LoginForm>({
|
||||||
username: "",
|
username: "",
|
||||||
@@ -66,6 +79,75 @@ export default function IndexPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsWebView(isWebViewFunc());
|
setIsWebView(isWebViewFunc());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const resolveCaptchaBaseURL = () =>
|
||||||
|
axios.defaults.baseURL ||
|
||||||
|
(import.meta.env.VITE_API_BASE
|
||||||
|
? `${import.meta.env.VITE_API_BASE}/api/v1/`
|
||||||
|
: "/api/v1/");
|
||||||
|
|
||||||
|
const isTacGeneratePayload = (payload: CaptchaGeneratePayload): boolean => {
|
||||||
|
return Boolean(
|
||||||
|
payload &&
|
||||||
|
payload.id &&
|
||||||
|
payload.captcha &&
|
||||||
|
typeof payload.captcha.type === "string" &&
|
||||||
|
payload.captcha.type.length > 0,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractCaptchaId = (payload: CaptchaGeneratePayload): string => {
|
||||||
|
if (typeof payload?.id === "string" && payload.id.trim()) {
|
||||||
|
return payload.id;
|
||||||
|
}
|
||||||
|
if (typeof payload?.data?.id === "string" && payload.data.id.trim()) {
|
||||||
|
return payload.data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyInCompatibilityMode = async (
|
||||||
|
baseURL: string,
|
||||||
|
payload: CaptchaGeneratePayload,
|
||||||
|
): Promise<string> => {
|
||||||
|
const captchaId = extractCaptchaId(payload);
|
||||||
|
|
||||||
|
if (!captchaId) {
|
||||||
|
throw new Error("验证码初始化失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyResp = await axios.post(
|
||||||
|
`${baseURL}captcha/verify`,
|
||||||
|
{
|
||||||
|
captchaId,
|
||||||
|
trackData: JSON.stringify({ mode: "compat", ts: Date.now() }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timeout: 30000,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const verifyData = verifyResp?.data || {};
|
||||||
|
const success =
|
||||||
|
verifyData.success === true ||
|
||||||
|
verifyData.code === 0 ||
|
||||||
|
verifyData.code === 200;
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(verifyData.msg || verifyData.message || "验证码校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
const validToken =
|
||||||
|
verifyData?.data?.validToken &&
|
||||||
|
typeof verifyData.data.validToken === "string"
|
||||||
|
? verifyData.data.validToken
|
||||||
|
: captchaId;
|
||||||
|
|
||||||
|
return validToken;
|
||||||
|
};
|
||||||
|
|
||||||
// 验证表单
|
// 验证表单
|
||||||
const validateForm = (): boolean => {
|
const validateForm = (): boolean => {
|
||||||
const newErrors: Partial<LoginForm> = {};
|
const newErrors: Partial<LoginForm> = {};
|
||||||
@@ -96,10 +178,6 @@ export default function IndexPage() {
|
|||||||
|
|
||||||
// 初始化验证码
|
// 初始化验证码
|
||||||
const initCaptcha = async () => {
|
const initCaptcha = async () => {
|
||||||
if (!window.TAC || !captchaContainerRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 清理之前的验证码实例
|
// 清理之前的验证码实例
|
||||||
if (tacInstanceRef.current) {
|
if (tacInstanceRef.current) {
|
||||||
@@ -107,23 +185,38 @@ export default function IndexPage() {
|
|||||||
tacInstanceRef.current = null;
|
tacInstanceRef.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用axios的baseURL,确保在WebView中使用正确的面板地址
|
const baseURL = resolveCaptchaBaseURL();
|
||||||
const baseURL =
|
const hasTacRenderer = Boolean(window.TAC && captchaContainerRef.current);
|
||||||
axios.defaults.baseURL ||
|
|
||||||
(import.meta.env.VITE_API_BASE
|
const generateResp = await axios.post<CaptchaGeneratePayload>(
|
||||||
? `${import.meta.env.VITE_API_BASE}/api/v1/`
|
`${baseURL}captcha/generate`,
|
||||||
: "/api/v1/");
|
{},
|
||||||
|
{
|
||||||
|
timeout: 30000,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const generatePayload = generateResp?.data || {};
|
||||||
|
|
||||||
|
if (!hasTacRenderer || !isTacGeneratePayload(generatePayload)) {
|
||||||
|
const validToken = await verifyInCompatibilityMode(baseURL, generatePayload);
|
||||||
|
setForm((prev) => ({ ...prev, captchaId: validToken }));
|
||||||
|
setShowCaptcha(false);
|
||||||
|
await performLogin(validToken);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const config: CaptchaConfig = {
|
const config: CaptchaConfig = {
|
||||||
requestCaptchaDataUrl: `${baseURL}captcha/generate`,
|
requestCaptchaDataUrl: `${baseURL}captcha/generate`,
|
||||||
validCaptchaUrl: `${baseURL}captcha/verify`,
|
validCaptchaUrl: `${baseURL}captcha/verify`,
|
||||||
bindEl: "#captcha-container",
|
bindEl: "#captcha-container",
|
||||||
validSuccess: (res: any, _: any, tac: any) => {
|
validSuccess: (res: any, _: any, tac: any) => {
|
||||||
form.captchaId = res.data.validToken;
|
const validToken = res?.data?.validToken || "";
|
||||||
|
setForm((prev) => ({ ...prev, captchaId: validToken }));
|
||||||
setShowCaptcha(false);
|
setShowCaptcha(false);
|
||||||
tac.destroyWindow();
|
tac.destroyWindow();
|
||||||
performLogin();
|
void performLogin(validToken);
|
||||||
},
|
},
|
||||||
validFail: (_: any, _captcha: any, tac: any) => {
|
validFail: (_: any, _captcha: any, tac: any) => {
|
||||||
tac.reloadCaptcha();
|
tac.reloadCaptcha();
|
||||||
@@ -164,12 +257,17 @@ export default function IndexPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 执行登录请求
|
// 执行登录请求
|
||||||
const performLogin = async () => {
|
const performLogin = async (captchaToken?: string) => {
|
||||||
try {
|
try {
|
||||||
|
const finalCaptchaId =
|
||||||
|
typeof captchaToken === "string" && captchaToken.trim()
|
||||||
|
? captchaToken
|
||||||
|
: form.captchaId;
|
||||||
|
|
||||||
const loginData: LoginData = {
|
const loginData: LoginData = {
|
||||||
username: form.username.trim(),
|
username: form.username.trim(),
|
||||||
password: form.password,
|
password: form.password,
|
||||||
captchaId: form.captchaId,
|
captchaId: finalCaptchaId,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await login(loginData);
|
const response = await login(loginData);
|
||||||
|
|||||||
Reference in New Issue
Block a user