feat: add manual and automatic WARP IP rotation (#5099)

* feat: add manual and automatic WARP IP rotation

* fix: update generated api and frontend schemas

* fix(warp): validate rotation interval, fix auto-update timing, sync editor

- Validate the auto-update interval as an integer and store it via setInt;
  a non-integer value previously broke GetAllSetting for the whole panel.
- Seed warpLastUpdate when the interval is saved and when changing IP
  manually, so auto-update counts from "now" instead of epoch 0 and a
  manual rotation doesn't trigger an immediate scheduled one.
- Guard WarpIpJob: when lastUpdate is unset, establish a baseline and skip
  instead of rotating on the next tick.
- Log WARP license re-apply failures instead of swallowing them.
- After a manual "Change IP", sync the in-memory Xray editor with the keys
  the backend persisted so a later template save can't revert them; only
  toast success when the interval save actually succeeds.
- Add the WARP rotation UI strings to all 13 locales.
- Drop trailing whitespace introduced in entity.go and xray_setting.go.

---------

Co-authored-by: Rqzbeh <Rqzbeh@example.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
Rouzbeh†
2026-06-09 01:43:43 +02:00
committed by GitHub
parent be8bd4e22c
commit d9ccf157c3
27 changed files with 436 additions and 28 deletions
+2
View File
@@ -75,6 +75,7 @@ export const EXAMPLES: Record<string, unknown> = {
"trustedProxyCIDRs": "",
"twoFactorEnable": false,
"twoFactorToken": "",
"warpUpdateInterval": 0,
"webBasePath": "",
"webCertFile": "",
"webDomain": "",
@@ -163,6 +164,7 @@ export const EXAMPLES: Record<string, unknown> = {
"trustedProxyCIDRs": "",
"twoFactorEnable": false,
"twoFactorToken": "",
"warpUpdateInterval": 0,
"webBasePath": "",
"webCertFile": "",
"webDomain": "",
+12
View File
@@ -299,6 +299,11 @@ export const SCHEMAS: Record<string, unknown> = {
"description": "Two-factor authentication token",
"type": "string"
},
"warpUpdateInterval": {
"description": "WARP",
"minimum": 0,
"type": "integer"
},
"webBasePath": {
"description": "Base path for web panel URLs",
"type": "string"
@@ -401,6 +406,7 @@ export const SCHEMAS: Record<string, unknown> = {
"trustedProxyCIDRs",
"twoFactorEnable",
"twoFactorToken",
"warpUpdateInterval",
"webBasePath",
"webCertFile",
"webDomain",
@@ -727,6 +733,11 @@ export const SCHEMAS: Record<string, unknown> = {
"description": "Two-factor authentication token",
"type": "string"
},
"warpUpdateInterval": {
"description": "WARP",
"minimum": 0,
"type": "integer"
},
"webBasePath": {
"description": "Base path for web panel URLs",
"type": "string"
@@ -835,6 +846,7 @@ export const SCHEMAS: Record<string, unknown> = {
"trustedProxyCIDRs",
"twoFactorEnable",
"twoFactorToken",
"warpUpdateInterval",
"webBasePath",
"webCertFile",
"webDomain",
+2
View File
@@ -80,6 +80,7 @@ export interface AllSetting {
trustedProxyCIDRs: string;
twoFactorEnable: boolean;
twoFactorToken: string;
warpUpdateInterval: number;
webBasePath: string;
webCertFile: string;
webDomain: string;
@@ -169,6 +170,7 @@ export interface AllSettingView {
trustedProxyCIDRs: string;
twoFactorEnable: boolean;
twoFactorToken: string;
warpUpdateInterval: number;
webBasePath: string;
webCertFile: string;
webDomain: string;
+2
View File
@@ -90,6 +90,7 @@ export const AllSettingSchema = z.object({
trustedProxyCIDRs: z.string(),
twoFactorEnable: z.boolean(),
twoFactorToken: z.string(),
warpUpdateInterval: z.number().int().min(0),
webBasePath: z.string(),
webCertFile: z.string(),
webDomain: z.string(),
@@ -180,6 +181,7 @@ export const AllSettingViewSchema = z.object({
trustedProxyCIDRs: z.string(),
twoFactorEnable: z.boolean(),
twoFactorToken: z.string(),
warpUpdateInterval: z.number().int().min(0),
webBasePath: z.string(),
webCertFile: z.string(),
webDomain: z.string(),