Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
b32cecb4
authored
Jan 03, 2026
by
Seefs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: check-in feature integrates Turnstile security check
parent
ea60d305
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
70 additions
and
13 deletions
+70
-13
router/api-router.go
+1
-1
web/src/components/settings/PersonalSetting.jsx
+6
-1
web/src/components/settings/personal/cards/CheckinCalendar.jsx
+57
-5
web/src/services/secureVerification.js
+6
-6
No files found.
router/api-router.go
View file @
b32cecb4
...
@@ -96,7 +96,7 @@ func SetApiRouter(router *gin.Engine) {
...
@@ -96,7 +96,7 @@ func SetApiRouter(router *gin.Engine) {
// Check-in routes
// Check-in routes
selfRoute
.
GET
(
"/checkin"
,
controller
.
GetCheckinStatus
)
selfRoute
.
GET
(
"/checkin"
,
controller
.
GetCheckinStatus
)
selfRoute
.
POST
(
"/checkin"
,
controller
.
DoCheckin
)
selfRoute
.
POST
(
"/checkin"
,
middleware
.
TurnstileCheck
(),
controller
.
DoCheckin
)
}
}
adminRoute
:=
userRoute
.
Group
(
"/"
)
adminRoute
:=
userRoute
.
Group
(
"/"
)
...
...
web/src/components/settings/PersonalSetting.jsx
View file @
b32cecb4
...
@@ -451,7 +451,12 @@ const PersonalSetting = () => {
...
@@ -451,7 +451,12 @@ const PersonalSetting = () => {
{
/* 签到日历 - 仅在启用时显示 */
}
{
/* 签到日历 - 仅在启用时显示 */
}
{
status
?.
checkin_enabled
&&
(
{
status
?.
checkin_enabled
&&
(
<
div
className=
'mt-4 md:mt-6'
>
<
div
className=
'mt-4 md:mt-6'
>
<
CheckinCalendar
t=
{
t
}
status=
{
status
}
/>
<
CheckinCalendar
t=
{
t
}
status=
{
status
}
turnstileEnabled=
{
turnstileEnabled
}
turnstileSiteKey=
{
turnstileSiteKey
}
/>
</
div
>
</
div
>
)
}
)
}
...
...
web/src/components/settings/personal/cards/CheckinCalendar.jsx
View file @
b32cecb4
...
@@ -27,6 +27,7 @@ import {
...
@@ -27,6 +27,7 @@ import {
Spin
,
Spin
,
Tooltip
,
Tooltip
,
Collapsible
,
Collapsible
,
Modal
,
}
from
'@douyinfe/semi-ui'
;
}
from
'@douyinfe/semi-ui'
;
import
{
import
{
CalendarCheck
,
CalendarCheck
,
...
@@ -35,11 +36,14 @@ import {
...
@@ -35,11 +36,14 @@ import {
ChevronDown
,
ChevronDown
,
ChevronUp
,
ChevronUp
,
}
from
'lucide-react'
;
}
from
'lucide-react'
;
import
Turnstile
from
'react-turnstile'
;
import
{
API
,
showError
,
showSuccess
,
renderQuota
}
from
'../../../../helpers'
;
import
{
API
,
showError
,
showSuccess
,
renderQuota
}
from
'../../../../helpers'
;
const
CheckinCalendar
=
({
t
,
status
})
=>
{
const
CheckinCalendar
=
({
t
,
status
,
turnstileEnabled
,
turnstileSiteKey
})
=>
{
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
checkinLoading
,
setCheckinLoading
]
=
useState
(
false
);
const
[
checkinLoading
,
setCheckinLoading
]
=
useState
(
false
);
const
[
turnstileModalVisible
,
setTurnstileModalVisible
]
=
useState
(
false
);
const
[
turnstileWidgetKey
,
setTurnstileWidgetKey
]
=
useState
(
0
);
const
[
checkinData
,
setCheckinData
]
=
useState
({
const
[
checkinData
,
setCheckinData
]
=
useState
({
enabled
:
false
,
enabled
:
false
,
stats
:
{
stats
:
{
...
@@ -109,11 +113,23 @@ const CheckinCalendar = ({ t, status }) => {
...
@@ -109,11 +113,23 @@ const CheckinCalendar = ({ t, status }) => {
}
}
};
};
// 执行签到
const
postCheckin
=
async
(
token
)
=>
{
const
doCheckin
=
async
()
=>
{
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
);
setCheckinLoading
(
true
);
try
{
try
{
const
res
=
await
API
.
post
(
'/api/user/checkin'
);
const
res
=
await
postCheckin
(
token
);
const
{
success
,
data
,
message
}
=
res
.
data
;
const
{
success
,
data
,
message
}
=
res
.
data
;
if
(
success
)
{
if
(
success
)
{
showSuccess
(
showSuccess
(
...
@@ -121,7 +137,19 @@ const CheckinCalendar = ({ t, status }) => {
...
@@ -121,7 +137,19 @@ const CheckinCalendar = ({ t, status }) => {
);
);
// 刷新签到状态
// 刷新签到状态
fetchCheckinStatus
(
currentMonth
);
fetchCheckinStatus
(
currentMonth
);
setTurnstileModalVisible
(
false
);
}
else
{
}
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
(
'签到失败'
));
showError
(
message
||
t
(
'签到失败'
));
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
...
@@ -186,6 +214,30 @@ const CheckinCalendar = ({ t, status }) => {
...
@@ -186,6 +214,30 @@ const CheckinCalendar = ({ t, status }) => {
return
(
return
(
<
Card
className=
'!rounded-2xl'
>
<
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
className=
'flex items-center justify-between'
>
<
div
<
div
...
@@ -221,7 +273,7 @@ const CheckinCalendar = ({ t, status }) => {
...
@@ -221,7 +273,7 @@ const CheckinCalendar = ({ t, status }) => {
type=
'primary'
type=
'primary'
theme=
'solid'
theme=
'solid'
icon=
{
<
Gift
size=
{
16
}
/>
}
icon=
{
<
Gift
size=
{
16
}
/>
}
onClick=
{
doCheckin
}
onClick=
{
()
=>
doCheckin
()
}
loading=
{
checkinLoading
||
!
initialLoaded
}
loading=
{
checkinLoading
||
!
initialLoaded
}
disabled=
{
!
initialLoaded
||
checkinData
.
stats
?.
checked_in_today
}
disabled=
{
!
initialLoaded
||
checkinData
.
stats
?.
checked_in_today
}
className=
'!bg-green-600 hover:!bg-green-700'
className=
'!bg-green-600 hover:!bg-green-700'
...
...
web/src/services/secureVerification.js
View file @
b32cecb4
...
@@ -42,12 +42,12 @@ export class SecureVerificationService {
...
@@ -42,12 +42,12 @@ export class SecureVerificationService {
isPasskeySupported
(),
isPasskeySupported
(),
]);
]);
console
.
log
(
'=== DEBUGGING VERIFICATION METHODS ==='
);
//
console.log('=== DEBUGGING VERIFICATION METHODS ===');
console
.
log
(
'2FA Response:'
,
JSON
.
stringify
(
twoFAResponse
,
null
,
2
));
//
console.log('2FA Response:', JSON.stringify(twoFAResponse, null, 2));
console
.
log
(
//
console.log(
'Passkey Response:'
,
//
'Passkey Response:',
JSON
.
stringify
(
passkeyResponse
,
null
,
2
),
//
JSON.stringify(passkeyResponse, null, 2),
);
//
);
const
has2FA
=
const
has2FA
=
twoFAResponse
.
data
?.
success
&&
twoFAResponse
.
data
?.
success
&&
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment