feat: make subscription risk controls configurable

This commit is contained in:
JetSprow
2026-04-29 15:27:16 +10:00
parent 46ce257b0b
commit ff15606d92
8 changed files with 333 additions and 115 deletions

View File

@@ -36,6 +36,15 @@ export default async function AdminSettingsPage() {
reminderDispatchIntervalMinutes: config.reminderDispatchIntervalMinutes,
trafficSyncEnabled: config.trafficSyncEnabled,
trafficSyncIntervalSeconds: config.trafficSyncIntervalSeconds,
subscriptionRiskEnabled: config.subscriptionRiskEnabled,
subscriptionRiskAutoSuspend: config.subscriptionRiskAutoSuspend,
subscriptionRiskWindowHours: config.subscriptionRiskWindowHours,
subscriptionRiskCityWarning: config.subscriptionRiskCityWarning,
subscriptionRiskCitySuspend: config.subscriptionRiskCitySuspend,
subscriptionRiskRegionWarning: config.subscriptionRiskRegionWarning,
subscriptionRiskRegionSuspend: config.subscriptionRiskRegionSuspend,
subscriptionRiskIpLimitPerHour: config.subscriptionRiskIpLimitPerHour,
subscriptionRiskTokenLimitPerHour: config.subscriptionRiskTokenLimitPerHour,
inviteRewardEnabled: config.inviteRewardEnabled,
inviteRewardRate: Number(config.inviteRewardRate),
inviteRewardCouponId: config.inviteRewardCouponId,

View File

@@ -25,6 +25,15 @@ interface AppConfig {
reminderDispatchIntervalMinutes: number;
trafficSyncEnabled: boolean;
trafficSyncIntervalSeconds: number;
subscriptionRiskEnabled: boolean;
subscriptionRiskAutoSuspend: boolean;
subscriptionRiskWindowHours: number;
subscriptionRiskCityWarning: number;
subscriptionRiskCitySuspend: number;
subscriptionRiskRegionWarning: number;
subscriptionRiskRegionSuspend: number;
subscriptionRiskIpLimitPerHour: number;
subscriptionRiskTokenLimitPerHour: number;
inviteRewardEnabled: boolean;
inviteRewardRate: number;
inviteRewardCouponId: string | null;
@@ -196,6 +205,121 @@ export function SettingsForm({ config, coupons }: { config: AppConfig; coupons:
</div>
</section>
<section className="space-y-4 rounded-lg border border-border bg-muted/25 p-3">
<div className="flex items-center gap-2 text-sm font-semibold">
<ShieldAlert className="size-4 text-primary" /> 访
</div>
<p className="text-xs leading-5 text-muted-foreground">
访访
</p>
<div className="grid gap-5 md:grid-cols-3">
<div className="space-y-2">
<Label htmlFor="subscriptionRiskEnabled"></Label>
<select
id="subscriptionRiskEnabled"
name="subscriptionRiskEnabled"
defaultValue={String(config.subscriptionRiskEnabled)}
className={selectClassName}
>
<option value="true"></option>
<option value="false"></option>
</select>
</div>
<div className="space-y-2">
<Label htmlFor="subscriptionRiskAutoSuspend"></Label>
<select
id="subscriptionRiskAutoSuspend"
name="subscriptionRiskAutoSuspend"
defaultValue={String(config.subscriptionRiskAutoSuspend)}
className={selectClassName}
>
<option value="true"></option>
<option value="false"></option>
</select>
</div>
<div className="space-y-2">
<Label htmlFor="subscriptionRiskWindowHours"></Label>
<Input
id="subscriptionRiskWindowHours"
name="subscriptionRiskWindowHours"
type="number"
min={1}
max={168}
defaultValue={config.subscriptionRiskWindowHours}
/>
</div>
<div className="space-y-2">
<Label htmlFor="subscriptionRiskCityWarning"></Label>
<Input
id="subscriptionRiskCityWarning"
name="subscriptionRiskCityWarning"
type="number"
min={2}
max={100}
defaultValue={config.subscriptionRiskCityWarning}
/>
</div>
<div className="space-y-2">
<Label htmlFor="subscriptionRiskCitySuspend"></Label>
<Input
id="subscriptionRiskCitySuspend"
name="subscriptionRiskCitySuspend"
type="number"
min={2}
max={100}
defaultValue={config.subscriptionRiskCitySuspend}
/>
</div>
<div className="space-y-2">
<Label htmlFor="subscriptionRiskRegionWarning">/</Label>
<Input
id="subscriptionRiskRegionWarning"
name="subscriptionRiskRegionWarning"
type="number"
min={2}
max={100}
defaultValue={config.subscriptionRiskRegionWarning}
/>
</div>
<div className="space-y-2">
<Label htmlFor="subscriptionRiskRegionSuspend">/</Label>
<Input
id="subscriptionRiskRegionSuspend"
name="subscriptionRiskRegionSuspend"
type="number"
min={2}
max={100}
defaultValue={config.subscriptionRiskRegionSuspend}
/>
</div>
<div className="space-y-2">
<Label htmlFor="subscriptionRiskIpLimitPerHour">IP /</Label>
<Input
id="subscriptionRiskIpLimitPerHour"
name="subscriptionRiskIpLimitPerHour"
type="number"
min={1}
max={100000}
defaultValue={config.subscriptionRiskIpLimitPerHour}
/>
</div>
<div className="space-y-2">
<Label htmlFor="subscriptionRiskTokenLimitPerHour">/</Label>
<Input
id="subscriptionRiskTokenLimitPerHour"
name="subscriptionRiskTokenLimitPerHour"
type="number"
min={1}
max={100000}
defaultValue={config.subscriptionRiskTokenLimitPerHour}
/>
</div>
</div>
<p className="text-xs leading-5 text-muted-foreground">
24 4 5 2 /3 /IP 180 / 60 /
</p>
</section>
<section className="space-y-4 rounded-lg border border-border bg-muted/25 p-3">
<div className="flex items-center gap-2 text-sm font-semibold">
<ShieldCheck className="size-4 text-primary" />