Files
3x-ui/frontend/src/models/setting.ts
T
DIMFLIX 2730e4d071 feat(sub): let the panel set the JSON subscription DNS servers (#6485)
* feat(sub): let the panel set the JSON subscription DNS servers

A baked routing profile (#6402) carries only the DNS its preset defines, so an
operator who wants their own resolvers has to override the whole profile or
patch the subscription behind a proxy.

Add the subJsonDns setting: either a full xray dns block or a bare array of
servers. It wins over the profile's DNS while leaving the profile's routing
rules intact, and reaches per-inbound, balancer and info-node documents alike.

The value is validated with xray's own schema (internal/xray/dnsconf): a block
the client could not load is rejected when the settings are saved and ignored
with a warning at request time, instead of being baked into every document.
Both the sub server and the settings API share that validator, so a stored
value can never be silently dropped.

xray's Build() is deliberately not used for validation: it resolves geosite
tokens from the geodata files and would reject valid configs whenever those
are absent from the panel's working directory.

* style(dnsconf): drop the ineffectual initial map assignment

golangci's ineffassign flagged the zero-value map whose value both paths
overwrite: the object branch now assigns the decoded map directly.

* docs(sub): scope the DNS setting to the documents it rewrites

The Routing header mirrored to Happ/INCY keeps the routing profile's own
resolvers, so the setting description and the header-source comment now say
so instead of claiming the profile's DNS is replaced everywhere.

Also trims two comments in the new dnsconf package to the repo's two-line cap.
2026-09-13 11:51:56 +02:00

166 lines
4.3 KiB
TypeScript

import { ObjectUtil } from '@/utils';
export class AllSetting {
webListen = '';
webDomain = '';
webPort = 2053;
webCertFile = '';
webKeyFile = '';
webBasePath = '/';
sessionMaxAge = 360;
trustedProxyCIDRs = '127.0.0.1/32,::1/128';
ipLimitAllowlist = '';
panelOutbound = '';
pageSize = 25;
expireDiff = 0;
trafficDiff = 0;
remarkTemplate = '{{INBOUND}}-{{EMAIL}}|📊{{TRAFFIC_LEFT}}|⏳{{DAYS_LEFT}}D';
subShowIdentityOnAllLinks = false;
subInfoNodeEnable = false;
subExpiredTemplate = '⛔ {{EMAIL}} | Expired: {{EXPIRE_DATE}}';
subTrafficDepletedTemplate =
'🚫 {{EMAIL}} | Traffic Depleted | {{TRAFFIC_USED}}/{{TRAFFIC_TOTAL}}';
datepicker: 'gregorian' | 'jalalian' = 'gregorian';
tgBotEnable = false;
tgBotToken = '';
tgBotAPIServer = '';
tgBotChatId = '';
tgRunTime = '@daily';
tgBotBackup = false;
tgCpu = 80;
tgMemory = 80;
tgLang = 'en-US';
twoFactorEnable = false;
twoFactorToken = '';
xrayTemplateConfig = '';
subEnable = true;
subJsonEnable = false;
subJsonAutoDetect = false;
subJsonAlwaysArray = false;
subJsonUserAgentRegex = '';
subClashAutoDetect = false;
subClashUserAgentRegex = '';
subTitle = '';
subSupportUrl = '';
subProfileUrl = '';
subAnnounce = '';
subEnableRouting = false;
subRoutingRules = '';
subIncyEnableRouting = false;
subIncyRoutingRules = '';
subListen = '';
subPort = 2096;
subPath = '/sub/';
subJsonPath = '/json/';
subClashEnable = false;
subClashPath = '/clash/';
subDomain = '';
externalTrafficInformEnable = false;
externalTrafficInformURI = '';
restartXrayOnClientDisable = true;
subCertFile = '';
subKeyFile = '';
subUpdates = 12;
subEncrypt = true;
subURI = '';
subJsonURI = '';
subClashURI = '';
subClashEnableRouting = false;
subClashRules = '';
subJsonMux = '';
subJsonRules = '';
subJsonRoutingRules = '';
subJsonDns = '';
subJsonFinalMask = '';
subJsonObservatory = '';
subThemeDir = '';
subHideSettings = false;
subHappAutoDetect = false;
subHappProviderId = '';
subHappNewUrl = '';
subHappFallbackUrl = '';
subHappSubInfoColor = 'blue';
subHappSubInfoText = '';
subHappSubInfoButtonText = '';
subHappSubInfoButtonLink = '';
subHappSubExpire = false;
subHappSubExpireButtonLink = '';
subHappNotificationExpire = false;
subHappNoLimit = false;
subHappAlwaysHwid = false;
subHappTunMode = '';
subHappTunType = '';
subHappExcludeRoutes = '';
subHappExcludeApns = false;
subHappColorProfile = '';
subHappPingType = '';
subHappAutoConnect = false;
subHappAutoConnectType = 'lowestdelay';
subHappPerAppMode = 'off';
subHappPerAppList = '';
timeLocation = 'Local';
ldapEnable = false;
ldapHost = '';
ldapPort = 389;
ldapUseTLS = false;
ldapInsecureSkipVerify = false;
ldapBindDN = '';
ldapPassword = '';
ldapBaseDN = '';
ldapUserFilter = '(objectClass=person)';
ldapUserAttr = 'mail';
ldapVlessField = 'vless_enabled';
ldapSyncCron = '@every 1m';
ldapFlagField = '';
ldapTruthyValues = 'true,1,yes,on';
ldapInvertFlag = false;
ldapInboundTags = '';
ldapAutoCreate = false;
ldapAutoDelete = false;
ldapDefaultTotalGB = 0;
ldapDefaultExpiryDays = 0;
ldapDefaultLimitIP = 0;
tgEnabledEvents = 'login.attempt,cpu.high';
smtpEnable = false;
smtpHost = '';
smtpPort = 587;
smtpUsername = '';
smtpPassword = '';
smtpFrom = '';
smtpFromName = '';
smtpTo = '';
smtpEncryptionType = 'starttls';
smtpEnabledEvents = 'login.attempt,cpu.high';
smtpCpu = 80;
smtpMemory = 80;
outboundDownThreshold = 3;
hasTgBotToken = false;
hasTwoFactorToken = false;
hasLdapPassword = false;
hasApiToken = false;
hasWarpSecret = false;
hasNordSecret = false;
hasSmtpPassword = false;
clearTgBotToken = false;
clearLdapPassword = false;
clearSmtpPassword = false;
constructor(data?: unknown) {
if (data != null) {
ObjectUtil.cloneProps(this, data);
}
const cpu = Math.round(Number(this.tgCpu));
this.tgCpu = Number.isFinite(cpu) ? Math.min(100, Math.max(0, cpu)) : 80;
const threshold = Math.round(Number(this.outboundDownThreshold));
this.outboundDownThreshold = Number.isFinite(threshold)
? Math.min(100, Math.max(1, threshold))
: 3;
}
equals(other: AllSetting): boolean {
return ObjectUtil.equals(this, other);
}
}