mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-20 10:00:58 +00:00
5c7ca5b579
* feat(clients): give each client its own traffic reset cycle Traffic reset is configured on the inbound, so every client sharing an inbound resets together. An operator running a monthly 1000GB plan and a weekly 200GB plan side by side has to press Reset Traffic by hand. Clients now carry the same trafficReset / trafficResetDay pair the inbound already has, with the same vocabulary and the same monthly due-day rule, and PeriodicTrafficResetJob makes a second pass over the clients whose own cycle matches the period it is running for. A client that leaves the field at never behaves exactly as before: only its inbound's schedule can reset it. The fields live on ClientRecord as well as in the inbound settings JSON, so an ordinary edit does not write the cycle back as empty, and an unknown period is rejected rather than coerced, since a coerced value would read as configured while no job would ever select the client. Cron expressions and a custom post-reset quota from the issue are left out: both are separate decisions, and neither has an inbound-level counterpart to stay consistent with. * fix(clients): make the per-client reset cycle editable and safe to run Review found three things wrong with the first cut, one of them mine and worse than the bug it replaced. The cycle could only be set at creation. ClientService.Update writes the columns directly only for a client with no inbounds; the normal path goes through SyncInbound and applyClientRecordMerge, which this change had not extended, so an edit updated the settings JSON while the clients column kept the old value and the job kept applying the old cycle. The earlier test passed because it asserted the value survived an unrelated edit, which it did precisely because nothing ever wrote it. Replaced with a test that changes the cycle and switches it off again. Avoiding the re-enable that ResetTrafficByEmail performs was wrong. Depletion disables clients.enable and the settings JSON as well as client_traffics.enable, so lifting only the quota gate left a depleted client out of the generated config with zeroed counters, which no longer match the depleted predicate: locked out permanently. The rule is now about cause, not state — a client the quota switched off is restored, one disabled below its quota was switched off by hand and is skipped. The bulk path also bypassed node propagation and the MTProto sidecar quota that ResetTrafficByEmail handles, so it silently did nothing on node-backed inbounds. Dropped in favour of the integrated path, whose needRestart is now collected and turned into a single SetToNeedRestart. Also adds the AutoMigrate NULL backfill, guards the merge so a stale node snapshot cannot erase a configured cycle, validates the bulk-create and import paths, normalizes the day the way the inbound path does, marks the fields omitempty so existing clients match the published contract, and shares one TRAFFIC_RESETS tuple between the three forms. * fix(clients): validate renew fields on the bulk and import paths too BulkCreate and ImportClients insert client records without going through Create, so the resetDay/resetMax checks added with the calendar renewal (#6239) and the renew cap (#6238) never ran there. An API caller could store resetDay 45 or a negative resetMax, values the renewal query then mishandles silently. Mirror Create's validation on both batch paths, next to the trafficReset check they already carry. --------- Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
754 lines
23 KiB
TypeScript
754 lines
23 KiB
TypeScript
// Code generated by tools/openapigen. DO NOT EDIT.
|
|
import { z } from 'zod';
|
|
export const GeoKindSchema = z.string();
|
|
export type GeoKind = z.infer<typeof GeoKindSchema>;
|
|
|
|
export const OnlineAPISupportSchema = z.number().int();
|
|
export type OnlineAPISupport = z.infer<typeof OnlineAPISupportSchema>;
|
|
|
|
export const ProcessStateSchema = z.string();
|
|
export type ProcessState = z.infer<typeof ProcessStateSchema>;
|
|
|
|
export const ProtocolSchema = z.string();
|
|
export type Protocol = z.infer<typeof ProtocolSchema>;
|
|
|
|
export const SubLinkProviderSchema = z.unknown();
|
|
export type SubLinkProvider = z.infer<typeof SubLinkProviderSchema>;
|
|
|
|
export const staticEgressResolverSchema = z.string();
|
|
export type staticEgressResolver = z.infer<typeof staticEgressResolverSchema>;
|
|
|
|
export const trafficLocalApplyActionSchema = z.number().int();
|
|
export type trafficLocalApplyAction = z.infer<typeof trafficLocalApplyActionSchema>;
|
|
|
|
export const transportBitsSchema = z.number().int();
|
|
export type transportBits = z.infer<typeof transportBitsSchema>;
|
|
|
|
export const AllSettingSchema = z.object({
|
|
datepicker: z.string(),
|
|
expireDiff: z.number().int().min(0),
|
|
externalTrafficInformEnable: z.boolean(),
|
|
externalTrafficInformURI: z.string(),
|
|
ipLimitAllowlist: z.string(),
|
|
ldapAutoCreate: z.boolean(),
|
|
ldapAutoDelete: z.boolean(),
|
|
ldapBaseDN: z.string(),
|
|
ldapBindDN: z.string(),
|
|
ldapDefaultExpiryDays: z.number().int().min(0),
|
|
ldapDefaultLimitIP: z.number().int().min(0),
|
|
ldapDefaultTotalGB: z.number().int().min(0),
|
|
ldapEnable: z.boolean(),
|
|
ldapFlagField: z.string(),
|
|
ldapHost: z.string(),
|
|
ldapInboundTags: z.string(),
|
|
ldapInsecureSkipVerify: z.boolean(),
|
|
ldapInvertFlag: z.boolean(),
|
|
ldapPassword: z.string(),
|
|
ldapPort: z.number().int().min(0).max(65535),
|
|
ldapSyncCron: z.string(),
|
|
ldapTruthyValues: z.string(),
|
|
ldapUseTLS: z.boolean(),
|
|
ldapUserAttr: z.string(),
|
|
ldapUserFilter: z.string(),
|
|
ldapVlessField: z.string(),
|
|
outboundDownThreshold: z.number().int().min(1).max(100),
|
|
pageSize: z.number().int().min(0).max(1000),
|
|
panelOutbound: z.string(),
|
|
remarkTemplate: z.string(),
|
|
restartXrayOnClientDisable: z.boolean(),
|
|
sessionMaxAge: z.number().int().min(1).max(525600),
|
|
smtpCpu: z.number().int().min(0).max(100),
|
|
smtpEnable: z.boolean(),
|
|
smtpEnabledEvents: z.string(),
|
|
smtpEncryptionType: z.string(),
|
|
smtpFrom: z.string(),
|
|
smtpFromName: z.string(),
|
|
smtpHost: z.string(),
|
|
smtpMemory: z.number().int().min(0).max(100),
|
|
smtpPassword: z.string(),
|
|
smtpPort: z.number().int().min(1).max(65535),
|
|
smtpTo: z.string(),
|
|
smtpUsername: z.string(),
|
|
subAnnounce: z.string(),
|
|
subCertFile: z.string(),
|
|
subClashAutoDetect: z.boolean(),
|
|
subClashEnable: z.boolean(),
|
|
subClashEnableRouting: z.boolean(),
|
|
subClashPath: z.string(),
|
|
subClashRules: z.string(),
|
|
subClashURI: z.string(),
|
|
subClashUserAgentRegex: z.string(),
|
|
subDomain: z.string(),
|
|
subEnable: z.boolean(),
|
|
subEnableRouting: z.boolean(),
|
|
subEncrypt: z.boolean(),
|
|
subHideSettings: z.boolean(),
|
|
subIncyEnableRouting: z.boolean(),
|
|
subIncyRoutingRules: z.string(),
|
|
subJsonAlwaysArray: z.boolean(),
|
|
subJsonAutoDetect: z.boolean(),
|
|
subJsonEnable: z.boolean(),
|
|
subJsonFinalMask: z.string(),
|
|
subJsonMux: z.string(),
|
|
subJsonPath: z.string(),
|
|
subJsonRules: z.string(),
|
|
subJsonURI: z.string(),
|
|
subJsonUserAgentRegex: z.string(),
|
|
subKeyFile: z.string(),
|
|
subListen: z.string(),
|
|
subPath: z.string(),
|
|
subPort: z.number().int().min(1).max(65535),
|
|
subProfileUrl: z.string(),
|
|
subRoutingRules: z.string(),
|
|
subShowIdentityOnAllLinks: z.boolean(),
|
|
subSupportUrl: z.string(),
|
|
subThemeDir: z.string(),
|
|
subTitle: z.string(),
|
|
subURI: z.string(),
|
|
subUpdates: z.number().int().min(0).max(525600),
|
|
tgBotAPIServer: z.string(),
|
|
tgBotBackup: z.boolean(),
|
|
tgBotChatId: z.string(),
|
|
tgBotEnable: z.boolean(),
|
|
tgBotProxy: z.string(),
|
|
tgBotToken: z.string(),
|
|
tgCpu: z.number().int().min(0).max(100),
|
|
tgEnabledEvents: z.string(),
|
|
tgLang: z.string(),
|
|
tgMemory: z.number().int().min(0).max(100),
|
|
tgRunTime: z.string(),
|
|
timeLocation: z.string(),
|
|
trafficDiff: z.number().int().min(0).max(100),
|
|
trustedProxyCIDRs: z.string(),
|
|
twoFactorEnable: z.boolean(),
|
|
twoFactorToken: z.string(),
|
|
warpUpdateInterval: z.number().int().min(0),
|
|
webBasePath: z.string(),
|
|
webCertFile: z.string(),
|
|
webDomain: z.string(),
|
|
webKeyFile: z.string(),
|
|
webListen: z.string(),
|
|
webPort: z.number().int().min(1).max(65535),
|
|
});
|
|
export type AllSetting = z.infer<typeof AllSettingSchema>;
|
|
|
|
export const AllSettingViewSchema = z.object({
|
|
datepicker: z.string(),
|
|
expireDiff: z.number().int().min(0),
|
|
externalTrafficInformEnable: z.boolean(),
|
|
externalTrafficInformURI: z.string(),
|
|
hasApiToken: z.boolean(),
|
|
hasLdapPassword: z.boolean(),
|
|
hasNordSecret: z.boolean(),
|
|
hasSmtpPassword: z.boolean(),
|
|
hasTgBotToken: z.boolean(),
|
|
hasTwoFactorToken: z.boolean(),
|
|
hasWarpSecret: z.boolean(),
|
|
ipLimitAllowlist: z.string(),
|
|
ldapAutoCreate: z.boolean(),
|
|
ldapAutoDelete: z.boolean(),
|
|
ldapBaseDN: z.string(),
|
|
ldapBindDN: z.string(),
|
|
ldapDefaultExpiryDays: z.number().int().min(0),
|
|
ldapDefaultLimitIP: z.number().int().min(0),
|
|
ldapDefaultTotalGB: z.number().int().min(0),
|
|
ldapEnable: z.boolean(),
|
|
ldapFlagField: z.string(),
|
|
ldapHost: z.string(),
|
|
ldapInboundTags: z.string(),
|
|
ldapInsecureSkipVerify: z.boolean(),
|
|
ldapInvertFlag: z.boolean(),
|
|
ldapPassword: z.string(),
|
|
ldapPort: z.number().int().min(0).max(65535),
|
|
ldapSyncCron: z.string(),
|
|
ldapTruthyValues: z.string(),
|
|
ldapUseTLS: z.boolean(),
|
|
ldapUserAttr: z.string(),
|
|
ldapUserFilter: z.string(),
|
|
ldapVlessField: z.string(),
|
|
outboundDownThreshold: z.number().int().min(1).max(100),
|
|
pageSize: z.number().int().min(0).max(1000),
|
|
panelOutbound: z.string(),
|
|
remarkTemplate: z.string(),
|
|
restartXrayOnClientDisable: z.boolean(),
|
|
sessionMaxAge: z.number().int().min(1).max(525600),
|
|
smtpCpu: z.number().int().min(0).max(100),
|
|
smtpEnable: z.boolean(),
|
|
smtpEnabledEvents: z.string(),
|
|
smtpEncryptionType: z.string(),
|
|
smtpFrom: z.string(),
|
|
smtpFromName: z.string(),
|
|
smtpHost: z.string(),
|
|
smtpMemory: z.number().int().min(0).max(100),
|
|
smtpPassword: z.string(),
|
|
smtpPort: z.number().int().min(1).max(65535),
|
|
smtpTo: z.string(),
|
|
smtpUsername: z.string(),
|
|
subAnnounce: z.string(),
|
|
subCertFile: z.string(),
|
|
subClashAutoDetect: z.boolean(),
|
|
subClashEnable: z.boolean(),
|
|
subClashEnableRouting: z.boolean(),
|
|
subClashPath: z.string(),
|
|
subClashRules: z.string(),
|
|
subClashURI: z.string(),
|
|
subClashUserAgentRegex: z.string(),
|
|
subDomain: z.string(),
|
|
subEnable: z.boolean(),
|
|
subEnableRouting: z.boolean(),
|
|
subEncrypt: z.boolean(),
|
|
subHideSettings: z.boolean(),
|
|
subIncyEnableRouting: z.boolean(),
|
|
subIncyRoutingRules: z.string(),
|
|
subJsonAlwaysArray: z.boolean(),
|
|
subJsonAutoDetect: z.boolean(),
|
|
subJsonEnable: z.boolean(),
|
|
subJsonFinalMask: z.string(),
|
|
subJsonMux: z.string(),
|
|
subJsonPath: z.string(),
|
|
subJsonRules: z.string(),
|
|
subJsonURI: z.string(),
|
|
subJsonUserAgentRegex: z.string(),
|
|
subKeyFile: z.string(),
|
|
subListen: z.string(),
|
|
subPath: z.string(),
|
|
subPort: z.number().int().min(1).max(65535),
|
|
subProfileUrl: z.string(),
|
|
subRoutingRules: z.string(),
|
|
subShowIdentityOnAllLinks: z.boolean(),
|
|
subSupportUrl: z.string(),
|
|
subThemeDir: z.string(),
|
|
subTitle: z.string(),
|
|
subURI: z.string(),
|
|
subUpdates: z.number().int().min(0).max(525600),
|
|
tgBotAPIServer: z.string(),
|
|
tgBotBackup: z.boolean(),
|
|
tgBotChatId: z.string(),
|
|
tgBotEnable: z.boolean(),
|
|
tgBotProxy: z.string(),
|
|
tgBotToken: z.string(),
|
|
tgCpu: z.number().int().min(0).max(100),
|
|
tgEnabledEvents: z.string(),
|
|
tgLang: z.string(),
|
|
tgMemory: z.number().int().min(0).max(100),
|
|
tgRunTime: z.string(),
|
|
timeLocation: z.string(),
|
|
trafficDiff: z.number().int().min(0).max(100),
|
|
trustedProxyCIDRs: z.string(),
|
|
twoFactorEnable: z.boolean(),
|
|
twoFactorToken: z.string(),
|
|
warpUpdateInterval: z.number().int().min(0),
|
|
webBasePath: z.string(),
|
|
webCertFile: z.string(),
|
|
webDomain: z.string(),
|
|
webKeyFile: z.string(),
|
|
webListen: z.string(),
|
|
webPort: z.number().int().min(1).max(65535),
|
|
});
|
|
export type AllSettingView = z.infer<typeof AllSettingViewSchema>;
|
|
|
|
export const ApiTokenSchema = z.object({
|
|
createdAt: z.number().int(),
|
|
enabled: z.boolean(),
|
|
expiresAt: z.number().int(),
|
|
id: z.number().int(),
|
|
name: z.string(),
|
|
scope: z.string(),
|
|
token: z.string(),
|
|
});
|
|
export type ApiToken = z.infer<typeof ApiTokenSchema>;
|
|
|
|
export const ApiTokenViewSchema = z.object({
|
|
createdAt: z.number().int(),
|
|
enabled: z.boolean(),
|
|
expiresAt: z.number().int(),
|
|
id: z.number().int(),
|
|
name: z.string(),
|
|
scope: z.string(),
|
|
token: z.string().optional(),
|
|
});
|
|
export type ApiTokenView = z.infer<typeof ApiTokenViewSchema>;
|
|
|
|
export const ClientSchema = z.object({
|
|
adTag: z.string().optional(),
|
|
allowedIPs: z.array(z.string()).optional(),
|
|
auth: z.string().optional(),
|
|
comment: z.string(),
|
|
created_at: z.number().int().optional(),
|
|
email: z.string(),
|
|
enable: z.boolean(),
|
|
expiryTime: z.number().int(),
|
|
flow: z.string().optional(),
|
|
group: z.string().optional(),
|
|
id: z.string().optional(),
|
|
keepAlive: z.number().int().optional(),
|
|
limitIp: z.number().int(),
|
|
password: z.string().optional(),
|
|
preSharedKey: z.string().optional(),
|
|
privateKey: z.string().optional(),
|
|
publicKey: z.string().optional(),
|
|
reset: z.number().int(),
|
|
resetDay: z.number().int(),
|
|
resetMax: z.number().int(),
|
|
reverse: z.lazy(() => ClientReverseSchema).nullable().optional(),
|
|
secret: z.string().optional(),
|
|
security: z.string(),
|
|
subId: z.string(),
|
|
tgId: z.number().int(),
|
|
totalGB: z.number().int(),
|
|
trafficReset: z.enum(['never', 'hourly', 'daily', 'weekly', 'monthly']).optional(),
|
|
trafficResetDay: z.number().int().min(1).max(31).optional(),
|
|
updated_at: z.number().int().optional(),
|
|
});
|
|
export type Client = z.infer<typeof ClientSchema>;
|
|
|
|
export const ClientInboundSchema = z.object({
|
|
clientId: z.number().int(),
|
|
createdAt: z.number().int(),
|
|
flowOverride: z.string(),
|
|
inboundId: z.number().int(),
|
|
});
|
|
export type ClientInbound = z.infer<typeof ClientInboundSchema>;
|
|
|
|
export const ClientRecordSchema = z.object({
|
|
adTag: z.string(),
|
|
allowedIPs: z.string(),
|
|
auth: z.string(),
|
|
comment: z.string(),
|
|
createdAt: z.number().int(),
|
|
email: z.string(),
|
|
enable: z.boolean(),
|
|
expiryTime: z.number().int(),
|
|
flow: z.string(),
|
|
group: z.string(),
|
|
id: z.number().int(),
|
|
keepAlive: z.number().int(),
|
|
limitHwid: z.number().int(),
|
|
limitIp: z.number().int(),
|
|
password: z.string(),
|
|
preSharedKey: z.string(),
|
|
privateKey: z.string(),
|
|
publicKey: z.string(),
|
|
reset: z.number().int(),
|
|
resetDay: z.number().int(),
|
|
resetMax: z.number().int(),
|
|
reverse: z.unknown(),
|
|
secret: z.string(),
|
|
security: z.string(),
|
|
subId: z.string(),
|
|
tgId: z.number().int(),
|
|
totalGB: z.number().int(),
|
|
trafficReset: z.string(),
|
|
trafficResetDay: z.number().int(),
|
|
updatedAt: z.number().int(),
|
|
uuid: z.string(),
|
|
});
|
|
export type ClientRecord = z.infer<typeof ClientRecordSchema>;
|
|
|
|
export const ClientReverseSchema = z.object({
|
|
tag: z.string(),
|
|
});
|
|
export type ClientReverse = z.infer<typeof ClientReverseSchema>;
|
|
|
|
export const ClientTrafficSchema = z.object({
|
|
down: z.number().int(),
|
|
email: z.string(),
|
|
enable: z.boolean(),
|
|
expiryTime: z.number().int(),
|
|
id: z.number().int(),
|
|
inboundId: z.number().int(),
|
|
lastOnline: z.number().int(),
|
|
lastSubFetch: z.number().int(),
|
|
reset: z.number().int(),
|
|
resetCount: z.number().int(),
|
|
resetDay: z.number().int(),
|
|
resetMax: z.number().int(),
|
|
subId: z.string(),
|
|
total: z.number().int(),
|
|
up: z.number().int(),
|
|
uuid: z.string(),
|
|
});
|
|
export type ClientTraffic = z.infer<typeof ClientTrafficSchema>;
|
|
|
|
export const FallbackParentInfoSchema = z.object({
|
|
masterId: z.number().int(),
|
|
path: z.string().optional(),
|
|
});
|
|
export type FallbackParentInfo = z.infer<typeof FallbackParentInfoSchema>;
|
|
|
|
export const GeoCategorySchema = z.object({
|
|
attributes: z.array(z.string()),
|
|
code: z.string(),
|
|
entries: z.number().int(),
|
|
});
|
|
export type GeoCategory = z.infer<typeof GeoCategorySchema>;
|
|
|
|
export const GeoCategoryPageSchema = z.object({
|
|
items: z.array(z.lazy(() => GeoCategorySchema)),
|
|
total: z.number().int(),
|
|
});
|
|
export type GeoCategoryPage = z.infer<typeof GeoCategoryPageSchema>;
|
|
|
|
export const GeoEntrySchema = z.object({
|
|
kind: z.string(),
|
|
value: z.string(),
|
|
});
|
|
export type GeoEntry = z.infer<typeof GeoEntrySchema>;
|
|
|
|
export const GeoEntryPageSchema = z.object({
|
|
items: z.array(z.lazy(() => GeoEntrySchema)),
|
|
total: z.number().int(),
|
|
});
|
|
export type GeoEntryPage = z.infer<typeof GeoEntryPageSchema>;
|
|
|
|
export const GeoFileSchema = z.object({
|
|
categories: z.number().int(),
|
|
error: z.string().optional(),
|
|
kind: z.lazy(() => GeoKindSchema),
|
|
modifiedAt: z.number().int(),
|
|
name: z.string(),
|
|
size: z.number().int(),
|
|
});
|
|
export type GeoFile = z.infer<typeof GeoFileSchema>;
|
|
|
|
export const GeodataTokenIssueSchema = z.object({
|
|
code: z.string().optional(),
|
|
file: z.string().optional(),
|
|
reason: z.string(),
|
|
token: z.string(),
|
|
});
|
|
export type GeodataTokenIssue = z.infer<typeof GeodataTokenIssueSchema>;
|
|
|
|
export const HistoryOfSeedersSchema = z.object({
|
|
id: z.number().int(),
|
|
seederName: z.string(),
|
|
});
|
|
export type HistoryOfSeeders = z.infer<typeof HistoryOfSeedersSchema>;
|
|
|
|
export const HostSchema = z.object({
|
|
address: z.string(),
|
|
allowInsecure: z.boolean(),
|
|
alpn: z.array(z.string()),
|
|
createdAt: z.number().int(),
|
|
echConfigList: z.string(),
|
|
excludeFromSubTypes: z.array(z.string()),
|
|
finalMask: z.string(),
|
|
fingerprint: z.string(),
|
|
groupId: z.string(),
|
|
hostHeader: z.string(),
|
|
id: z.number().int(),
|
|
inboundId: z.number().int(),
|
|
isDisabled: z.boolean(),
|
|
isHidden: z.boolean(),
|
|
keepSniBlank: z.boolean(),
|
|
mihomoIpVersion: z.enum(['dual', 'ipv4', 'ipv6', 'ipv4-prefer', 'ipv6-prefer']),
|
|
mihomoX25519: z.boolean(),
|
|
muxParams: z.unknown(),
|
|
nodeGuids: z.array(z.string()).optional(),
|
|
overrideSniFromAddress: z.boolean(),
|
|
path: z.string(),
|
|
pinnedPeerCertSha256: z.array(z.string()),
|
|
port: z.number().int().min(0).max(65535),
|
|
remark: z.string().max(256),
|
|
security: z.enum(['same', 'tls', 'none', 'reality']),
|
|
serverDescription: z.string().max(64),
|
|
shuffleHost: z.boolean(),
|
|
sni: z.string(),
|
|
sockoptParams: z.unknown(),
|
|
sortOrder: z.number().int(),
|
|
tags: z.array(z.string()),
|
|
updatedAt: z.number().int(),
|
|
verifyPeerCertByName: z.string(),
|
|
vlessRoute: z.string(),
|
|
});
|
|
export type Host = z.infer<typeof HostSchema>;
|
|
|
|
export const HostGroupSchema = z.object({
|
|
allowInsecure: z.boolean(),
|
|
alpn: z.array(z.string()),
|
|
echConfigList: z.string(),
|
|
excludeFromSubTypes: z.array(z.string()),
|
|
finalMask: z.string(),
|
|
fingerprint: z.string(),
|
|
groupId: z.string(),
|
|
hostHeader: z.string(),
|
|
hosts: z.array(z.string()),
|
|
inboundIds: z.array(z.number().int()),
|
|
isDisabled: z.boolean(),
|
|
isHidden: z.boolean(),
|
|
keepSniBlank: z.boolean(),
|
|
mihomoIpVersion: z.enum(['dual', 'ipv4', 'ipv6', 'ipv4-prefer', 'ipv6-prefer']),
|
|
mihomoX25519: z.boolean(),
|
|
muxParams: z.string(),
|
|
nodeGuids: z.array(z.string()),
|
|
overrideSniFromAddress: z.boolean(),
|
|
path: z.string(),
|
|
pinnedPeerCertSha256: z.array(z.string()),
|
|
port: z.number().int().min(0).max(65535),
|
|
remark: z.string().max(256),
|
|
security: z.enum(['same', 'tls', 'none', 'reality']),
|
|
serverDescription: z.string().max(64),
|
|
shuffleHost: z.boolean(),
|
|
sni: z.string(),
|
|
sockoptParams: z.string(),
|
|
sortOrder: z.number().int(),
|
|
tags: z.array(z.string()),
|
|
verifyPeerCertByName: z.string(),
|
|
vlessRoute: z.string(),
|
|
});
|
|
export type HostGroup = z.infer<typeof HostGroupSchema>;
|
|
|
|
export const InboundSchema = z.object({
|
|
clientStats: z.array(z.lazy(() => ClientTrafficSchema)),
|
|
disableFlow: z.boolean(),
|
|
down: z.number().int(),
|
|
enable: z.boolean(),
|
|
expiryTime: z.number().int(),
|
|
fallbackParent: z.lazy(() => FallbackParentInfoSchema).nullable().optional(),
|
|
id: z.number().int(),
|
|
lastTrafficResetTime: z.number().int(),
|
|
listen: z.string(),
|
|
nodeId: z.number().int().nullable().optional(),
|
|
originNodeGuid: z.string().optional(),
|
|
port: z.number().int().min(0).max(65535),
|
|
protocol: z.enum(['vmess', 'vless', 'trojan', 'shadowsocks', 'wireguard', 'hysteria', 'http', 'mixed', 'tunnel', 'tun', 'mtproto']),
|
|
remark: z.string(),
|
|
settings: z.unknown(),
|
|
shareAddr: z.string(),
|
|
shareAddrStrategy: z.enum(['node', 'listen', 'custom']),
|
|
sniffing: z.unknown(),
|
|
streamSettings: z.unknown(),
|
|
subSortIndex: z.number().int().min(1),
|
|
tag: z.string(),
|
|
total: z.number().int(),
|
|
trafficReset: z.enum(['never', 'hourly', 'daily', 'weekly', 'monthly']),
|
|
trafficResetDay: z.number().int().min(1).max(31),
|
|
up: z.number().int(),
|
|
});
|
|
export type Inbound = z.infer<typeof InboundSchema>;
|
|
|
|
export const InboundClientIpsSchema = z.object({
|
|
clientEmail: z.string(),
|
|
id: z.number().int(),
|
|
ips: z.unknown(),
|
|
});
|
|
export type InboundClientIps = z.infer<typeof InboundClientIpsSchema>;
|
|
|
|
export const InboundFallbackSchema = z.object({
|
|
alpn: z.string(),
|
|
childId: z.number().int(),
|
|
dest: z.string(),
|
|
id: z.number().int(),
|
|
masterId: z.number().int(),
|
|
name: z.string(),
|
|
path: z.string(),
|
|
sortOrder: z.number().int(),
|
|
xver: z.number().int(),
|
|
});
|
|
export type InboundFallback = z.infer<typeof InboundFallbackSchema>;
|
|
|
|
export const InboundOptionSchema = z.object({
|
|
enable: z.boolean(),
|
|
id: z.number().int(),
|
|
listen: z.string().optional(),
|
|
mtprotoDomain: z.string().optional(),
|
|
nodeAddress: z.string().optional(),
|
|
nodeId: z.number().int().nullable().optional(),
|
|
port: z.number().int(),
|
|
protocol: z.string(),
|
|
remark: z.string(),
|
|
shareAddr: z.string().optional(),
|
|
shareAddrStrategy: z.string().optional(),
|
|
ssMethod: z.string(),
|
|
tag: z.string(),
|
|
tlsFlowCapable: z.boolean(),
|
|
wgDns: z.string().optional(),
|
|
wgMtu: z.number().int().optional(),
|
|
wgPublicKey: z.string().optional(),
|
|
});
|
|
export type InboundOption = z.infer<typeof InboundOptionSchema>;
|
|
|
|
export const MsgSchema = z.object({
|
|
msg: z.string(),
|
|
obj: z.unknown(),
|
|
success: z.boolean(),
|
|
});
|
|
export type Msg = z.infer<typeof MsgSchema>;
|
|
|
|
export const NodeSchema = z.object({
|
|
activeCount: z.number().int(),
|
|
address: z.string(),
|
|
allowPrivateAddress: z.boolean(),
|
|
basePath: z.string(),
|
|
clientCount: z.number().int(),
|
|
configDirty: z.boolean(),
|
|
configDirtyAt: z.number().int(),
|
|
cpuPct: z.number(),
|
|
createdAt: z.number().int(),
|
|
depletedCount: z.number().int(),
|
|
disabledCount: z.number().int(),
|
|
enable: z.boolean(),
|
|
guid: z.string(),
|
|
id: z.number().int(),
|
|
inboundCount: z.number().int(),
|
|
inboundSyncMode: z.enum(['all', 'selected']),
|
|
inboundTags: z.array(z.string()),
|
|
lastError: z.string(),
|
|
lastHeartbeat: z.number().int(),
|
|
latencyMs: z.number().int(),
|
|
memPct: z.number(),
|
|
name: z.string(),
|
|
netDown: z.number().int(),
|
|
netUp: z.number().int(),
|
|
onlineCount: z.number().int(),
|
|
outboundTag: z.string(),
|
|
panelVersion: z.string(),
|
|
parentGuid: z.string().optional(),
|
|
pinnedCertSha256: z.string(),
|
|
port: z.number().int().min(1).max(65535),
|
|
remark: z.string(),
|
|
scheme: z.enum(['http', 'https']),
|
|
status: z.string(),
|
|
tlsVerifyMode: z.enum(['verify', 'skip', 'pin', 'mtls']),
|
|
transitive: z.boolean().optional(),
|
|
updatedAt: z.number().int(),
|
|
uptimeSecs: z.number().int(),
|
|
xrayError: z.string(),
|
|
xrayState: z.string(),
|
|
xrayVersion: z.string(),
|
|
});
|
|
export type Node = z.infer<typeof NodeSchema>;
|
|
|
|
export const NodeMutationRequestSchema = z.object({
|
|
address: z.string(),
|
|
allowPrivateAddress: z.boolean(),
|
|
apiToken: z.string().nullable().optional(),
|
|
basePath: z.string(),
|
|
clearApiToken: z.boolean().optional(),
|
|
enable: z.boolean(),
|
|
id: z.number().int(),
|
|
inboundSyncMode: z.enum(['all', 'selected']),
|
|
inboundTags: z.array(z.string()),
|
|
name: z.string(),
|
|
outboundTag: z.string(),
|
|
pinnedCertSha256: z.string(),
|
|
port: z.number().int().min(1).max(65535),
|
|
remark: z.string(),
|
|
scheme: z.enum(['http', 'https']),
|
|
tlsVerifyMode: z.enum(['verify', 'skip', 'pin', 'mtls']),
|
|
});
|
|
export type NodeMutationRequest = z.infer<typeof NodeMutationRequestSchema>;
|
|
|
|
export const NodeViewSchema = z.object({
|
|
activeCount: z.number().int(),
|
|
address: z.string(),
|
|
allowPrivateAddress: z.boolean(),
|
|
basePath: z.string(),
|
|
clientCount: z.number().int(),
|
|
configDirty: z.boolean(),
|
|
configDirtyAt: z.number().int(),
|
|
cpuPct: z.number(),
|
|
createdAt: z.number().int(),
|
|
depletedCount: z.number().int(),
|
|
disabledCount: z.number().int(),
|
|
enable: z.boolean(),
|
|
guid: z.string(),
|
|
hasApiToken: z.boolean(),
|
|
id: z.number().int(),
|
|
inboundCount: z.number().int(),
|
|
inboundSyncMode: z.string(),
|
|
inboundTags: z.array(z.string()),
|
|
lastError: z.string(),
|
|
lastHeartbeat: z.number().int(),
|
|
latencyMs: z.number().int(),
|
|
memPct: z.number(),
|
|
name: z.string(),
|
|
netDown: z.number().int(),
|
|
netUp: z.number().int(),
|
|
onlineCount: z.number().int(),
|
|
outboundTag: z.string(),
|
|
panelVersion: z.string(),
|
|
parentGuid: z.string().optional(),
|
|
pinnedCertSha256: z.string(),
|
|
port: z.number().int(),
|
|
remark: z.string(),
|
|
scheme: z.string(),
|
|
status: z.string(),
|
|
tlsVerifyMode: z.string(),
|
|
transitive: z.boolean().optional(),
|
|
updatedAt: z.number().int(),
|
|
uptimeSecs: z.number().int(),
|
|
xrayError: z.string(),
|
|
xrayState: z.string(),
|
|
xrayVersion: z.string(),
|
|
});
|
|
export type NodeView = z.infer<typeof NodeViewSchema>;
|
|
|
|
export const OutboundTrafficsSchema = z.object({
|
|
down: z.number().int(),
|
|
id: z.number().int(),
|
|
tag: z.string(),
|
|
total: z.number().int(),
|
|
up: z.number().int(),
|
|
});
|
|
export type OutboundTraffics = z.infer<typeof OutboundTrafficsSchema>;
|
|
|
|
export const PanelUpdateStatusSchema = z.object({
|
|
exitCode: z.number().int(),
|
|
finishedAt: z.number().int(),
|
|
runId: z.string(),
|
|
state: z.string(),
|
|
});
|
|
export type PanelUpdateStatus = z.infer<typeof PanelUpdateStatusSchema>;
|
|
|
|
export const ProbeResultUISchema = z.object({
|
|
cpuPct: z.number(),
|
|
error: z.string(),
|
|
latencyMs: z.number().int(),
|
|
memPct: z.number(),
|
|
panelVersion: z.string(),
|
|
status: z.string(),
|
|
uptimeSecs: z.number().int(),
|
|
xrayError: z.string(),
|
|
xrayState: z.string(),
|
|
xrayVersion: z.string(),
|
|
});
|
|
export type ProbeResultUI = z.infer<typeof ProbeResultUISchema>;
|
|
|
|
export const RealityScanResultSchema = z.object({
|
|
alpn: z.string(),
|
|
certIssuer: z.string(),
|
|
certSubject: z.string(),
|
|
certValid: z.boolean(),
|
|
curveID: z.string(),
|
|
feasible: z.boolean(),
|
|
h2: z.boolean(),
|
|
host: z.string(),
|
|
ip: z.string(),
|
|
latencyMs: z.number().int(),
|
|
notAfter: z.string(),
|
|
port: z.number().int(),
|
|
reason: z.string(),
|
|
serverNames: z.array(z.string()),
|
|
target: z.string(),
|
|
tls13: z.boolean(),
|
|
tlsVersion: z.string(),
|
|
x25519: z.boolean(),
|
|
});
|
|
export type RealityScanResult = z.infer<typeof RealityScanResultSchema>;
|
|
|
|
export const SettingSchema = z.object({
|
|
id: z.number().int(),
|
|
key: z.string(),
|
|
value: z.string(),
|
|
});
|
|
export type Setting = z.infer<typeof SettingSchema>;
|
|
|
|
export const UserSchema = z.object({
|
|
id: z.number().int(),
|
|
password: z.string(),
|
|
username: z.string(),
|
|
});
|
|
export type User = z.infer<typeof UserSchema>;
|
|
|