162759c599
Разбор кода на c0a43ae со сверкой с официальной документацией Hysteria 2.
Общая тема: операции, у которых была только одна из двух необходимых половин.
Отзыв доступа. Запись disabled=1 видит лишь выборка в Hysteria2Auth, то есть
закрывает БУДУЩИЕ обращения к HTTP-auth; установленная QUIC-сессия живёт своей
жизнью и сама не разрывается. После «Отключить» пир пользовался доступом сколько
угодно долго, а панель показывала его отключённым. Появился DisconnectPeers —
только официальный Traffic Stats /kick, без записи в базу; прежний Hysteria2Kick
вместе с разрывом проставлял banned_until и потому для отключения не годился.
Порядок «запись, затем разрыв» обратному не подлежит и доказан снимком базы в
момент прихода /kick. Неудача разрыва не откатывает disabled и сообщается кодом
peer_disconnect_failed: обычная ошибка означала бы для оператора вывод, прямо
противоположный истине. KickPeer переведён на тот же примитив — он писал
banned_until дважды и мог ответить чистым отказом уже в применённом состоянии.
Ограничение устройств. Отказ /online обрабатывался возвратом успеха
авторизации, то есть недоступность 127.0.0.1 превращала объявленный лимит в
безлимит. Вторая половина дыры была тише: Hysteria2Online отдавал пустую карту
БЕЗ ошибки, когда systemd отвечал «служба неактивна», — а этот ответ не
отличается от «спросить systemctl не удалось». Пути разделены: терпимый для
отображения, строгий для решения о доступе. Hysteria2IsRunning убран с путей
принятия решений совсем.
Журнал. entry.Info() вызывался без аргумента, и logrus писал "msg":"" для
каждого запроса — пустой столбец на экране был точным отражением файла. Ветка
«файла ещё нет» отвечала голым массивом вместо {records,total}, поэтому на
свежей установке страница системных логов не работала вовсе. Битая строка
вызывала vo.Fail И continue: клиент получал два JSON-документа подряд.
Панель. Общий LogViewer и utils/download.ts (копий скачивания было четыре, две
ставили запрос вне try и глушили причину); меню на command с быстрым
включением/отключением; popper-style у подсказки; kick с подтверждением и
названным сроком; отмена подтверждений перестала быть ошибкой. Отдельно:
skipErrorToast гасил и транспортный отказ, при том что страницы писали
«перехватчик уже показал» и молчали, — обрыв связи не показывал ничего.
Закреплено go-тестами против настоящего HTTP, контрактными тестами панели и
двумя гейтами приёмки. Ручная часть — в
docs/acceptance/2026-09-01-v1.0.0-rc2-preflight-findings.md.
500 lines
21 KiB
Go
500 lines
21 KiB
Go
export default {
|
||
// МаршрутЛокализация
|
||
route: {
|
||
dashboard: "Dashboard",
|
||
peer: "Peers",
|
||
peerList: "Peer Management",
|
||
hysteria: "Hysteria",
|
||
hysteriaList: "Hysteria Manage",
|
||
config: "System",
|
||
configList: "System Config",
|
||
monitor: "Monitor",
|
||
monitorSystem: "System Monitor",
|
||
log: "Log",
|
||
logSystem: "System Log",
|
||
logHysteria: "Hysteria Log",
|
||
},
|
||
// Локализация страницы входа
|
||
login: {
|
||
title: "HY2XS admin",
|
||
username: "Username",
|
||
password: "Password",
|
||
login: "Login",
|
||
capsLockOn: "Caps lock is On",
|
||
usernameFormatIncorrect: "Username format is incorrect",
|
||
passwordFormatIncorrect: "Password format is incorrect",
|
||
},
|
||
dashboard: {
|
||
stale: "Dashboard data is stale. Retrying automatically...",
|
||
topPeers24h: "Top peers (24h)",
|
||
topPeersRange: "Top peers ({range})",
|
||
refreshFailed: "Failed to refresh dashboard data",
|
||
cpu: "CPU",
|
||
ram: "RAM",
|
||
disk: "Disk",
|
||
peers: "Peers",
|
||
onlinePeers: "Online peers",
|
||
onlineDevices: "Online devices",
|
||
todayDownload: "Today download",
|
||
todayUpload: "Today upload",
|
||
trafficChart: "Traffic & System Timeseries",
|
||
trafficSeriesChart: "Traffic: download/upload",
|
||
noTrafficData: "No traffic data for the selected period",
|
||
systemSeriesChart: "System: CPU/RAM",
|
||
download: "Download",
|
||
upload: "Upload",
|
||
total: "Total",
|
||
security: {
|
||
hysteriaStopped: "Hysteria2 is stopped",
|
||
trafficApiUnavailable: "Traffic API is unavailable",
|
||
},
|
||
error: {
|
||
trafficApiUnavailable: "Traffic API is unavailable",
|
||
collectorStale: "Collector data is stale",
|
||
},
|
||
},
|
||
admin: {
|
||
changePasswordTitle: "Change password",
|
||
oldPassword: "Old password",
|
||
newPassword: "New password",
|
||
forcePasswordChangeNotice:
|
||
"You must change your password before continuing.",
|
||
changedSuccess: "Password updated",
|
||
},
|
||
// Локализация навигационной панели
|
||
navbar: {
|
||
logout: "Logout",
|
||
},
|
||
common: {
|
||
id: "ID",
|
||
createTime: "Create Time",
|
||
operate: "Operate",
|
||
edit: "Edit",
|
||
delete: "Delete",
|
||
deleted: "Status",
|
||
all: "All",
|
||
enable: "Enable",
|
||
disable: "Disable",
|
||
search: "Search",
|
||
reset: "Reset",
|
||
add: "Add",
|
||
confirm: "Confirm",
|
||
cancel: "Cancel",
|
||
close: "Close",
|
||
copySuccess: "Copy successful",
|
||
nodeUrl: "Node URL",
|
||
nodeQrCode: "Node QR Code",
|
||
resetTraffic: "Reset traffic",
|
||
import: "Import",
|
||
export: "Export",
|
||
save: "Save",
|
||
update: "Update",
|
||
downloadSuccess: "Download successful",
|
||
wait: "The version is being changed, please wait a moment",
|
||
enableSuccess: "Hysteria2 start successful",
|
||
disableSuccess: "Hysteria2 stop successful",
|
||
success: "Success",
|
||
refresh: "Refresh",
|
||
yes: "Yes",
|
||
no: "No",
|
||
securityRisk: "Security Risks",
|
||
required: "Required",
|
||
warning: "Warning",
|
||
fileFormatUnsupported: "File format not supported",
|
||
fileTooLarge: "The file is too big, less than 2 MB",
|
||
weekLater: "A week later",
|
||
monthLater: "A month later",
|
||
yearLater: "A year later",
|
||
hourLater: "A hour later",
|
||
dayLater: "A day later",
|
||
deleteConfirm: "Are you sure to delete peer \u300c{username}\u300d?",
|
||
resetTrafficConfirm: "Are you sure to reset peer traffic?",
|
||
invalid: "Invalid value",
|
||
switchLanguageSuccess: "Language switched successfully",
|
||
logoutConfirm: "Are you sure you want to log out?",
|
||
sessionExpired: "Your session has expired. Sign in again to continue.",
|
||
signInRequired: "Signing in is required.",
|
||
signIn: "Sign in",
|
||
systemError: "System error",
|
||
networkError: "The server is not responding. Check the connection.",
|
||
},
|
||
error: {
|
||
field: {
|
||
name: "Peer name",
|
||
secret: "Secret",
|
||
remark: "Remark",
|
||
quotaBytes: "Quota",
|
||
expiresAt: "Expiry",
|
||
maxDevices: "Max devices",
|
||
disabled: "State",
|
||
bannedUntil: "Banned until",
|
||
file: "File",
|
||
id: "Identifier",
|
||
username: "Username",
|
||
pass: "Password",
|
||
oldPassword: "Old password",
|
||
newPassword: "New password",
|
||
key: "Setting key",
|
||
value: "Setting value",
|
||
numLine: "Line count",
|
||
pageNum: "Page number",
|
||
pageSize: "Page size",
|
||
},
|
||
code: {
|
||
required: "“{field}”: required",
|
||
min: "“{field}”: must not be less than {min}",
|
||
max: "“{field}”: must not be greater than {max}",
|
||
min_length: "“{field}”: at least {min} characters",
|
||
max_length: "“{field}”: at most {max} characters",
|
||
len: "“{field}”: length must be exactly {len}",
|
||
oneof: "“{field}”: allowed values are {values}",
|
||
gt: "“{field}”: must be greater than {gt}",
|
||
peer_name:
|
||
"“{field}”: {min} to {max} characters from {charset}. Spaces, non-latin letters and / : ; . are not allowed",
|
||
credential_format: "“{field}”: contains characters that are not allowed",
|
||
rule_violated: "“{field}”: value is not acceptable",
|
||
validation_failed: "Validation failed",
|
||
body_invalid:
|
||
"Request could not be parsed: check field formats and types",
|
||
peer_name_taken: "A peer with this name already exists",
|
||
peer_name_reserved: "This name is reserved for the installer peer",
|
||
peer_bootstrap_identity_locked:
|
||
"The installer peer's name and secret are mirrored in a file on the server and cannot be changed from the panel. Delete the bootstrap peer entirely if it is no longer needed.",
|
||
invalid_credentials: "Wrong username or password",
|
||
// The phrase must open with what has ALREADY been applied, otherwise it
|
||
// reads as "the operation failed" and the operator repeats an action
|
||
// that in fact went through.
|
||
peer_disconnect_failed:
|
||
"New connections for this peer are now refused, but its active session could not be terminated: the Hysteria Traffic Stats API is unreachable. An established connection may keep working until the client reconnects. Check the hysteria-server service and retry.",
|
||
import_file_extension: "Import accepts .json files only",
|
||
unauthorized: "Signing in is required",
|
||
session_expired: "Session expired",
|
||
token_invalid: "Session is not valid",
|
||
account_disabled: "Account is disabled",
|
||
},
|
||
},
|
||
sidebar: {
|
||
developedBy: "Made at {brand}",
|
||
},
|
||
info: {
|
||
expireTime: "y-M-d H:m:s",
|
||
greeting1: "The cool and fresh air awakens your energy for the day🌅!",
|
||
greeting2: "Good morning,",
|
||
greeting3: "Good afternoon,",
|
||
greeting4: "Good evening,",
|
||
greeting5:
|
||
"I want to be a shooting star, cutting through the darkness, just to illuminate your dreams, good night🌛!",
|
||
},
|
||
peer: {
|
||
name: "Peer",
|
||
remark: "Remark",
|
||
secret: "Secret",
|
||
form: {
|
||
namePlaceholder: "client-01",
|
||
// The character set is stated as service.IsValidPeerName accepts it. The
|
||
// previous wording described the rule that was in force before EX-03.
|
||
nameHint:
|
||
"Peer identifier: 6 to 32 characters — latin letters, digits and !@#$%^&*()_+-=. The name becomes part of the auto-generated secret and is shown to the client as the profile name.",
|
||
remarkPlaceholder: "laptop",
|
||
remarkHint: "Optional operator note. It is never shown to the client.",
|
||
secretPlaceholder: "leave empty to generate one",
|
||
secretHint:
|
||
"Client connection password. Leave empty to generate one automatically. If set manually: 6 to 128 characters.",
|
||
quotaHint: "Traffic limit in bytes. Use -1 for unlimited.",
|
||
},
|
||
maxDevices: "Max devices",
|
||
disabled: "Disabled",
|
||
status: "Status",
|
||
traffic: "Traffic",
|
||
devices: "Devices",
|
||
lastConnectionAt: "Last connection",
|
||
overview: "Overview",
|
||
authId: "Auth ID",
|
||
clientUri: "Client URI",
|
||
clientQr: "Client QR",
|
||
unlimited: "Unlimited",
|
||
secretRotateConfirm:
|
||
"Rotate peer secret? Existing client configurations will stop working until updated.",
|
||
exportSettings: "Export settings",
|
||
exportBackup: "Backup",
|
||
exportSettingsHint:
|
||
"Peer list without secrets. Safe to store and share, but after importing on another server the peers get new secrets, so existing client links stop working.",
|
||
exportBackupConfirm:
|
||
"A backup contains the LIVE connection secrets of every peer in plain text: anyone who gets this file gets VPN access. Treat it like a password and delete it once the migration is done. Only this kind of copy keeps existing client links working after moving to another server.",
|
||
exportBackupConfirmButton: "I understand, download with secrets",
|
||
quota: "Quota",
|
||
download: "Download",
|
||
upload: "Upload",
|
||
expireTime: "Expire Time",
|
||
kickUtilTimeLast: "Offline Remaining Time",
|
||
kickUtilTime: "Offline Time",
|
||
deviceNo: "Limit Devices",
|
||
onlineStatus: "Online Status",
|
||
online: "Online",
|
||
offline: "Offline",
|
||
device: "Online Devices",
|
||
unit: "Unit",
|
||
loginAt: "Last login time",
|
||
conAt: "Last connection time",
|
||
createTime: "Create Time",
|
||
releaseSuccess: "Release successful",
|
||
kick: "Ban temporarily",
|
||
kickTip:
|
||
"Drop the session and refuse new connections until the ban expires",
|
||
kickConfirm:
|
||
"Ban peer “{name}” until {until}? The current session will be dropped and new connections refused until then.",
|
||
kickSuccess: "Peer banned temporarily",
|
||
releaseKick: "Lift ban",
|
||
releaseKickTip: "Lift the temporary ban early",
|
||
// Disabling and the temporary ban are separate mechanisms: the ban expires
|
||
// on its own, disabling is only undone by hand.
|
||
enablePeer: "Enable peer",
|
||
disablePeer: "Disable peer",
|
||
disableConfirm:
|
||
"Disable peer “{name}”? The current session will be dropped and new connections refused until the peer is enabled again.",
|
||
enableSuccess: "Peer enabled",
|
||
disableSuccess: "Peer disabled",
|
||
createdAt: "Created at",
|
||
bannedUntil: "Banned until",
|
||
totalTraffic: "Total traffic",
|
||
copyUri: "Copy URI",
|
||
more: "More",
|
||
},
|
||
config: {
|
||
orchestratorManaged: "Managed by hy2xs-orchestrator reconfigure",
|
||
resetTrafficCron: "Reset traffic schedule task",
|
||
resetTrafficCronTip:
|
||
"Scheduled task expression, reference: https://pkg.go.dev/github.com/robfig/cron/v3. An empty value disables the automatic reset",
|
||
resetTrafficMonth: "Run once a month, midnight, first of month",
|
||
resetTrafficWeek: "Run once a week, midnight between Sat/Sun",
|
||
},
|
||
monitor: {
|
||
cpuPercent: "CPU Usage",
|
||
memPercent: "Memory Usage",
|
||
diskPercent: "Disk Usage",
|
||
hysteria2UserTotal: "Number of online users",
|
||
hysteria2DeviceTotal: "Number of online devices",
|
||
hysteria2Version: "Hysteria2 Version",
|
||
hysteria2Running: "Hysteria2 Status",
|
||
hysteria2RunningTrue: "Running",
|
||
hysteria2RunningFalse: "Stop",
|
||
},
|
||
log: {
|
||
numLine: "Number of lines",
|
||
level: "Level",
|
||
message: "Message",
|
||
time: "Time",
|
||
exportFailed: "Could not export the log",
|
||
},
|
||
errorPage: {
|
||
back: "Back",
|
||
oops: "Oops!",
|
||
forbiddenTitle: "You do not have permission to access this page",
|
||
forbiddenGo: "Or you can go:",
|
||
backHome: "Back to Home",
|
||
forbiddenImageAlt: "Girl has dropped her ice cream.",
|
||
notFoundHeadline: "The webmaster said that you can not enter this page...",
|
||
notFoundInfo:
|
||
"Please check that the URL you entered is correct, or click the button below to return to the homepage.",
|
||
notFoundBackHome: "Back to home",
|
||
},
|
||
hysteria: {
|
||
enable: "Enable",
|
||
disable: "Disable",
|
||
addConfigItem: "Add Config Item",
|
||
hysteria2Version: "Hysteria2 Version",
|
||
hysteria2Running: "Hysteria2 Status",
|
||
outboundsEmpty: "No outbounds are configured on the server",
|
||
listen: "Listen",
|
||
tls: "TLS",
|
||
obfs: "Obfuscation",
|
||
quic: "QUIC parameters",
|
||
bandwidth: "Bandwidth",
|
||
congestion: "Congestion control",
|
||
speedTest: "Speed Test",
|
||
udp: "UDP",
|
||
resolver: "Resolver",
|
||
sniff: "Protocol Sniffing",
|
||
acl: "ACL",
|
||
outbounds: "Outbounds",
|
||
http: "Traffic Stats API (HTTP)",
|
||
masquerade: "Masquerade",
|
||
config: {
|
||
listen:
|
||
"When the IP address is omitted, the server will listen on all interfaces, both IPv4 and IPv6. To listen on IPv4 only, you can use 0.0.0.0:443. To listen on IPv6 only, you can use [::]:443.",
|
||
tlsType: "TLS type",
|
||
tls: {
|
||
cert: "The path to the Cert file.",
|
||
key: "The path to the Key file.",
|
||
sniGuard:
|
||
'Verify the SNI provided by the client. Accept the connection only when it matches what\'s in the certificate. Terminate the TLS handshake otherwise. Set to strict to enforce this behavior. Set to disable to disable this entirely. The default is dns-san, which enables this feature only when the certificate contains the "Subject Alternative Name" extension with a domain name in it.',
|
||
},
|
||
acme: {
|
||
domains: "Domains",
|
||
email: "Email",
|
||
ca: "The CA to use. Can be letsencrypt or zerossl.",
|
||
listenHost:
|
||
"The host address (not including the port) to listen on for the ACME challenge. If omitted, the server will listen on all interfaces.",
|
||
dir: "The directory to store the ACME account key and certificates.",
|
||
type: "ACME challenge type. Can be http, tls, or dns.",
|
||
http: {
|
||
altPort:
|
||
"Listening port for HTTP challenges. (Note: Changing to a port other than 80 requires port forwarding or HTTP reverse proxy, or the challenge will fail!)",
|
||
},
|
||
tls: {
|
||
altPort:
|
||
"Listening port for TLS-ALPN challenges. (Note: Changing to a port other than 443 requires port forwarding or TLS reverse proxy, or the challenge will fail!)",
|
||
},
|
||
dns: {
|
||
name: "DNS provider. For details, refer to ACME DNS Configuration.",
|
||
config: "ACME DNS Configuration",
|
||
},
|
||
disableHTTP: "Disable HTTP challenge.",
|
||
disableTLSALPN: "Disable TLS-ALPN challenge.",
|
||
altHTTPPort:
|
||
"Alternate HTTP challenge port. (Note: If you want to use anything other than 80, you must set up port forward/HTTP reverse proxy from 80 to that port, otherwise ACME will not be able to issue the certificate.)",
|
||
altTLSALPNPort:
|
||
"Alternate TLS-ALPN challenge port. (Note: If you want to use anything other than 443, you must set up port forward/SNI proxy from 443 to that port, otherwise ACME will not be able to issue the certificate.)",
|
||
},
|
||
obfs: {
|
||
type: "Obfuscation type: gecko (HY2XS production default) or salamander (compatibility fallback).",
|
||
salamander: {
|
||
password: "Replace with a strong password of your choice.",
|
||
},
|
||
gecko: {
|
||
password: "Replace with a strong password of your choice.",
|
||
minPacketSize:
|
||
"Minimum QUIC handshake fragment size. HY2XS baseline: 512.",
|
||
maxPacketSize:
|
||
"Maximum QUIC handshake fragment size. HY2XS baseline: 1200, upstream limit: 2048.",
|
||
},
|
||
},
|
||
quic: {
|
||
initStreamReceiveWindow: "The initial QUIC stream receive window size.",
|
||
maxStreamReceiveWindow: "The maximum QUIC stream receive window size.",
|
||
initConnReceiveWindow:
|
||
"The initial QUIC connection receive window size.",
|
||
maxConnReceiveWindow:
|
||
"The maximum QUIC connection receive window size.",
|
||
maxIdleTimeout:
|
||
"The maximum idle timeout. How long the server will consider the client still connected without any activity.",
|
||
maxIncomingStreams:
|
||
"The maximum number of concurrent incoming streams.",
|
||
disablePathMTUDiscovery: "Disable QUIC path MTU discovery.",
|
||
disableStatelessReset:
|
||
"Disable QUIC stateless reset. Kept off in the HY2XS baseline: stateless reset lets a client with a stale connection reconnect immediately after a server restart or device sleep.",
|
||
},
|
||
bandwidth: {
|
||
up: "Up",
|
||
down: "Down",
|
||
disableLossCompensation:
|
||
"Disable loss compensation. Kept off in the HY2XS baseline, so compensation stays active.",
|
||
},
|
||
congestion: {
|
||
type: "Fallback congestion controller: bbr or reno. Used when Brutal bandwidth is not negotiated by both sides.",
|
||
bbrProfile: "BBR profile: standard, conservative or aggressive.",
|
||
},
|
||
ignoreClientBandwidth:
|
||
"When enabled, makes the server to disregard any bandwidth hints set by clients",
|
||
speedTest:
|
||
"speedTest enables the built-in speed test server. When enabled, clients can test their download and upload speeds with the server. For more information, see the Speed Test documentation.",
|
||
disableUDP:
|
||
"disableUDP disables UDP forwarding, only allowing TCP connections.",
|
||
udpIdleTimeout:
|
||
"udpIdleTimeout specifies the amount of time the server will keep a local UDP port open for each UDP session that has no activity. This is conceptually similar to the NAT UDP session timeout.",
|
||
resolver: {
|
||
type: "Type",
|
||
tcp: {
|
||
addr: "The address of the TCP resolver.",
|
||
timeout: "The timeout for DNS queries.",
|
||
},
|
||
udp: {
|
||
addr: "The address of the UDP resolver.",
|
||
timeout: "The timeout for DNS queries.",
|
||
},
|
||
tls: {
|
||
addr: "The address of the TLS resolver.",
|
||
timeout: "The timeout for DNS queries.",
|
||
sni: "The SNI to use for the TLS resolver.",
|
||
insecure: "Disable TLS verification for the TLS resolver.",
|
||
},
|
||
https: {
|
||
addr: "The address of the HTTPS resolver.",
|
||
timeout: "The timeout for DNS queries.",
|
||
sni: "The SNI to use for the TLS resolver.",
|
||
insecure: "Disable TLS verification for the TLS resolver.",
|
||
},
|
||
},
|
||
sniff: {
|
||
enable: "Whether to enable protocol sniffing.",
|
||
timeout:
|
||
"Sniffing timeout. If the protocol/domain cannot be determined within this time, the original address will be used to initiate the connection.",
|
||
rewriteDomain:
|
||
"Whether to rewrite requests that are already in domain name form. If enabled, requests with the target address already in domain name form will still be sniffed.",
|
||
tcpPorts:
|
||
"List of TCP ports. Only TCP requests on these ports will be sniffed.",
|
||
udpPorts:
|
||
"List of UDP ports. Only UDP requests on these ports will be sniffed.",
|
||
},
|
||
aclType: "ACL type",
|
||
acl: {
|
||
file: "The path to the ACL file.",
|
||
inline: "The list of inline ACL rules.",
|
||
geoip:
|
||
"Optional. Uncomment to enable. The path to the GeoIP database file. If this field is omitted, Hysteria will automatically download the latest database to your working directory.",
|
||
geosite:
|
||
"Optional. Uncomment to enable. The path to the GeoSite database file. If this field is omitted, Hysteria will automatically download the latest database to your working directory.",
|
||
geoUpdateInterval:
|
||
"Optional. The interval at which to refresh the GeoIP/GeoSite databases. 168 hours (1 week) by default. Only applies if the GeoIP/GeoSite databases are automatically downloaded. (Check the note below for more information.)",
|
||
},
|
||
outbounds: {
|
||
name: "The name of the outbound. This is used in ACL rules.",
|
||
type: "Type",
|
||
socks5: {
|
||
addr: "The address of the SOCKS5 proxy.",
|
||
username:
|
||
"Optional. The username for the SOCKS5 proxy, if authentication is required.",
|
||
password:
|
||
"Optional. The password for the SOCKS5 proxy, if authentication is required.",
|
||
},
|
||
http: {
|
||
url: "The URL of the HTTP/HTTPS proxy. (Can be http:// or https://)",
|
||
insecure:
|
||
"Optional. Whether to disable TLS verification. Applies to HTTPS proxies only.",
|
||
},
|
||
direct: {
|
||
mode: "Type",
|
||
bindIPv4: "The local IPv4 address to bind to.",
|
||
bindIPv6: "The local IPv6 address to bind to.",
|
||
bindDevice: "The local network interface to bind to.",
|
||
fastOpen: "Enable TCP fast open.",
|
||
},
|
||
},
|
||
trafficStats: {
|
||
listen: "The address to listen on.",
|
||
},
|
||
masquerade: {
|
||
type: "Type",
|
||
file: {
|
||
dir: "The directory to serve files from.",
|
||
},
|
||
proxy: {
|
||
url: "The URL of the website to proxy.",
|
||
rewriteHost:
|
||
"Whether to rewrite the Host header to match the proxied website. This is required if the target web server uses Host to determine which site to serve.",
|
||
insecure: "Disable TLS verification for the proxied website.",
|
||
},
|
||
string: {
|
||
content: "The string to return.",
|
||
headers: "Optional. The headers to return.",
|
||
statusCode: "Optional. The status code to return. 200 by default.",
|
||
},
|
||
listenHTTP: "HTTP (TCP) listen address.",
|
||
listenHTTPS: "HTTPS (TCP) listen address.",
|
||
forceHTTPS:
|
||
"Whether to force HTTPS. If enabled, all HTTP requests will be redirected to HTTPS.",
|
||
},
|
||
},
|
||
},
|
||
};
|