mirror of
https://github.com/JetSprow/J-Board-Lite.git
synced 2026-05-01 01:14:10 +05:30
feat: release v3.0.0 risk telemetry
This commit is contained in:
@@ -91,7 +91,7 @@ export function NodeActions({ node, siteUrl }: { node: NodeActionValue; siteUrl:
|
||||
size="sm"
|
||||
variant="outline"
|
||||
title="撤销这个探测 Token?"
|
||||
description="撤销后,延迟和线路探测程序将无法继续上报数据。"
|
||||
description="撤销后,延迟、线路探测和节点日志风控程序将无法继续上报数据。"
|
||||
confirmLabel="撤销 Token"
|
||||
successMessage="探测 Token 已撤销"
|
||||
errorMessage="撤销失败"
|
||||
@@ -170,7 +170,7 @@ export function NodeActions({ node, siteUrl }: { node: NodeActionValue; siteUrl:
|
||||
</p>
|
||||
)}
|
||||
<p className="text-xs leading-5 text-muted-foreground">
|
||||
此 Agent 仅用于 `/api/agent/latency` 和 `/api/agent/trace` 探测上报;节点客户端开通已改由 3x-ui 面板 API 处理。
|
||||
此 Agent 用于 `/api/agent/latency`、`/api/agent/trace` 探测上报;安装脚本会自动查找 3x-ui/Xray access log,找到后启用节点日志风控。Agent 只读日志,不修改 3x-ui 配置。
|
||||
</p>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
||||
@@ -48,6 +48,11 @@ export default async function AdminSettingsPage() {
|
||||
subscriptionRiskCountrySuspend: config.subscriptionRiskCountrySuspend,
|
||||
subscriptionRiskIpLimitPerHour: config.subscriptionRiskIpLimitPerHour,
|
||||
subscriptionRiskTokenLimitPerHour: config.subscriptionRiskTokenLimitPerHour,
|
||||
nodeAccessRiskEnabled: config.nodeAccessRiskEnabled,
|
||||
nodeAccessConnectionWarning: config.nodeAccessConnectionWarning,
|
||||
nodeAccessConnectionSuspend: config.nodeAccessConnectionSuspend,
|
||||
nodeAccessUniqueTargetWarning: config.nodeAccessUniqueTargetWarning,
|
||||
nodeAccessUniqueTargetSuspend: config.nodeAccessUniqueTargetSuspend,
|
||||
inviteRewardEnabled: config.inviteRewardEnabled,
|
||||
inviteRewardRate: Number(config.inviteRewardRate),
|
||||
inviteRewardCouponId: config.inviteRewardCouponId,
|
||||
|
||||
@@ -37,6 +37,11 @@ interface AppConfig {
|
||||
subscriptionRiskCountrySuspend: number;
|
||||
subscriptionRiskIpLimitPerHour: number;
|
||||
subscriptionRiskTokenLimitPerHour: number;
|
||||
nodeAccessRiskEnabled: boolean;
|
||||
nodeAccessConnectionWarning: number;
|
||||
nodeAccessConnectionSuspend: number;
|
||||
nodeAccessUniqueTargetWarning: number;
|
||||
nodeAccessUniqueTargetSuspend: number;
|
||||
inviteRewardEnabled: boolean;
|
||||
inviteRewardRate: number;
|
||||
inviteRewardCouponId: string | null;
|
||||
@@ -389,9 +394,37 @@ export function SettingsForm({ config, coupons }: { config: AppConfig; coupons:
|
||||
defaultValue={config.subscriptionRiskTokenLimitPerHour}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="nodeAccessRiskEnabled">节点日志风控</Label>
|
||||
<select
|
||||
id="nodeAccessRiskEnabled"
|
||||
name="nodeAccessRiskEnabled"
|
||||
defaultValue={String(config.nodeAccessRiskEnabled)}
|
||||
className={selectClassName}
|
||||
>
|
||||
<option value="true">开启,接收 Agent Xray 日志上报</option>
|
||||
<option value="false">关闭,只保留订阅接口风控</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="nodeAccessConnectionWarning">节点连接警告阈值</Label>
|
||||
<Input id="nodeAccessConnectionWarning" name="nodeAccessConnectionWarning" type="number" min={1} max={100000} defaultValue={config.nodeAccessConnectionWarning} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="nodeAccessConnectionSuspend">节点连接暂停阈值</Label>
|
||||
<Input id="nodeAccessConnectionSuspend" name="nodeAccessConnectionSuspend" type="number" min={1} max={100000} defaultValue={config.nodeAccessConnectionSuspend} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="nodeAccessUniqueTargetWarning">不同目标警告阈值</Label>
|
||||
<Input id="nodeAccessUniqueTargetWarning" name="nodeAccessUniqueTargetWarning" type="number" min={1} max={100000} defaultValue={config.nodeAccessUniqueTargetWarning} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="nodeAccessUniqueTargetSuspend">不同目标暂停阈值</Label>
|
||||
<Input id="nodeAccessUniqueTargetSuspend" name="nodeAccessUniqueTargetSuspend" type="number" min={1} max={100000} defaultValue={config.nodeAccessUniqueTargetSuspend} />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs leading-5 text-muted-foreground">
|
||||
默认值对应原规则:24 小时内 4 城市警告、5 城市暂停;2 省/地区警告、3 省/地区暂停;2 国家警告、3 国家暂停;IP 180 次/小时,订阅 60 次/小时。
|
||||
默认值对应原规则:24 小时内 4 城市警告、5 城市暂停;2 省/地区警告、3 省/地区暂停;2 国家警告、3 国家暂停;IP 180 次/小时,订阅 60 次/小时。节点日志风控只在 Agent 配置 XRAY_ACCESS_LOG_PATH 后生效;连接数和不同目标数按 Agent 单次聚合窗口计算。
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -44,7 +44,7 @@ function WorldRiskMap({ summary }: { summary: SubscriptionRiskGeoSummary }) {
|
||||
viewBox="0 0 360 180"
|
||||
className="h-[15rem] w-full bg-[radial-gradient(circle_at_30%_20%,color-mix(in_oklch,var(--primary)_10%,transparent),transparent_30%),linear-gradient(135deg,var(--muted),var(--card))]"
|
||||
role="img"
|
||||
aria-label="订阅访问 IP 世界地图分布"
|
||||
aria-label="订阅访问与节点连接 IP 世界地图分布"
|
||||
>
|
||||
<rect width="360" height="180" rx="12" fill="transparent" />
|
||||
{[-120, -60, 0, 60, 120].map((longitude) => {
|
||||
@@ -164,7 +164,7 @@ export function SubscriptionRiskGeoDetails({ summary }: { summary: SubscriptionR
|
||||
<details className="group rounded-xl border border-border/70 bg-muted/20">
|
||||
<summary className="flex cursor-pointer list-none items-center justify-between gap-3 px-3 py-2 text-sm font-medium [&::-webkit-details-marker]:hidden">
|
||||
<span className="flex items-center gap-2">
|
||||
<MapPin className="size-4 text-primary" /> IP 访问明细
|
||||
<MapPin className="size-4 text-primary" /> IP 访问/连接明细
|
||||
</span>
|
||||
<ChevronDown className="size-4 text-muted-foreground transition-transform group-open:rotate-180" />
|
||||
</summary>
|
||||
|
||||
@@ -31,6 +31,14 @@ function reasonLabel(reason: SubscriptionRiskEvent["reason"]) {
|
||||
return "国家异常警告";
|
||||
case "COUNTRY_VARIANCE_SUSPEND":
|
||||
return "国家异常暂停";
|
||||
case "NODE_ACCESS_VOLUME_WARNING":
|
||||
return "节点高频警告";
|
||||
case "NODE_ACCESS_VOLUME_SUSPEND":
|
||||
return "节点高频暂停";
|
||||
case "NODE_ACCESS_TARGET_WARNING":
|
||||
return "目标分散警告";
|
||||
case "NODE_ACCESS_TARGET_SUSPEND":
|
||||
return "目标分散暂停";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +263,7 @@ export function SubscriptionRiskTable({ events }: { events: SubscriptionRiskEven
|
||||
return (
|
||||
<EmptyState
|
||||
title="暂无订阅风控事件"
|
||||
description="订阅链接出现跨城市、跨省份或跨国家访问异常后,会在这里进入人工跟进队列。"
|
||||
description="订阅链接或节点真实连接出现跨城市、跨省份或跨国家异常后,会在这里进入人工跟进队列。"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getSubscriptionRiskEvents } from "./risk-data";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "订阅风控",
|
||||
description: "查看订阅访问异常、关联用户和人工处理状态。",
|
||||
description: "查看订阅访问与节点连接异常、关联用户和人工处理状态。",
|
||||
};
|
||||
|
||||
export default async function AdminSubscriptionRiskPage({
|
||||
@@ -22,7 +22,7 @@ export default async function AdminSubscriptionRiskPage({
|
||||
<PageHeader
|
||||
eyebrow="商品与订单"
|
||||
title="订阅风控"
|
||||
description="订阅链接跨城市或跨省份访问异常后,会进入这里供管理员确认、备注、恢复或继续处置。"
|
||||
description="订阅链接或节点真实连接出现跨城市、跨省份访问异常后,会进入这里供管理员确认、备注、恢复或继续处置。"
|
||||
/>
|
||||
|
||||
<AdminFilterBar
|
||||
|
||||
@@ -67,6 +67,14 @@ function reasonLabel(reason: SubscriptionRiskEvent["reason"]) {
|
||||
return "国家异常警告";
|
||||
case "COUNTRY_VARIANCE_SUSPEND":
|
||||
return "国家异常暂停";
|
||||
case "NODE_ACCESS_VOLUME_WARNING":
|
||||
return "节点高频警告";
|
||||
case "NODE_ACCESS_VOLUME_SUSPEND":
|
||||
return "节点高频暂停";
|
||||
case "NODE_ACCESS_TARGET_WARNING":
|
||||
return "目标分散警告";
|
||||
case "NODE_ACCESS_TARGET_SUSPEND":
|
||||
return "目标分散暂停";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +121,7 @@ export function SubscriptionAccessRiskSection({
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold tracking-[-0.02em]">订阅访问风控</h3>
|
||||
<p className="mt-0.5 text-sm text-muted-foreground">记录订阅拉取 IP、地区变化和人工处理状态。</p>
|
||||
<p className="mt-0.5 text-sm text-muted-foreground">记录订阅拉取与节点真实连接 IP、地区变化和人工处理状态。</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/admin/subscription-risk" className={buttonVariants({ variant: "outline", size: "sm" })}>
|
||||
|
||||
Reference in New Issue
Block a user