mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
feat: 新增签到 Cookie 失效警告功能
- 签到认证失败时返回 cookie_expired 标记而非静默跳过 - 前端显示"签到 Cookie 已失效"警告提示 - 支持 auth_expired 状态,余额数据仍可正常显示 - 认证失败(auth_failed)时清除余额缓存以便重试
This commit is contained in:
@@ -152,9 +152,9 @@
|
|||||||
<span class="font-semibold text-foreground/90 min-w-[4.5rem] tabular-nums">
|
<span class="font-semibold text-foreground/90 min-w-[4.5rem] tabular-nums">
|
||||||
{{ formatBalanceDisplay(getProviderBalance(provider.id)) }}
|
{{ formatBalanceDisplay(getProviderBalance(provider.id)) }}
|
||||||
</span>
|
</span>
|
||||||
<!-- 窗口限额 + 签到状态 -->
|
<!-- 窗口限额 + 签到状态 + Cookie 失效警告 -->
|
||||||
<div
|
<div
|
||||||
v-if="getProviderBalanceExtra(provider.id, provider.ops_architecture_id).length > 0 || getProviderCheckin(provider.id)"
|
v-if="getProviderBalanceExtra(provider.id, provider.ops_architecture_id).length > 0 || getProviderCheckin(provider.id) || getProviderCookieExpired(provider.id)"
|
||||||
class="text-muted-foreground/70 space-y-0.5"
|
class="text-muted-foreground/70 space-y-0.5"
|
||||||
>
|
>
|
||||||
<!-- 限额(进度条 + 倒计时,每行一个) -->
|
<!-- 限额(进度条 + 倒计时,每行一个) -->
|
||||||
@@ -184,9 +184,19 @@
|
|||||||
>{{ formatResetCountdown(item.resetsAt) }}</span>
|
>{{ formatResetCountdown(item.resetsAt) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- Cookie 失效警告 -->
|
||||||
|
<div
|
||||||
|
v-if="getProviderCookieExpired(provider.id)"
|
||||||
|
class="flex items-center gap-1"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="text-[10px] text-amber-600 dark:text-amber-500"
|
||||||
|
:title="getProviderCookieExpired(provider.id)?.message"
|
||||||
|
>签到 Cookie 已失效</span>
|
||||||
|
</div>
|
||||||
<!-- 签到状态 -->
|
<!-- 签到状态 -->
|
||||||
<div
|
<div
|
||||||
v-if="getProviderCheckin(provider.id)"
|
v-else-if="getProviderCheckin(provider.id)"
|
||||||
class="flex items-center gap-1.5"
|
class="flex items-center gap-1.5"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -429,9 +439,15 @@
|
|||||||
class="text-muted-foreground"
|
class="text-muted-foreground"
|
||||||
>
|
>
|
||||||
余额 <span class="font-semibold text-foreground/90">{{ formatBalanceDisplay(getProviderBalance(provider.id)) }}</span>
|
余额 <span class="font-semibold text-foreground/90">{{ formatBalanceDisplay(getProviderBalance(provider.id)) }}</span>
|
||||||
|
<!-- Cookie 失效警告 -->
|
||||||
|
<span
|
||||||
|
v-if="getProviderCookieExpired(provider.id)"
|
||||||
|
class="ml-1 text-amber-600 dark:text-amber-500"
|
||||||
|
:title="getProviderCookieExpired(provider.id)?.message"
|
||||||
|
>签到 Cookie 已失效</span>
|
||||||
<!-- 签到状态显示 -->
|
<!-- 签到状态显示 -->
|
||||||
<span
|
<span
|
||||||
v-if="getProviderCheckin(provider.id) && getProviderCheckin(provider.id)?.success !== false"
|
v-else-if="getProviderCheckin(provider.id) && getProviderCheckin(provider.id)?.success !== false"
|
||||||
class="ml-1 text-muted-foreground"
|
class="ml-1 text-muted-foreground"
|
||||||
:title="getProviderCheckin(provider.id)?.message"
|
:title="getProviderCheckin(provider.id)?.message"
|
||||||
>已签到</span>
|
>已签到</span>
|
||||||
@@ -724,7 +740,8 @@ function isBalanceInfo(data: unknown): data is { total_available: number | null;
|
|||||||
// 获取 provider 的余额显示
|
// 获取 provider 的余额显示
|
||||||
function getProviderBalance(providerId: string): { available: number | null; currency: string } | null {
|
function getProviderBalance(providerId: string): { available: number | null; currency: string } | null {
|
||||||
const result = balanceCache.value[providerId]
|
const result = balanceCache.value[providerId]
|
||||||
if (!result || result.status !== 'success' || !result.data) {
|
// auth_expired 时余额数据仍有效(只是签到 Cookie 失效)
|
||||||
|
if (!result || (result.status !== 'success' && result.status !== 'auth_expired') || !result.data) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
if (!isBalanceInfo(result.data)) {
|
if (!isBalanceInfo(result.data)) {
|
||||||
@@ -776,6 +793,27 @@ function getProviderCheckin(providerId: string): { success: boolean | null; mess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取 provider 的 Cookie 失效状态(从 extra 字段)
|
||||||
|
function getProviderCookieExpired(providerId: string): { expired: boolean; message: string } | null {
|
||||||
|
const result = balanceCache.value[providerId]
|
||||||
|
if (!result || !result.data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
// 支持 status 为 'success' 或 'auth_expired'(Cookie 失效时状态会变为 auth_expired)
|
||||||
|
if (result.status !== 'success' && result.status !== 'auth_expired') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const data = result.data as Record<string, any>
|
||||||
|
const extra = data.extra
|
||||||
|
if (!extra || !extra.cookie_expired) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
expired: true,
|
||||||
|
message: extra.cookie_expired_message || 'Cookie 已失效'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 格式化余额显示
|
// 格式化余额显示
|
||||||
function formatBalanceDisplay(balance: { available: number | null; currency: string } | null): string {
|
function formatBalanceDisplay(balance: { available: number | null; currency: string } | null): string {
|
||||||
if (!balance || balance.available == null) {
|
if (!balance || balance.available == null) {
|
||||||
@@ -812,7 +850,8 @@ function getProviderBalanceExtra(providerId: string, architectureId?: string): B
|
|||||||
if (!architectureId) return []
|
if (!architectureId) return []
|
||||||
|
|
||||||
const result = balanceCache.value[providerId]
|
const result = balanceCache.value[providerId]
|
||||||
if (!result || result.status !== 'success' || !result.data) {
|
// auth_expired 时余额数据仍有效(只是签到 Cookie 失效)
|
||||||
|
if (!result || (result.status !== 'success' && result.status !== 'auth_expired') || !result.data) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import httpx
|
|||||||
from src.services.provider_ops.actions.base import ProviderAction
|
from src.services.provider_ops.actions.base import ProviderAction
|
||||||
from src.services.provider_ops.types import (
|
from src.services.provider_ops.types import (
|
||||||
ActionResult,
|
ActionResult,
|
||||||
|
ActionStatus,
|
||||||
BalanceInfo,
|
BalanceInfo,
|
||||||
ProviderActionType,
|
ProviderActionType,
|
||||||
)
|
)
|
||||||
@@ -53,9 +54,16 @@ class BalanceAction(ProviderAction):
|
|||||||
if checkin_result and result.data and hasattr(result.data, "extra"):
|
if checkin_result and result.data and hasattr(result.data, "extra"):
|
||||||
if result.data.extra is None:
|
if result.data.extra is None:
|
||||||
result.data.extra = {}
|
result.data.extra = {}
|
||||||
result.data.extra["checkin_success"] = checkin_result.get("success")
|
# 处理 cookie_expired 标记
|
||||||
result.data.extra["checkin_message"] = checkin_result.get("message", "")
|
if checkin_result.get("cookie_expired"):
|
||||||
logger.debug(f"签到结果已附加到 extra: {checkin_result}")
|
result.data.extra["cookie_expired"] = True
|
||||||
|
result.data.extra["cookie_expired_message"] = checkin_result.get("message", "")
|
||||||
|
result.status = ActionStatus.AUTH_EXPIRED
|
||||||
|
logger.warning(f"Cookie 已失效: {checkin_result}")
|
||||||
|
else:
|
||||||
|
result.data.extra["checkin_success"] = checkin_result.get("success")
|
||||||
|
result.data.extra["checkin_message"] = checkin_result.get("message", "")
|
||||||
|
logger.debug(f"签到结果已附加到 extra: {checkin_result}")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,8 @@ class NewApiBalanceAction(BalanceAction):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
签到结果字典,包含 success 和 message 字段;
|
签到结果字典,包含 success 和 message 字段;
|
||||||
如果功能未开放或认证失败,返回 None
|
如果功能未开放返回 None;
|
||||||
|
如果 Cookie 失效返回 {"cookie_expired": True}
|
||||||
"""
|
"""
|
||||||
site = client.base_url.host or str(client.base_url)
|
site = client.base_url.host or str(client.base_url)
|
||||||
checkin_endpoint = self.config.get("checkin_endpoint", "/api/user/checkin")
|
checkin_endpoint = self.config.get("checkin_endpoint", "/api/user/checkin")
|
||||||
@@ -139,10 +140,10 @@ class NewApiBalanceAction(BalanceAction):
|
|||||||
logger.debug(f"[{site}] 签到功能未开放")
|
logger.debug(f"[{site}] 签到功能未开放")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 401/403 表示签到需要额外认证(如 Cookie),当前配置不支持
|
# 401/403 表示 Cookie 已失效
|
||||||
if response.status_code in (401, 403):
|
if response.status_code in (401, 403):
|
||||||
logger.debug(f"[{site}] 签到需要额外认证")
|
logger.warning(f"[{site}] Cookie 已失效(签到返回 {response.status_code})")
|
||||||
return None
|
return {"cookie_expired": True, "message": "Cookie 已失效"}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = response.json()
|
data = response.json()
|
||||||
@@ -160,15 +161,15 @@ class NewApiBalanceAction(BalanceAction):
|
|||||||
logger.debug(f"[{site}] 今日已签到: {message}")
|
logger.debug(f"[{site}] 今日已签到: {message}")
|
||||||
return {"success": None, "message": message or "今日已签到"}
|
return {"success": None, "message": message or "今日已签到"}
|
||||||
|
|
||||||
# 检查是否是认证失败(未登录、无权限等)- 静默跳过
|
# 检查是否是认证失败(未登录、无权限等)- Cookie 已失效
|
||||||
auth_fail_indicators = [
|
auth_fail_indicators = [
|
||||||
"未登录", "请登录", "login", "unauthorized", "无权限", "权限不足",
|
"未登录", "请登录", "login", "unauthorized", "无权限", "权限不足",
|
||||||
"turnstile", "captcha", "验证码", # 需要人机验证
|
"turnstile", "captcha", "验证码", # 需要人机验证
|
||||||
]
|
]
|
||||||
is_auth_fail = any(ind in message.lower() for ind in auth_fail_indicators)
|
is_auth_fail = any(ind in message.lower() for ind in auth_fail_indicators)
|
||||||
if is_auth_fail:
|
if is_auth_fail:
|
||||||
logger.debug(f"[{site}] 签到需要额外认证,跳过")
|
logger.warning(f"[{site}] Cookie 已失效(签到认证失败): {message}")
|
||||||
return None
|
return {"cookie_expired": True, "message": message or "Cookie 已失效"}
|
||||||
|
|
||||||
# 其他失败情况
|
# 其他失败情况
|
||||||
logger.debug(f"[{site}] 签到失败: {message}")
|
logger.debug(f"[{site}] 签到失败: {message}")
|
||||||
|
|||||||
@@ -353,9 +353,12 @@ class ProviderOpsService:
|
|||||||
provider_id, ProviderActionType.QUERY_BALANCE, config
|
provider_id, ProviderActionType.QUERY_BALANCE, config
|
||||||
)
|
)
|
||||||
|
|
||||||
# 成功时更新缓存
|
# 成功或 auth_expired 时缓存(auth_expired 带有 cookie_expired 信息供前端显示警告)
|
||||||
if result.status == ActionStatus.SUCCESS and result.data:
|
if result.status in (ActionStatus.SUCCESS, ActionStatus.AUTH_EXPIRED) and result.data:
|
||||||
await self._cache_balance(provider_id, result)
|
await self._cache_balance(provider_id, result)
|
||||||
|
# auth_failed 时清除缓存(配置错误,用户修正后应立即重试)
|
||||||
|
elif result.status == ActionStatus.AUTH_FAILED:
|
||||||
|
await self._clear_balance_cache(provider_id)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -398,6 +401,12 @@ class ProviderOpsService:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"异步刷新余额失败: provider_id={provider_id}, error={e}")
|
logger.warning(f"异步刷新余额失败: provider_id={provider_id}, error={e}")
|
||||||
|
|
||||||
|
async def _clear_balance_cache(self, provider_id: str) -> None:
|
||||||
|
"""清除余额缓存(认证失败时调用)"""
|
||||||
|
cache_key = f"provider_ops:balance:{provider_id}"
|
||||||
|
await CacheService.delete(cache_key)
|
||||||
|
logger.info(f"余额缓存已清除: provider_id={provider_id}")
|
||||||
|
|
||||||
async def _cache_balance(self, provider_id: str, result: ActionResult) -> None:
|
async def _cache_balance(self, provider_id: str, result: ActionResult) -> None:
|
||||||
"""缓存余额结果"""
|
"""缓存余额结果"""
|
||||||
cache_key = f"provider_ops:balance:{provider_id}"
|
cache_key = f"provider_ops:balance:{provider_id}"
|
||||||
|
|||||||
Reference in New Issue
Block a user