refactor(admin): удалить мёртвый updater/config-write API и его UI
Маршруты, операциями которых продукт не владеет, отвечали заглушкой "managed by orchestrator" или пустым списком: POST /hysteria2ChangeVersion GET /listRelease POST /config/updateHysteria2Config POST /config/importHysteria2Config POST /config/restartServer POST /config/uploadCertFile GET /config/hysteria2AcmePath (не имел потребителя вовсе) Они удалены, а не оставлены заглушками. Причины две. API-контракт не должен обещать updater, которого у продукта принципиально нет: маршрут, всегда возвращающий отказ, вводит в заблуждение. И это лишняя attack surface плюс технический мусор от прежней архитектуры. Вместе с маршрутами убраны мёртвые сервисы (StartHysteria2, StopHysteria2, RestartHysteria2, SetHysteria2Config, UpdateHysteria2Config, GetAuthHttpUrl, Hysteria2AcmePath), неиспользуемые типы и клиентские функции фронтенда. Отдельно - кнопки. "Перезапустить панель" и загрузка сертификатов обращались к заглушкам, то есть гарантированно возвращали ошибку. Кнопка, которая всегда падает, - не точка расширения на будущее, а дефект UX. Удалены вместе со строками i18n. Конфигурация Hysteria остаётся доступной панели на чтение и на выгрузку: getHysteria2Config и exportHysteria2Config.
This commit is contained in:
@@ -176,10 +176,6 @@ func GetHysteria2Config(c *gin.Context) {
|
|||||||
vo.Success(config, c)
|
vo.Success(config, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateHysteria2Config(c *gin.Context) {
|
|
||||||
vo.Fail("managed by orchestrator: use hy2xs-orchestrator reconfigure", c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportHysteria2Config отдаёт оператору фактический серверный конфиг.
|
// ExportHysteria2Config отдаёт оператору фактический серверный конфиг.
|
||||||
//
|
//
|
||||||
// Экспорт работает от исходного YAML, а не от типизированной модели: поля,
|
// Экспорт работает от исходного YAML, а не от типизированной модели: поля,
|
||||||
@@ -200,10 +196,6 @@ func ExportHysteria2Config(c *gin.Context) {
|
|||||||
c.Data(200, "application/octet-stream", sanitized)
|
c.Data(200, "application/octet-stream", sanitized)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ImportHysteria2Config(c *gin.Context) {
|
|
||||||
vo.Fail("managed by orchestrator: use hy2xs-orchestrator reconfigure", c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExportConfig(c *gin.Context) {
|
func ExportConfig(c *gin.Context) {
|
||||||
configs, err := service.ListConfigNotIn([]string{constant.Hysteria2Config})
|
configs, err := service.ListConfigNotIn([]string{constant.Hysteria2Config})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -267,20 +259,3 @@ func ImportConfig(c *gin.Context) {
|
|||||||
}()
|
}()
|
||||||
vo.Success(nil, c)
|
vo.Success(nil, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Hysteria2AcmePath(c *gin.Context) {
|
|
||||||
hysteria2AcmePathVo, err := service.Hysteria2AcmePath()
|
|
||||||
if err != nil {
|
|
||||||
vo.Fail(err.Error(), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vo.Success(hysteria2AcmePathVo, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RestartServer(c *gin.Context) {
|
|
||||||
vo.Fail("managed by orchestrator: use hy2xs-orchestrator reconfigure", c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func UploadCertFile(c *gin.Context) {
|
|
||||||
vo.Fail("managed by orchestrator: use hy2xs-orchestrator reconfigure", c)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -32,11 +32,3 @@ func Hysteria2Auth(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
vo.Hysteria2AuthSuccess(username, c)
|
vo.Hysteria2AuthSuccess(username, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Hysteria2ChangeVersion(c *gin.Context) {
|
|
||||||
vo.Fail("Смена версии Hysteria2 отключена: runtime управляется install-оркестратором HY2XS", c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListRelease(c *gin.Context) {
|
|
||||||
vo.Success([]string{}, c)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,10 +5,13 @@ import {
|
|||||||
ConfigsDto,
|
ConfigsDto,
|
||||||
ConfigUpdateDto,
|
ConfigUpdateDto,
|
||||||
ConfigVo,
|
ConfigVo,
|
||||||
Hysteria2AcmePathVo,
|
|
||||||
Hysteria2ServerConfig,
|
Hysteria2ServerConfig,
|
||||||
} from "@/api/config/types";
|
} from "@/api/config/types";
|
||||||
|
|
||||||
|
// Серверный конфиг Hysteria доступен панели только на чтение и на выгрузку:
|
||||||
|
// им владеет install-оркестратор. Клиентов записи, импорта, перезапуска и
|
||||||
|
// загрузки сертификатов здесь нет — соответствующих маршрутов не существует.
|
||||||
|
|
||||||
export function getHysteria2ConfigApi(): AxiosPromise<Hysteria2ServerConfig> {
|
export function getHysteria2ConfigApi(): AxiosPromise<Hysteria2ServerConfig> {
|
||||||
return request({
|
return request({
|
||||||
url: "/config/getHysteria2Config",
|
url: "/config/getHysteria2Config",
|
||||||
@@ -16,16 +19,6 @@ export function getHysteria2ConfigApi(): AxiosPromise<Hysteria2ServerConfig> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateHysteria2ConfigApi(
|
|
||||||
data: Hysteria2ServerConfig
|
|
||||||
): AxiosPromise {
|
|
||||||
return request({
|
|
||||||
url: "/config/updateHysteria2Config",
|
|
||||||
method: "post",
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getConfigApi(data: ConfigDto): AxiosPromise<ConfigVo> {
|
export function getConfigApi(data: ConfigDto): AxiosPromise<ConfigVo> {
|
||||||
return request({
|
return request({
|
||||||
url: "/config/getConfig",
|
url: "/config/getConfig",
|
||||||
@@ -77,36 +70,3 @@ export function exportHysteria2ConfigApi(): AxiosPromise {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -340,8 +340,3 @@ export interface Tab {
|
|||||||
name: string;
|
name: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Hysteria2AcmePathVo {
|
|
||||||
crtPath: string;
|
|
||||||
keyPath: string;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -185,10 +185,6 @@ export default {
|
|||||||
hysteria2TrafficTime: "Hysteria2 Traffic Time",
|
hysteria2TrafficTime: "Hysteria2 Traffic Time",
|
||||||
huiCrtPath: "HY2XS admin CRT File Path",
|
huiCrtPath: "HY2XS admin CRT File Path",
|
||||||
huiKeyPath: "HY2XS admin KEY File Path",
|
huiKeyPath: "HY2XS admin KEY File Path",
|
||||||
uploadCrtFile: "Upload CRT File",
|
|
||||||
uploadKeyFile: "Upload KEY File",
|
|
||||||
restartServer: "Restart Panel",
|
|
||||||
restartTip: "Restarting, please refresh",
|
|
||||||
useHysteria2Cert: "Use Hysteria2 cert",
|
useHysteria2Cert: "Use Hysteria2 cert",
|
||||||
huiHttps: "Open https on the panel",
|
huiHttps: "Open https on the panel",
|
||||||
resetTrafficCron: "Reset traffic schedule task",
|
resetTrafficCron: "Reset traffic schedule task",
|
||||||
@@ -237,7 +233,6 @@ export default {
|
|||||||
addConfigItem: "Add Config Item",
|
addConfigItem: "Add Config Item",
|
||||||
hysteria2Version: "Hysteria2 Version",
|
hysteria2Version: "Hysteria2 Version",
|
||||||
hysteria2Running: "Hysteria2 Status",
|
hysteria2Running: "Hysteria2 Status",
|
||||||
hysteria2ChangeVersion: "Change",
|
|
||||||
addOutbound: "Add Outbound",
|
addOutbound: "Add Outbound",
|
||||||
extension: "Extension",
|
extension: "Extension",
|
||||||
listen: "Listen",
|
listen: "Listen",
|
||||||
|
|||||||
@@ -181,10 +181,6 @@ export default {
|
|||||||
hysteria2TrafficTime: "Период учёта трафика Hysteria2",
|
hysteria2TrafficTime: "Период учёта трафика Hysteria2",
|
||||||
huiCrtPath: "Путь к CRT HY2XS admin",
|
huiCrtPath: "Путь к CRT HY2XS admin",
|
||||||
huiKeyPath: "Путь к KEY HY2XS admin",
|
huiKeyPath: "Путь к KEY HY2XS admin",
|
||||||
uploadCrtFile: "Загрузить CRT",
|
|
||||||
uploadKeyFile: "Загрузить KEY",
|
|
||||||
restartServer: "Перезапустить панель",
|
|
||||||
restartTip: "Перезапуск, обновите страницу",
|
|
||||||
useHysteria2Cert: "Использовать сертификат Hysteria2",
|
useHysteria2Cert: "Использовать сертификат Hysteria2",
|
||||||
huiHttps: "Включить HTTPS панели",
|
huiHttps: "Включить HTTPS панели",
|
||||||
resetTrafficCron: "Расписание сброса трафика",
|
resetTrafficCron: "Расписание сброса трафика",
|
||||||
@@ -233,7 +229,6 @@ export default {
|
|||||||
addConfigItem: "Добавить параметр",
|
addConfigItem: "Добавить параметр",
|
||||||
hysteria2Version: "Версия Hysteria2",
|
hysteria2Version: "Версия Hysteria2",
|
||||||
hysteria2Running: "Статус Hysteria2",
|
hysteria2Running: "Статус Hysteria2",
|
||||||
hysteria2ChangeVersion: "Сменить",
|
|
||||||
addOutbound: "Добавить outbound",
|
addOutbound: "Добавить outbound",
|
||||||
extension: "Расширение",
|
extension: "Расширение",
|
||||||
listen: "Адрес прослушивания",
|
listen: "Адрес прослушивания",
|
||||||
|
|||||||
@@ -7,14 +7,6 @@
|
|||||||
{{ $t("common.save") }}
|
{{ $t("common.save") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="handleRestartServer">
|
|
||||||
<template #icon>
|
|
||||||
<i-ep-refreshRight />
|
|
||||||
</template>
|
|
||||||
{{ $t("config.restartServer") }}
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-upload
|
<el-upload
|
||||||
v-model:file-list="fileList"
|
v-model:file-list="fileList"
|
||||||
@@ -166,7 +158,6 @@ import {
|
|||||||
exportConfigApi,
|
exportConfigApi,
|
||||||
importConfigApi,
|
importConfigApi,
|
||||||
listConfigApi,
|
listConfigApi,
|
||||||
restartServerApi,
|
|
||||||
updateConfigsApi,
|
updateConfigsApi,
|
||||||
} from "@/api/config";
|
} from "@/api/config";
|
||||||
import { ConfigsUpdateDto } from "@/api/config/types";
|
import { ConfigsUpdateDto } from "@/api/config/types";
|
||||||
@@ -352,21 +343,6 @@ const handleExport = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRestartServer = async () => {
|
|
||||||
try {
|
|
||||||
ElMessageBox.confirm(t("config.restartTip"), t("common.warning"), {
|
|
||||||
confirmButtonText: t("common.confirm"),
|
|
||||||
cancelButtonText: t("common.cancel"),
|
|
||||||
type: "warning",
|
|
||||||
}).then(() => {
|
|
||||||
restartServerApi();
|
|
||||||
ElMessage.success(t("config.restartTip"));
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
/* empty */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setConfig();
|
setConfig();
|
||||||
if (route.query.focus === "huiHttps") {
|
if (route.query.focus === "huiHttps") {
|
||||||
|
|||||||
@@ -95,28 +95,9 @@
|
|||||||
<el-form-item label="cert" prop="tls.cert">
|
<el-form-item label="cert" prop="tls.cert">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="dataForm.tls.cert"
|
v-model="dataForm.tls.cert"
|
||||||
style="width: 50%"
|
style="width: 100%"
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
<el-upload
|
|
||||||
style="height: 32px"
|
|
||||||
ref="uploadCrtFile"
|
|
||||||
action=""
|
|
||||||
:file-list="crtFileList"
|
|
||||||
:http-request="uploadCertFile"
|
|
||||||
accept=".crt"
|
|
||||||
:before-upload="
|
|
||||||
() => {
|
|
||||||
crtFileList = [];
|
|
||||||
}
|
|
||||||
"
|
|
||||||
:show-file-list="false"
|
|
||||||
:limit="1"
|
|
||||||
>
|
|
||||||
<template #trigger>
|
|
||||||
<el-button>{{ t("config.uploadCrtFile") }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-upload>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
@@ -127,28 +108,9 @@
|
|||||||
<el-form-item label="key" prop="tls.key">
|
<el-form-item label="key" prop="tls.key">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="dataForm.tls.key"
|
v-model="dataForm.tls.key"
|
||||||
style="width: 50%"
|
style="width: 100%"
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
<el-upload
|
|
||||||
style="height: 32px"
|
|
||||||
ref="uploadKeyFile"
|
|
||||||
action=""
|
|
||||||
:file-list="keyFileList"
|
|
||||||
:http-request="uploadCertFile"
|
|
||||||
accept=".key"
|
|
||||||
:before-upload="
|
|
||||||
() => {
|
|
||||||
keyFileList = [];
|
|
||||||
}
|
|
||||||
"
|
|
||||||
:show-file-list="false"
|
|
||||||
:limit="1"
|
|
||||||
>
|
|
||||||
<template #trigger>
|
|
||||||
<el-button>{{ t("config.uploadKeyFile") }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-upload>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
@@ -1104,13 +1066,10 @@ import {
|
|||||||
exportHysteria2ConfigApi,
|
exportHysteria2ConfigApi,
|
||||||
getHysteria2ConfigApi,
|
getHysteria2ConfigApi,
|
||||||
listConfigApi,
|
listConfigApi,
|
||||||
uploadCertFileApi,
|
|
||||||
} from "@/api/config";
|
} from "@/api/config";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { assignWith, deepCopy } from "@/utils/copy";
|
import { assignWith, deepCopy } from "@/utils/copy";
|
||||||
import { UploadRequestOptions } from "element-plus";
|
|
||||||
import { dashboardSummaryApi } from "@/api/dashboard";
|
import { dashboardSummaryApi } from "@/api/dashboard";
|
||||||
import { UploadUserFile } from "element-plus";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -1181,8 +1140,6 @@ const state = reactive({
|
|||||||
version: "",
|
version: "",
|
||||||
running: false,
|
running: false,
|
||||||
},
|
},
|
||||||
crtFileList: [] as UploadUserFile[],
|
|
||||||
keyFileList: [] as UploadUserFile[],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -1203,8 +1160,6 @@ const {
|
|||||||
outbounds,
|
outbounds,
|
||||||
masquerade,
|
masquerade,
|
||||||
hysteria2Monitor,
|
hysteria2Monitor,
|
||||||
crtFileList,
|
|
||||||
keyFileList,
|
|
||||||
} = toRefs(state);
|
} = toRefs(state);
|
||||||
|
|
||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
@@ -1287,33 +1242,6 @@ const setHysteria2Monitor = async () => {
|
|||||||
state.hysteria2Monitor.running = data.hysteria.running;
|
state.hysteria2Monitor.running = data.hysteria.running;
|
||||||
};
|
};
|
||||||
|
|
||||||
const uploadCertFile = async (params: UploadRequestOptions) => {
|
|
||||||
try {
|
|
||||||
if (!state.dataForm.tls) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!params.file.name.endsWith(".crt") &&
|
|
||||||
!params.file.name.endsWith(".key")
|
|
||||||
) {
|
|
||||||
ElMessage.error(t("common.fileFormatUnsupported"));
|
|
||||||
}
|
|
||||||
if (params.file.size > 1024 * 1024) {
|
|
||||||
ElMessage.error(t("common.fileTooLarge"));
|
|
||||||
}
|
|
||||||
let formData = new FormData();
|
|
||||||
formData.append("file", params.file);
|
|
||||||
const { data } = await uploadCertFileApi(formData);
|
|
||||||
if (params.file.name.endsWith(".crt")) {
|
|
||||||
state.dataForm.tls.cert = data;
|
|
||||||
} else if (params.file.name.endsWith(".key")) {
|
|
||||||
state.dataForm.tls.key = data;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
/* empty */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setConfig();
|
setConfig();
|
||||||
setHysteria2Monitor();
|
setHysteria2Monitor();
|
||||||
|
|||||||
@@ -5,7 +5,3 @@ type Hysteria2AuthDto struct {
|
|||||||
Auth *string `json:"auth" form:"auth" validate:"required"`
|
Auth *string `json:"auth" form:"auth" validate:"required"`
|
||||||
Tx *int64 `json:"tx" form:"tx" validate:"required"`
|
Tx *int64 `json:"tx" form:"tx" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hysteria2VersionDto struct {
|
|
||||||
Version *string `json:"version" form:"version" validate:"required,min=1,max=10"`
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -30,13 +30,3 @@ func Hysteria2AuthBadRequest(c *gin.Context) {
|
|||||||
Id: "",
|
Id: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hysteria2UrlVo struct {
|
|
||||||
Url string `json:"url"`
|
|
||||||
QrCode []byte `json:"qrCode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Hysteria2AcmePathVo struct {
|
|
||||||
CrtPath string `json:"crtPath"`
|
|
||||||
KeyPath string `json:"keyPath"`
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ import (
|
|||||||
"hy2xs-admin/controller"
|
"hy2xs-admin/controller"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Серверный конфиг Hysteria в HY2XS доступен только на чтение и на выгрузку:
|
||||||
|
// его владелец — install-оркестратор. Маршруты записи (updateHysteria2Config,
|
||||||
|
// importHysteria2Config), перезапуска панели и загрузки сертификатов удалены,
|
||||||
|
// а не оставлены заглушками: маршрут, который всегда отвечает «feature
|
||||||
|
// disabled», вводит в заблуждение и остаётся точкой входа.
|
||||||
func initConfigRouter(configApi *gin.RouterGroup) {
|
func initConfigRouter(configApi *gin.RouterGroup) {
|
||||||
config := configApi.Group("/config")
|
config := configApi.Group("/config")
|
||||||
{
|
{
|
||||||
@@ -12,13 +17,8 @@ func initConfigRouter(configApi *gin.RouterGroup) {
|
|||||||
config.GET("/getConfig", controller.GetConfig)
|
config.GET("/getConfig", controller.GetConfig)
|
||||||
config.POST("/listConfig", controller.ListConfig)
|
config.POST("/listConfig", controller.ListConfig)
|
||||||
config.GET("/getHysteria2Config", controller.GetHysteria2Config)
|
config.GET("/getHysteria2Config", controller.GetHysteria2Config)
|
||||||
config.POST("/updateHysteria2Config", controller.UpdateHysteria2Config)
|
|
||||||
config.POST("/exportHysteria2Config", controller.ExportHysteria2Config)
|
config.POST("/exportHysteria2Config", controller.ExportHysteria2Config)
|
||||||
config.POST("/importHysteria2Config", controller.ImportHysteria2Config)
|
|
||||||
config.POST("/exportConfig", controller.ExportConfig)
|
config.POST("/exportConfig", controller.ExportConfig)
|
||||||
config.POST("/importConfig", controller.ImportConfig)
|
config.POST("/importConfig", controller.ImportConfig)
|
||||||
config.GET("/hysteria2AcmePath", controller.Hysteria2AcmePath)
|
|
||||||
config.POST("/restartServer", controller.RestartServer)
|
|
||||||
config.POST("/uploadCertFile", controller.UploadCertFile)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,13 @@ import (
|
|||||||
"hy2xs-admin/controller"
|
"hy2xs-admin/controller"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Панель HY2XS не управляет жизненным циклом Hysteria: установка, обновление
|
||||||
|
// и перезапуск выполняются install-оркестратором. Маршрутов updater'а здесь
|
||||||
|
// нет намеренно — API-контракт не должен обещать операцию, которой у продукта
|
||||||
|
// принципиально не существует, и не должен нести лишнюю attack surface.
|
||||||
func initHysteria2MachineAuthRouter(hysteria2Api *gin.RouterGroup) {
|
func initHysteria2MachineAuthRouter(hysteria2Api *gin.RouterGroup) {
|
||||||
hysteria2 := hysteria2Api.Group("/hysteria2")
|
hysteria2 := hysteria2Api.Group("/hysteria2")
|
||||||
{
|
{
|
||||||
hysteria2.POST("/auth", controller.Hysteria2Auth)
|
hysteria2.POST("/auth", controller.Hysteria2Auth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initHysteria2Router(hysteria2Api *gin.RouterGroup) {
|
|
||||||
hysteria2 := hysteria2Api.Group("/hysteria2")
|
|
||||||
{
|
|
||||||
hysteria2.POST("/hysteria2ChangeVersion", controller.Hysteria2ChangeVersion)
|
|
||||||
hysteria2.GET("/listRelease", controller.ListRelease)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -65,6 +65,5 @@ func Router(router *gin.Engine, huiWebContext *string) {
|
|||||||
initDashboardRouter(huiAdminApi)
|
initDashboardRouter(huiAdminApi)
|
||||||
initPeerRouter(huiAdminApi)
|
initPeerRouter(huiAdminApi)
|
||||||
initConfigRouter(huiAdminApi)
|
initConfigRouter(huiAdminApi)
|
||||||
initHysteria2Router(huiAdminApi)
|
|
||||||
initLogRouter(huiAdminApi)
|
initLogRouter(huiAdminApi)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"hy2xs-admin/model/constant"
|
"hy2xs-admin/model/constant"
|
||||||
"hy2xs-admin/model/entity"
|
"hy2xs-admin/model/entity"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -54,62 +53,6 @@ func GetHysteria2Config() (bo.Hysteria2ServerConfig, error) {
|
|||||||
return serverConfig, nil
|
return serverConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateHysteria2Config(hysteria2ServerConfig bo.Hysteria2ServerConfig) error {
|
|
||||||
// Значения по умолчанию
|
|
||||||
config, err := dao.ListConfig("key in ?", []string{constant.HUIWebPort, constant.Hysteria2TrafficStatsSecret})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var hUIWebPort string
|
|
||||||
var trafficStatsSecret string
|
|
||||||
for _, item := range config {
|
|
||||||
if *item.Key == constant.HUIWebPort {
|
|
||||||
hUIWebPort = *item.Value
|
|
||||||
} else if *item.Key == constant.Hysteria2TrafficStatsSecret {
|
|
||||||
trafficStatsSecret = *item.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hUIWebPort == "" || trafficStatsSecret == "" {
|
|
||||||
logrus.Errorf("hUIWebPort or trafficStatsSecret is nil")
|
|
||||||
return errors.New(constant.SysError)
|
|
||||||
}
|
|
||||||
|
|
||||||
authHttpUrl, err := GetAuthHttpUrl()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
authType := "http"
|
|
||||||
authHttpInsecure := true
|
|
||||||
var auth bo.ServerConfigAuth
|
|
||||||
auth.Type = &authType
|
|
||||||
var http bo.ServerConfigAuthHTTP
|
|
||||||
http.URL = &authHttpUrl
|
|
||||||
http.Insecure = &authHttpInsecure
|
|
||||||
auth.HTTP = &http
|
|
||||||
hysteria2ServerConfig.Auth = &auth
|
|
||||||
if hysteria2ServerConfig.TrafficStats == nil {
|
|
||||||
hysteria2ServerConfig.TrafficStats = &bo.ServerConfigTrafficStats{}
|
|
||||||
}
|
|
||||||
hysteria2ServerConfig.TrafficStats.Secret = &trafficStatsSecret
|
|
||||||
|
|
||||||
yamlConfig, err := yaml.Marshal(&hysteria2ServerConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return dao.UpdateConfig([]string{constant.Hysteria2Config}, map[string]interface{}{"value": string(yamlConfig)})
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetHysteria2Config(hysteria2ServerConfig bo.Hysteria2ServerConfig) error {
|
|
||||||
config, err := yaml.Marshal(&hysteria2ServerConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return dao.UpdateConfig([]string{constant.Hysteria2Config}, map[string]interface{}{"value": string(config)})
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpsertConfig(configs []entity.Config) error {
|
func UpsertConfig(configs []entity.Config) error {
|
||||||
return dao.UpsertConfig(configs)
|
return dao.UpsertConfig(configs)
|
||||||
}
|
}
|
||||||
@@ -185,31 +128,3 @@ func GetPortAndCert() (int64, string, string, error) {
|
|||||||
|
|
||||||
return portInt, crtPath, keyPath, nil
|
return portInt, crtPath, keyPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAuthHttpUrl() (string, error) {
|
|
||||||
port, crtPath, keyPath, err := GetPortAndCert()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
protocol := "http"
|
|
||||||
if crtPath != "" && keyPath != "" {
|
|
||||||
protocol = "https"
|
|
||||||
}
|
|
||||||
config, err := dao.GetConfig("key = ?", constant.HUIWebContext)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
webContext := ""
|
|
||||||
if config.Value != nil && *config.Value != "/" && strings.HasPrefix(*config.Value, "/") {
|
|
||||||
webContext = *config.Value
|
|
||||||
}
|
|
||||||
trafficSecretConfig, err := dao.GetConfig("key = ?", constant.Hysteria2TrafficStatsSecret)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
authURL := fmt.Sprintf("%s://127.0.0.1:%d%s/hui/hysteria2/auth", protocol, port, webContext)
|
|
||||||
if trafficSecretConfig.Value != nil && strings.TrimSpace(*trafficSecretConfig.Value) != "" {
|
|
||||||
authURL = fmt.Sprintf("%s?access_token=%s", authURL, url.QueryEscape(strings.TrimSpace(*trafficSecretConfig.Value)))
|
|
||||||
}
|
|
||||||
return authURL, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,14 +2,17 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"hy2xs-admin/dao"
|
"hy2xs-admin/dao"
|
||||||
"hy2xs-admin/model/constant"
|
"hy2xs-admin/model/constant"
|
||||||
"hy2xs-admin/model/vo"
|
|
||||||
"hy2xs-admin/util"
|
"hy2xs-admin/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Жизненный цикл Hysteria принадлежит systemd и install-оркестратору.
|
||||||
|
// Панель умеет только наблюдать состояние: функций Start/Stop/Restart и
|
||||||
|
// смены версии здесь нет намеренно, а не «временно отключены».
|
||||||
|
|
||||||
func InitHysteria2() error {
|
func InitHysteria2() error {
|
||||||
if !util.Exists(util.GetHysteria2BinPath()) {
|
if !util.Exists(util.GetHysteria2BinPath()) {
|
||||||
return errors.New("systemd-managed hysteria binary not found")
|
return errors.New("systemd-managed hysteria binary not found")
|
||||||
@@ -31,58 +34,6 @@ func Hysteria2IsRunning() bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartHysteria2() error {
|
|
||||||
return errors.New("managed by orchestrator: use hy2xs-orchestrator reconfigure")
|
|
||||||
}
|
|
||||||
|
|
||||||
func StopHysteria2() error {
|
|
||||||
return errors.New("managed by orchestrator: use hy2xs-orchestrator reconfigure")
|
|
||||||
}
|
|
||||||
|
|
||||||
func RestartHysteria2() error {
|
|
||||||
return errors.New("managed by orchestrator: use hy2xs-orchestrator reconfigure")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReleaseHysteria2() error {
|
func ReleaseHysteria2() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Hysteria2AcmePath() (vo.Hysteria2AcmePathVo, error) {
|
|
||||||
hysteria2AcmePathVo := vo.Hysteria2AcmePathVo{}
|
|
||||||
hysteria2Config, err := GetHysteria2Config()
|
|
||||||
if err != nil {
|
|
||||||
return hysteria2AcmePathVo, err
|
|
||||||
}
|
|
||||||
if hysteria2Config.TLS != nil &&
|
|
||||||
hysteria2Config.TLS.Cert != nil && *hysteria2Config.TLS.Cert != "" &&
|
|
||||||
hysteria2Config.TLS.Key != nil && *hysteria2Config.TLS.Key != "" {
|
|
||||||
if util.Exists(*hysteria2Config.TLS.Cert) && util.Exists(*hysteria2Config.TLS.Key) {
|
|
||||||
hysteria2AcmePathVo.CrtPath = *hysteria2Config.TLS.Cert
|
|
||||||
hysteria2AcmePathVo.KeyPath = *hysteria2Config.TLS.Key
|
|
||||||
return hysteria2AcmePathVo, nil
|
|
||||||
}
|
|
||||||
return hysteria2AcmePathVo, errors.New("cert not found")
|
|
||||||
} else if hysteria2Config.ACME != nil &&
|
|
||||||
hysteria2Config.ACME.Domains != nil &&
|
|
||||||
len(hysteria2Config.ACME.Domains) > 0 &&
|
|
||||||
hysteria2Config.ACME.CA != nil &&
|
|
||||||
*hysteria2Config.ACME.CA != "" &&
|
|
||||||
hysteria2Config.ACME.Dir != nil &&
|
|
||||||
*hysteria2Config.ACME.Dir != "" {
|
|
||||||
acmeDir := *hysteria2Config.ACME.Dir
|
|
||||||
for _, domain := range hysteria2Config.ACME.Domains {
|
|
||||||
crtPath, err := util.FindFile(acmeDir, fmt.Sprintf("%s.crt", domain))
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
keyPath, err := util.FindFile(acmeDir, fmt.Sprintf("%s.key", domain))
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hysteria2AcmePathVo.CrtPath = crtPath
|
|
||||||
hysteria2AcmePathVo.KeyPath = keyPath
|
|
||||||
return hysteria2AcmePathVo, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vo.Hysteria2AcmePathVo{}, errors.New("cert not found")
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user