mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-24 06:45:01 +03:00
2f62dadd25
One lost heartbeat on a lossy link flipped a node offline and sent node.down, and the next probe sent node.up, so a flaky node flooded Telegram, email and Discord with alert pairs. A new nodeDownThreshold setting (default 1, today's behaviour) works like outboundDownThreshold: node.down fires once the failed-check streak reaches it, once per outage, and node.up only after a node.down was sent. The announcement is persisted in nodes.down_notified, so a restart inside the threshold window neither drops the outage's node.down nor re-announces nodes already reported offline; nodes offline at upgrade are seeded as announced.
230 lines
6.4 KiB
TypeScript
230 lines
6.4 KiB
TypeScript
import { ObjectUtil } from '@/utils';
|
|
import type { SubProfileMode } from '@/schemas/setting';
|
|
|
|
export class AllSetting {
|
|
webListen = '';
|
|
webDomain = '';
|
|
webPort = 2053;
|
|
webCertFile = '';
|
|
webKeyFile = '';
|
|
webBasePath = '/';
|
|
sessionMaxAge = 360;
|
|
trustedProxyCIDRs = '127.0.0.1/32,::1/128';
|
|
realityScanCandidates =
|
|
'www.cloudflare.com:443,www.microsoft.com:443,www.amazon.com:443,aws.amazon.com:443,www.samsung.com:443,www.nvidia.com:443,www.amd.com:443,www.intel.com:443,www.sony.com:443,dl.google.com:443';
|
|
ipLimitAllowlist = '';
|
|
panelOutbound = '';
|
|
pageSize = 25;
|
|
expireDiff = 0;
|
|
trafficDiff = 0;
|
|
remarkTemplate = '{{INBOUND}}-{{EMAIL}}|📊{{TRAFFIC_LEFT}}|⏳{{DAYS_LEFT}}D';
|
|
subShowIdentityOnAllLinks = false;
|
|
subInfoNodeEnable = false;
|
|
subCalendarExpireInclusive = 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 = '';
|
|
happLinkEnable = false;
|
|
subEnable = true;
|
|
subJsonEnable = false;
|
|
subJsonAutoDetect = false;
|
|
subJsonAlwaysArray = false;
|
|
subJsonUserAgentRegex = '';
|
|
subClashAutoDetect = false;
|
|
subClashUserAgentRegex = '';
|
|
subTitle = '';
|
|
subSupportUrl = '';
|
|
subProfileMode: SubProfileMode = 'none';
|
|
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 = '';
|
|
externalSubUserAgent = 'v2rayNG/1.8.5';
|
|
subUpdates = 12;
|
|
subEncrypt = true;
|
|
subURI = '';
|
|
subJsonURI = '';
|
|
subClashURI = '';
|
|
subClashEnableRouting = false;
|
|
subClashRules = '';
|
|
subClashRemoteDNS = false;
|
|
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 = '';
|
|
subHappLocalProxyAuth = 'auto';
|
|
|
|
subIncyAppAutoDetect = false;
|
|
subIncyProfileDescription = '';
|
|
subIncySortOrder = '';
|
|
subIncySupportEmail = '';
|
|
subIncyAnnounceUrl = '';
|
|
subIncyPremiumUrl = '';
|
|
subIncyBannerText = '';
|
|
subIncyBannerButtonText = '';
|
|
subIncyBannerButtonUrl = '';
|
|
subIncyBannerBgColor = '';
|
|
subIncyBannerButtonColor = '';
|
|
subIncyHideUrl = '';
|
|
subIncyHideCheck = '';
|
|
subIncyNoLimitEnabled = '';
|
|
subIncyPerAppEnable = '';
|
|
subIncyPerAppMode = '';
|
|
subIncyPerAppList = '';
|
|
subIncyFragmentationEnable = '';
|
|
subIncyFragmentLength = '';
|
|
subIncyFragmentInterval = '';
|
|
subIncyFragmentPackets = '';
|
|
subIncyNoisesEnable = '';
|
|
subIncyNoisesType = '';
|
|
subIncyNoisesPacket = '';
|
|
subIncyNoisesDelay = '';
|
|
subIncyResolveEnable = '';
|
|
subIncyResolveDnsDomain = '';
|
|
subIncyResolveDnsIp = '';
|
|
|
|
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;
|
|
nodeDownThreshold = 1;
|
|
hasTgBotToken = false;
|
|
hasTwoFactorToken = false;
|
|
hasLdapPassword = false;
|
|
hasApiToken = false;
|
|
hasWarpSecret = false;
|
|
hasNordSecret = false;
|
|
hasSmtpPassword = false;
|
|
clearTgBotToken = false;
|
|
clearLdapPassword = false;
|
|
clearSmtpPassword = false;
|
|
discordBotEnable = false;
|
|
discordBotToken = '';
|
|
discordChannelId = '';
|
|
discordAdminIds = '';
|
|
discordRunTime = '@daily';
|
|
discordBotBackup = false;
|
|
discordCpu = 80;
|
|
discordMemory = 80;
|
|
discordLang = 'en-US';
|
|
discordEnabledEvents = 'login.attempt,cpu.high';
|
|
hasDiscordBotToken = false;
|
|
clearDiscordBotToken = false;
|
|
|
|
constructor(data?: unknown) {
|
|
if (data != null) {
|
|
ObjectUtil.cloneProps(this, data);
|
|
}
|
|
// Legacy settings with a custom URL retain it until an explicit mode is saved.
|
|
if (
|
|
typeof data === 'object' &&
|
|
data !== null &&
|
|
(!('subProfileMode' in data) || data.subProfileMode === undefined) &&
|
|
this.subProfileUrl.trim() !== ''
|
|
) {
|
|
this.subProfileMode = 'custom';
|
|
}
|
|
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;
|
|
const nodeThreshold = Math.round(Number(this.nodeDownThreshold));
|
|
this.nodeDownThreshold = Number.isFinite(nodeThreshold)
|
|
? Math.min(100, Math.max(1, nodeThreshold))
|
|
: 1;
|
|
}
|
|
|
|
equals(other: AllSetting): boolean {
|
|
return ObjectUtil.equals(this, other);
|
|
}
|
|
}
|