Подготовить HY2XS к production-сборке
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
import request from "@/utils/request";
|
||||
import { AxiosPromise } from "axios";
|
||||
import {
|
||||
AccountSaveDto,
|
||||
AccountInfo,
|
||||
AccountLoginDto,
|
||||
AccountLoginVo,
|
||||
AccountPageDto,
|
||||
AccountUpdateDto,
|
||||
AccountVo,
|
||||
} from "./types";
|
||||
|
||||
/**
|
||||
* 查询
|
||||
*/
|
||||
export function getAccountApi(data: IdDto): AxiosPromise<AccountVo> {
|
||||
return request({
|
||||
url: "/account/getAccount",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
export function saveAccountApi(data: AccountSaveDto): AxiosPromise {
|
||||
return request({
|
||||
url: "/account/saveAccount",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前
|
||||
*/
|
||||
export function getAccountInfoApi(): AxiosPromise<AccountInfo> {
|
||||
return request({
|
||||
url: "/account/getAccountInfo",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
* @param data
|
||||
*/
|
||||
export function pageAccountApi(
|
||||
data: AccountPageDto
|
||||
): AxiosPromise<PageVo<AccountVo>> {
|
||||
return request({
|
||||
url: "/account/pageAccount",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
export function deleteAccountApi(data: IdDto): AxiosPromise {
|
||||
return request({
|
||||
url: "/account/deleteAccount",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
* @param data
|
||||
*/
|
||||
export function updateAccountApi(data: AccountUpdateDto): AxiosPromise {
|
||||
return request({
|
||||
url: "/account/updateAccount",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重设流量
|
||||
* @param data
|
||||
*/
|
||||
export function resetTrafficApi(data: IdDto): AxiosPromise {
|
||||
return request({
|
||||
url: "/account/resetTraffic",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param data
|
||||
*/
|
||||
export function loginApi(data: AccountLoginDto): AxiosPromise<AccountLoginVo> {
|
||||
return request({
|
||||
url: "/auth/login",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入
|
||||
*/
|
||||
export function importAccountApi(data: FormData): AxiosPromise {
|
||||
return request({
|
||||
url: "/account/importAccount",
|
||||
method: "post",
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出
|
||||
*/
|
||||
export function exportAccountApi(): AxiosPromise {
|
||||
return request({
|
||||
url: "/account/exportAccount",
|
||||
method: "post",
|
||||
responseType: "blob",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除下线状态
|
||||
*/
|
||||
export function releaseKickAccountApi(data: IdDto): AxiosPromise {
|
||||
return request({
|
||||
url: "/account/releaseKickAccount",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否默认密码
|
||||
* @param data
|
||||
*/
|
||||
export function verifyDefaultPassApi(): AxiosPromise {
|
||||
return request({
|
||||
url: "/account/verifyDefaultPass",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
export interface AccountPageDto extends BaseDto {
|
||||
username?: string;
|
||||
deleted?: number;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface AccountUpdateDto extends IdDto {
|
||||
username: string;
|
||||
pass: string;
|
||||
conPass: string;
|
||||
quota: number;
|
||||
expireTime: number;
|
||||
deviceNo: number;
|
||||
deleted: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface AccountSaveDto {
|
||||
username: string;
|
||||
pass: string;
|
||||
conPass: string;
|
||||
quota: number;
|
||||
expireTime: number;
|
||||
deviceNo: number;
|
||||
deleted: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface AccountLoginDto {
|
||||
username: string;
|
||||
pass: string;
|
||||
}
|
||||
|
||||
export interface AccountVo extends IdDto {
|
||||
username: string;
|
||||
quota: number;
|
||||
download: number;
|
||||
upload: number;
|
||||
expireTime: number;
|
||||
kickUtilTime: number;
|
||||
deviceNo: number;
|
||||
role: string;
|
||||
deleted: number;
|
||||
createTime: string;
|
||||
|
||||
online: boolean;
|
||||
device: number;
|
||||
|
||||
loginAt: number;
|
||||
conAt: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface AccountLoginVo {
|
||||
accessToken: string;
|
||||
tokenType: string;
|
||||
}
|
||||
|
||||
export interface AccountInfo {
|
||||
id: number;
|
||||
username: string;
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
export interface AccountForm extends IdDto {
|
||||
username: string;
|
||||
pass: string;
|
||||
conPass: string;
|
||||
quota: number;
|
||||
expireTime: number;
|
||||
deviceNo: number;
|
||||
deleted: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface KickAccountForm {
|
||||
ids: number[];
|
||||
kickUtilTime: number;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { AxiosPromise } from "axios";
|
||||
import request from "@/utils/request";
|
||||
import {
|
||||
ConfigDto,
|
||||
ConfigsDto,
|
||||
ConfigUpdateDto,
|
||||
ConfigVo,
|
||||
Hysteria2AcmePathVo,
|
||||
Hysteria2ServerConfig,
|
||||
} from "@/api/config/types";
|
||||
|
||||
export function getHysteria2ConfigApi(): AxiosPromise<Hysteria2ServerConfig> {
|
||||
return request({
|
||||
url: "/config/getHysteria2Config",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function updateHysteria2ConfigApi(
|
||||
data: Hysteria2ServerConfig
|
||||
): AxiosPromise {
|
||||
return request({
|
||||
url: "/config/updateHysteria2Config",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function getConfigApi(data: ConfigDto): AxiosPromise<ConfigVo> {
|
||||
return request({
|
||||
url: "/config/getConfig",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function listConfigApi(data: ConfigsDto): AxiosPromise<Array<ConfigVo>> {
|
||||
return request({
|
||||
url: "/config/listConfig",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateConfigsApi(data: ConfigUpdateDto): AxiosPromise {
|
||||
return request({
|
||||
url: "/config/updateConfigs",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function exportConfigApi(): AxiosPromise {
|
||||
return request({
|
||||
url: "/config/exportConfig",
|
||||
method: "post",
|
||||
responseType: "blob",
|
||||
});
|
||||
}
|
||||
|
||||
export function importConfigApi(data: FormData): AxiosPromise {
|
||||
return request({
|
||||
url: "/config/importConfig",
|
||||
method: "post",
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function exportHysteria2ConfigApi(): AxiosPromise {
|
||||
return request({
|
||||
url: "/config/exportHysteria2Config",
|
||||
method: "post",
|
||||
responseType: "blob",
|
||||
});
|
||||
}
|
||||
|
||||
export function importHysteria2ConfigApi(data: FormData): AxiosPromise {
|
||||
return request({
|
||||
url: "/config/importHysteria2Config",
|
||||
method: "post",
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function hysteria2AcmePathApi(): AxiosPromise<Hysteria2AcmePathVo> {
|
||||
return request({
|
||||
url: "/config/hysteria2AcmePath",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function restartServerApi(): AxiosPromise {
|
||||
return request({
|
||||
url: "/config/restartServer",
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
export function uploadCertFileApi(data: FormData): AxiosPromise<string> {
|
||||
return request({
|
||||
url: "/config/uploadCertFile",
|
||||
method: "post",
|
||||
data,
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
export interface ConfigDto {
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface ConfigsDto {
|
||||
keys: Array<string>;
|
||||
}
|
||||
|
||||
export interface ConfigVo {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ConfigsUpdateDto {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ConfigUpdateDto {
|
||||
configUpdateDtos: Array<ConfigsUpdateDto>;
|
||||
}
|
||||
|
||||
export interface Hysteria2ServerConfig {
|
||||
listen: string;
|
||||
tls?: {
|
||||
cert: string;
|
||||
key: string;
|
||||
sniGuard?: string;
|
||||
};
|
||||
acme?: {
|
||||
domains: string[];
|
||||
email: string;
|
||||
ca: string;
|
||||
listenHost: string;
|
||||
dir: string;
|
||||
type?: string;
|
||||
http?: {
|
||||
altPort: number;
|
||||
};
|
||||
tls?: {
|
||||
altPort: number;
|
||||
};
|
||||
dns?: {
|
||||
name: string;
|
||||
config: { [key: string]: string };
|
||||
};
|
||||
disableHTTP: boolean;
|
||||
disableTLSALPN: boolean;
|
||||
altHTTPPort: number;
|
||||
altTLSALPNPort: number;
|
||||
};
|
||||
obfs?: {
|
||||
type: string;
|
||||
salamander: {
|
||||
password: string;
|
||||
};
|
||||
};
|
||||
quic?: {
|
||||
initStreamReceiveWindow?: number;
|
||||
maxStreamReceiveWindow?: number;
|
||||
initConnReceiveWindow?: number;
|
||||
maxConnReceiveWindow?: number;
|
||||
maxIdleTimeout?: string;
|
||||
maxIncomingStreams?: number;
|
||||
disablePathMTUDiscovery?: boolean;
|
||||
};
|
||||
bandwidth?: {
|
||||
up: string;
|
||||
down: string;
|
||||
};
|
||||
ignoreClientBandwidth?: boolean;
|
||||
speedTest?: boolean;
|
||||
disableUDP?: boolean;
|
||||
udpIdleTimeout?: string;
|
||||
resolver?: {
|
||||
type: string;
|
||||
tcp?: {
|
||||
addr: string;
|
||||
timeout: string;
|
||||
};
|
||||
udp?: {
|
||||
addr: string;
|
||||
timeout: string;
|
||||
};
|
||||
tls?: {
|
||||
addr: string;
|
||||
timeout: string;
|
||||
sni: string;
|
||||
insecure: boolean;
|
||||
};
|
||||
https?: {
|
||||
addr: string;
|
||||
timeout: string;
|
||||
sni: string;
|
||||
insecure: boolean;
|
||||
};
|
||||
};
|
||||
sniff?: {
|
||||
enable: boolean;
|
||||
timeout: string;
|
||||
rewriteDomain: boolean;
|
||||
tcpPorts?: string;
|
||||
udpPorts?: string;
|
||||
};
|
||||
acl?: {
|
||||
file?: string;
|
||||
inline?: string[];
|
||||
geoip?: string;
|
||||
geosite?: string;
|
||||
geoUpdateInterval?: string;
|
||||
};
|
||||
outbounds?: Hysteria2ServerConfigOutbound[];
|
||||
trafficStats: {
|
||||
listen: string;
|
||||
};
|
||||
masquerade?: {
|
||||
type: string;
|
||||
file?: {
|
||||
dir: string;
|
||||
};
|
||||
proxy?: {
|
||||
url: string;
|
||||
rewriteHost: boolean;
|
||||
insecure: boolean;
|
||||
};
|
||||
string?: {
|
||||
content: string;
|
||||
headers?: { [key: string]: string };
|
||||
statusCode?: number;
|
||||
};
|
||||
listenHTTP?: string;
|
||||
listenHTTPS?: string;
|
||||
forceHTTPS?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const defaultHysteria2ServerConfig: Hysteria2ServerConfig = {
|
||||
listen: ":443",
|
||||
tls: {
|
||||
cert: "",
|
||||
key: "",
|
||||
sniGuard: "",
|
||||
},
|
||||
acme: {
|
||||
domains: [],
|
||||
email: "",
|
||||
ca: "zerossl",
|
||||
listenHost: "0.0.0.0",
|
||||
dir: "my_acme_dir",
|
||||
type: "",
|
||||
http: {
|
||||
altPort: 8888,
|
||||
},
|
||||
tls: {
|
||||
altPort: 44333,
|
||||
},
|
||||
dns: {
|
||||
name: "gomommy",
|
||||
config: {},
|
||||
},
|
||||
disableHTTP: false,
|
||||
disableTLSALPN: false,
|
||||
altHTTPPort: 80,
|
||||
altTLSALPNPort: 443,
|
||||
},
|
||||
obfs: {
|
||||
type: "salamander",
|
||||
salamander: {
|
||||
password: "cry_me_a_r1ver",
|
||||
},
|
||||
},
|
||||
quic: {
|
||||
initStreamReceiveWindow: 8388608,
|
||||
maxStreamReceiveWindow: 8388608,
|
||||
initConnReceiveWindow: 20971520,
|
||||
maxConnReceiveWindow: 20971520,
|
||||
maxIdleTimeout: "30s",
|
||||
maxIncomingStreams: 1024,
|
||||
disablePathMTUDiscovery: false,
|
||||
},
|
||||
bandwidth: {
|
||||
up: "1 gbps",
|
||||
down: "1 gbps",
|
||||
},
|
||||
ignoreClientBandwidth: false,
|
||||
speedTest: false,
|
||||
disableUDP: false,
|
||||
udpIdleTimeout: "60s",
|
||||
resolver: {
|
||||
type: "",
|
||||
tcp: {
|
||||
addr: "8.8.8.8:53",
|
||||
timeout: "4s",
|
||||
},
|
||||
udp: {
|
||||
addr: "8.8.4.4:53",
|
||||
timeout: "4s",
|
||||
},
|
||||
tls: {
|
||||
addr: "1.1.1.1:853",
|
||||
timeout: "10s",
|
||||
sni: "cloudflare-dns.com",
|
||||
insecure: false,
|
||||
},
|
||||
https: {
|
||||
addr: "1.1.1.1:443",
|
||||
timeout: "10s",
|
||||
sni: "cloudflare-dns.com",
|
||||
insecure: false,
|
||||
},
|
||||
},
|
||||
sniff: {
|
||||
enable: true,
|
||||
timeout: "2s",
|
||||
rewriteDomain: false,
|
||||
tcpPorts: "80,443,8000-9000",
|
||||
udpPorts: "all",
|
||||
},
|
||||
acl: {
|
||||
file: "",
|
||||
inline: [],
|
||||
geoip: "",
|
||||
geosite: "",
|
||||
geoUpdateInterval: "168h",
|
||||
},
|
||||
outbounds: [],
|
||||
trafficStats: {
|
||||
listen: ":9999",
|
||||
},
|
||||
masquerade: {
|
||||
type: "",
|
||||
file: {
|
||||
dir: "",
|
||||
},
|
||||
proxy: {
|
||||
url: "",
|
||||
rewriteHost: true,
|
||||
insecure: false,
|
||||
},
|
||||
string: {
|
||||
content: "hello stupid world",
|
||||
headers: {},
|
||||
statusCode: 200,
|
||||
},
|
||||
listenHTTP: ":80",
|
||||
listenHTTPS: ":443",
|
||||
forceHTTPS: true,
|
||||
},
|
||||
};
|
||||
|
||||
export interface Hysteria2ServerConfigOutbound {
|
||||
name: string;
|
||||
type: string;
|
||||
socks5?: {
|
||||
addr: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
};
|
||||
http?: {
|
||||
url: string;
|
||||
insecure: boolean;
|
||||
};
|
||||
direct?: {
|
||||
mode: string;
|
||||
bindIPv4?: string;
|
||||
bindIPv6?: string;
|
||||
bindDevice?: string;
|
||||
fastOpen?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const defaultHysteria2ServerConfigOutbound: Hysteria2ServerConfigOutbound =
|
||||
{
|
||||
name: "",
|
||||
type: "socks5",
|
||||
socks5: {
|
||||
addr: "",
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
},
|
||||
http: {
|
||||
url: "",
|
||||
insecure: false,
|
||||
},
|
||||
direct: {
|
||||
mode: "auto",
|
||||
bindIPv4: undefined,
|
||||
bindIPv6: undefined,
|
||||
bindDevice: undefined,
|
||||
fastOpen: false,
|
||||
},
|
||||
};
|
||||
|
||||
export interface Tab {
|
||||
name: string;
|
||||
desc: string;
|
||||
}
|
||||
|
||||
export interface Hysteria2AcmePathVo {
|
||||
crtPath: string;
|
||||
keyPath: string;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { AxiosPromise } from "axios";
|
||||
import { Hysteria2ServerConfig } from "@/api/config/types";
|
||||
import request from "@/utils/request";
|
||||
import {
|
||||
Hysteria2KickDto,
|
||||
Hysteria2SubscribeVo,
|
||||
Hysteria2SubscribeUrlDto,
|
||||
Hysteria2UrlDto,
|
||||
Hysteria2UrlVo,
|
||||
} from "@/api/hysteria2/types";
|
||||
|
||||
export function hysteria2KickApi(
|
||||
data: Hysteria2KickDto
|
||||
): AxiosPromise<Hysteria2ServerConfig> {
|
||||
return request({
|
||||
url: "/hysteria2/hysteria2Kick",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function hysteria2SubscribeUrlApi(
|
||||
dto: Hysteria2SubscribeUrlDto
|
||||
): AxiosPromise<Hysteria2SubscribeVo> {
|
||||
return request({
|
||||
url: "/hysteria2/hysteria2SubscribeUrl",
|
||||
method: "get",
|
||||
params: dto,
|
||||
});
|
||||
}
|
||||
|
||||
export function hysteria2UrlApi(
|
||||
dto: Hysteria2UrlDto
|
||||
): AxiosPromise<Hysteria2UrlVo> {
|
||||
return request({
|
||||
url: "/hysteria2/hysteria2Url",
|
||||
method: "get",
|
||||
params: dto,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
export interface Hysteria2KickDto {
|
||||
ids: number[];
|
||||
kickUtilTime: number;
|
||||
}
|
||||
|
||||
export interface Hysteria2SubscribeUrlDto {
|
||||
accountId: number;
|
||||
protocol: string;
|
||||
host: string;
|
||||
}
|
||||
|
||||
export interface Hysteria2UrlDto {
|
||||
accountId: number;
|
||||
hostname: string;
|
||||
}
|
||||
|
||||
export interface Hysteria2SubscribeVo {
|
||||
url: string;
|
||||
qrCode: string;
|
||||
}
|
||||
|
||||
|
||||
export interface Hysteria2UrlVo {
|
||||
url: string;
|
||||
qrCode: string;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { AxiosPromise } from "axios";
|
||||
import request from "@/utils/request";
|
||||
import {
|
||||
LogDto,
|
||||
LogExportDto,
|
||||
LogHysteria2Vo,
|
||||
LogSystemVo,
|
||||
} from "@/api/log/types";
|
||||
|
||||
export function logSystemApi(data: LogDto): AxiosPromise<PageVo<LogSystemVo>> {
|
||||
return request({
|
||||
url: "/log/logSystem",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function logHysteria2Api(
|
||||
data: LogDto
|
||||
): AxiosPromise<PageVo<LogHysteria2Vo>> {
|
||||
return request({
|
||||
url: "/log/logHysteria2",
|
||||
method: "get",
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function exportLogApi(data: LogExportDto): AxiosPromise {
|
||||
return request({
|
||||
url: "/log/exportLog",
|
||||
method: "post",
|
||||
data: data,
|
||||
responseType: "blob",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
export interface LogDto {
|
||||
numLine: number;
|
||||
}
|
||||
|
||||
export interface LogExportDto {
|
||||
option: number;
|
||||
}
|
||||
|
||||
export interface LogSystemVo {
|
||||
clientIp: string;
|
||||
latencyTime: string;
|
||||
level: string;
|
||||
msg: string;
|
||||
reqMethod: string;
|
||||
reqUri: string;
|
||||
statusCode: string;
|
||||
time: string;
|
||||
}
|
||||
|
||||
export interface LogHysteria2Vo {
|
||||
level: string;
|
||||
msg: string;
|
||||
time: string;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { AxiosPromise } from "axios";
|
||||
import request from "@/utils/request";
|
||||
import { Hysteria2MonitorVo, SystemMonitorVo } from "@/api/monitor/types";
|
||||
|
||||
export function monitorSystemApi(): AxiosPromise<SystemMonitorVo> {
|
||||
return request({
|
||||
url: "/monitor/monitorSystem",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
export function monitorHysteria2Api(): AxiosPromise<Hysteria2MonitorVo> {
|
||||
return request({
|
||||
url: "/monitor/monitorHysteria2",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface SystemMonitorVo {
|
||||
huiVersion: string;
|
||||
cpuPercent: number;
|
||||
diskPercent: number;
|
||||
memPercent: number;
|
||||
}
|
||||
|
||||
export interface Hysteria2MonitorVo {
|
||||
userTotal: number;
|
||||
deviceTotal: number;
|
||||
version: string;
|
||||
running: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user