mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-03-30 05:41:37 +00:00
Merge pull request #2571 from seefs001/feature/check-in-security-check
feat: check-in feature integrates Turnstile security check
This commit is contained in:
@@ -96,7 +96,7 @@ func SetApiRouter(router *gin.Engine) {
|
||||
|
||||
// Check-in routes
|
||||
selfRoute.GET("/checkin", controller.GetCheckinStatus)
|
||||
selfRoute.POST("/checkin", controller.DoCheckin)
|
||||
selfRoute.POST("/checkin", middleware.TurnstileCheck(), controller.DoCheckin)
|
||||
}
|
||||
|
||||
adminRoute := userRoute.Group("/")
|
||||
|
||||
@@ -451,7 +451,12 @@ const PersonalSetting = () => {
|
||||
{/* 签到日历 - 仅在启用时显示 */}
|
||||
{status?.checkin_enabled && (
|
||||
<div className='mt-4 md:mt-6'>
|
||||
<CheckinCalendar t={t} status={status} />
|
||||
<CheckinCalendar
|
||||
t={t}
|
||||
status={status}
|
||||
turnstileEnabled={turnstileEnabled}
|
||||
turnstileSiteKey={turnstileSiteKey}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
Spin,
|
||||
Tooltip,
|
||||
Collapsible,
|
||||
Modal,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
CalendarCheck,
|
||||
@@ -35,11 +36,14 @@ import {
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
} from 'lucide-react';
|
||||
import Turnstile from 'react-turnstile';
|
||||
import { API, showError, showSuccess, renderQuota } from '../../../../helpers';
|
||||
|
||||
const CheckinCalendar = ({ t, status }) => {
|
||||
const CheckinCalendar = ({ t, status, turnstileEnabled, turnstileSiteKey }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [checkinLoading, setCheckinLoading] = useState(false);
|
||||
const [turnstileModalVisible, setTurnstileModalVisible] = useState(false);
|
||||
const [turnstileWidgetKey, setTurnstileWidgetKey] = useState(0);
|
||||
const [checkinData, setCheckinData] = useState({
|
||||
enabled: false,
|
||||
stats: {
|
||||
@@ -109,11 +113,23 @@ const CheckinCalendar = ({ t, status }) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 执行签到
|
||||
const doCheckin = async () => {
|
||||
const postCheckin = async (token) => {
|
||||
const url = token
|
||||
? `/api/user/checkin?turnstile=${encodeURIComponent(token)}`
|
||||
: '/api/user/checkin';
|
||||
return API.post(url);
|
||||
};
|
||||
|
||||
const shouldTriggerTurnstile = (message) => {
|
||||
if (!turnstileEnabled) return false;
|
||||
if (typeof message !== 'string') return true;
|
||||
return message.includes('Turnstile');
|
||||
};
|
||||
|
||||
const doCheckin = async (token) => {
|
||||
setCheckinLoading(true);
|
||||
try {
|
||||
const res = await API.post('/api/user/checkin');
|
||||
const res = await postCheckin(token);
|
||||
const { success, data, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess(
|
||||
@@ -121,7 +137,19 @@ const CheckinCalendar = ({ t, status }) => {
|
||||
);
|
||||
// 刷新签到状态
|
||||
fetchCheckinStatus(currentMonth);
|
||||
setTurnstileModalVisible(false);
|
||||
} else {
|
||||
if (!token && shouldTriggerTurnstile(message)) {
|
||||
if (!turnstileSiteKey) {
|
||||
showError('Turnstile is enabled but site key is empty.');
|
||||
return;
|
||||
}
|
||||
setTurnstileModalVisible(true);
|
||||
return;
|
||||
}
|
||||
if (token && shouldTriggerTurnstile(message)) {
|
||||
setTurnstileWidgetKey((v) => v + 1);
|
||||
}
|
||||
showError(message || t('签到失败'));
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -186,6 +214,30 @@ const CheckinCalendar = ({ t, status }) => {
|
||||
|
||||
return (
|
||||
<Card className='!rounded-2xl'>
|
||||
<Modal
|
||||
title='Security Check'
|
||||
visible={turnstileModalVisible}
|
||||
footer={null}
|
||||
centered
|
||||
onCancel={() => {
|
||||
setTurnstileModalVisible(false);
|
||||
setTurnstileWidgetKey((v) => v + 1);
|
||||
}}
|
||||
>
|
||||
<div className='flex justify-center py-2'>
|
||||
<Turnstile
|
||||
key={turnstileWidgetKey}
|
||||
sitekey={turnstileSiteKey}
|
||||
onVerify={(token) => {
|
||||
doCheckin(token);
|
||||
}}
|
||||
onExpire={() => {
|
||||
setTurnstileWidgetKey((v) => v + 1);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* 卡片头部 */}
|
||||
<div className='flex items-center justify-between'>
|
||||
<div
|
||||
@@ -221,7 +273,7 @@ const CheckinCalendar = ({ t, status }) => {
|
||||
type='primary'
|
||||
theme='solid'
|
||||
icon={<Gift size={16} />}
|
||||
onClick={doCheckin}
|
||||
onClick={() => doCheckin()}
|
||||
loading={checkinLoading || !initialLoaded}
|
||||
disabled={!initialLoaded || checkinData.stats?.checked_in_today}
|
||||
className='!bg-green-600 hover:!bg-green-700'
|
||||
|
||||
@@ -42,12 +42,12 @@ export class SecureVerificationService {
|
||||
isPasskeySupported(),
|
||||
]);
|
||||
|
||||
console.log('=== DEBUGGING VERIFICATION METHODS ===');
|
||||
console.log('2FA Response:', JSON.stringify(twoFAResponse, null, 2));
|
||||
console.log(
|
||||
'Passkey Response:',
|
||||
JSON.stringify(passkeyResponse, null, 2),
|
||||
);
|
||||
// console.log('=== DEBUGGING VERIFICATION METHODS ===');
|
||||
// console.log('2FA Response:', JSON.stringify(twoFAResponse, null, 2));
|
||||
// console.log(
|
||||
// 'Passkey Response:',
|
||||
// JSON.stringify(passkeyResponse, null, 2),
|
||||
// );
|
||||
|
||||
const has2FA =
|
||||
twoFAResponse.data?.success &&
|
||||
|
||||
Reference in New Issue
Block a user