From 2b4a45ad23bc4d52f9ec2eef1c6427633dda544c Mon Sep 17 00:00:00 2001 From: Crimson Date: Sat, 25 Apr 2026 23:51:21 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=B8=D1=84=D0=B8=D1=86?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20README=20=D0=B8=20?= =?UTF-8?q?=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81=20HY2XS?= =?UTF-8?q?=20admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 70 ++-- apps/cmd/cmd.go | 1 + apps/cmd/reset.go | 1 + apps/cmd/server.go | 1 + apps/cmd/version.go | 1 + apps/controller/account.go | 9 +- apps/controller/config.go | 5 +- apps/controller/hysteria2.go | 5 +- apps/controller/log.go | 1 + apps/controller/monitor.go | 1 + apps/controller/validator.go | 3 +- apps/dao/account.go | 1 + apps/dao/config.go | 1 + apps/dao/sqlite.go | 1 + apps/frontend/.editorconfig | 16 +- apps/frontend/.env.development | 4 +- apps/frontend/.env.production | 2 +- apps/frontend/.eslintrc.cjs | 8 +- apps/frontend/.prettierrc.cjs | 34 +- apps/frontend/.stylelintrc.cjs | 20 +- apps/frontend/README.md | 24 +- apps/frontend/embed.go | 1 + apps/frontend/src/App.vue | 2 + apps/frontend/src/api/account/index.ts | 26 +- apps/frontend/src/api/account/types.ts | 2 + apps/frontend/src/api/config/index.ts | 2 + apps/frontend/src/api/config/types.ts | 2 + apps/frontend/src/api/hysteria2/index.ts | 2 + apps/frontend/src/api/hysteria2/types.ts | 2 + apps/frontend/src/api/log/index.ts | 2 + apps/frontend/src/api/log/types.ts | 2 + apps/frontend/src/api/monitor/index.ts | 2 + apps/frontend/src/api/monitor/types.ts | 2 + .../src/components/Hamburger/index.vue | 2 + .../src/components/ImputMultiple/index.vue | 2 + .../src/components/LangSelect/index.vue | 2 + apps/frontend/src/components/MapAdd/index.vue | 2 + .../src/components/Pagination/index.vue | 2 + .../src/components/RightPanel/index.vue | 4 +- .../src/components/SizeSelect/index.vue | 10 +- .../frontend/src/components/SvgIcon/index.vue | 6 +- .../src/components/UnitSelect/index.vue | 2 + apps/frontend/src/directive/index.ts | 6 +- .../src/directive/permission/index.ts | 6 +- apps/frontend/src/lang/index.ts | 2 + apps/frontend/src/lang/package/en.ts | 8 +- apps/frontend/src/lang/package/ru.ts | 2 + apps/frontend/src/lang/package/zh-cn.ts | 312 ------------------ .../src/layout/components/AppMain.vue | 2 + .../frontend/src/layout/components/Navbar.vue | 34 +- .../src/layout/components/Settings/index.vue | 24 +- .../src/layout/components/Sidebar/Link.vue | 2 + .../src/layout/components/Sidebar/Logo.vue | 2 + .../layout/components/Sidebar/SidebarItem.vue | 40 +-- .../src/layout/components/Sidebar/index.vue | 2 + .../layout/components/TagsView/ScrollPane.vue | 2 + .../src/layout/components/TagsView/index.vue | 20 +- apps/frontend/src/layout/components/index.ts | 2 + apps/frontend/src/layout/index.vue | 18 +- apps/frontend/src/main.ts | 12 +- apps/frontend/src/permission.ts | 14 +- apps/frontend/src/router/index.ts | 10 +- apps/frontend/src/settings.ts | 30 +- apps/frontend/src/store/index.ts | 4 +- apps/frontend/src/store/modules/account.ts | 14 +- apps/frontend/src/store/modules/app.ts | 6 +- apps/frontend/src/store/modules/permission.ts | 28 +- apps/frontend/src/store/modules/settings.ts | 2 + apps/frontend/src/store/modules/tagsView.ts | 6 +- apps/frontend/src/styles/dark.scss | 2 + apps/frontend/src/styles/element-plus.scss | 14 +- apps/frontend/src/styles/index.scss | 2 + apps/frontend/src/styles/reset.scss | 6 +- apps/frontend/src/styles/sidebar.scss | 2 + .../frontend/src/styles/variables.module.scss | 4 +- apps/frontend/src/styles/variables.scss | 4 +- apps/frontend/src/types/env.d.ts | 4 +- apps/frontend/src/types/global.d.ts | 2 + apps/frontend/src/utils/byte.ts | 50 +-- apps/frontend/src/utils/copy.ts | 6 +- apps/frontend/src/utils/i18n.ts | 4 +- apps/frontend/src/utils/index.ts | 2 + apps/frontend/src/utils/request.ts | 22 +- apps/frontend/src/utils/scroll-to.ts | 2 + apps/frontend/src/utils/time.ts | 36 +- .../frontend/src/views/account/list/index.vue | 38 ++- apps/frontend/src/views/config/list/index.vue | 4 +- apps/frontend/src/views/error-page/401.vue | 4 +- apps/frontend/src/views/error-page/404.vue | 4 +- .../list/components/Outbounds/index.vue | 2 + .../src/views/hysteria/list/index.vue | 2 + .../frontend/src/views/info/account/index.vue | 2 + .../frontend/src/views/log/hysteria/index.vue | 4 +- apps/frontend/src/views/log/system/index.vue | 4 +- apps/frontend/src/views/login/index.vue | 24 +- .../src/views/monitor/system/index.vue | 2 + apps/frontend/src/views/redirect/index.vue | 2 + apps/frontend/tsconfig.json | 2 +- apps/frontend/vite.config.ts | 30 +- apps/main.go | 1 + apps/middleware/admin.go | 1 + apps/middleware/cron.go | 1 + apps/middleware/filter.go | 1 + apps/middleware/jwt.go | 1 + apps/middleware/log.go | 1 + apps/middleware/rate_limiter.go | 1 + apps/model/bo/account.go | 1 + apps/model/bo/hysteria2.go | 1 + apps/model/bo/hysteria2_api.go | 1 + apps/model/bo/subscribe.go | 1 + apps/model/constant/client.go | 1 + apps/model/constant/code.go | 1 + apps/model/constant/config.go | 1 + apps/model/constant/error.go | 1 + apps/model/constant/system.go | 1 + apps/model/dto/account.go | 1 + apps/model/dto/config.go | 1 + apps/model/dto/dto.go | 11 +- apps/model/dto/hysteria2.go | 3 +- apps/model/dto/log.go | 1 + apps/model/dto/server.go | 1 + apps/model/entity/account.go | 1 + apps/model/entity/config.go | 1 + apps/model/entity/entity.go | 1 + apps/model/vo/account.go | 1 + apps/model/vo/config.go | 1 + apps/model/vo/hysteria2.go | 1 + apps/model/vo/jwt.go | 1 + apps/model/vo/log.go | 1 + apps/model/vo/monitor.go | 9 +- apps/model/vo/result.go | 1 + apps/model/vo/vo.go | 1 + apps/proxy/hysteria2.go | 1 + apps/proxy/hysteria2_api.go | 7 +- apps/proxy/process.go | 7 +- apps/router/account.go | 1 + apps/router/auth.go | 1 + apps/router/config.go | 1 + apps/router/hysteria2.go | 1 + apps/router/log.go | 1 + apps/router/monitor.go | 1 + apps/router/router.go | 1 + apps/service/account.go | 1 + apps/service/config.go | 5 +- apps/service/cron.go | 5 +- apps/service/forward.go | 7 +- apps/service/hysteria2.go | 1 + apps/service/hysteria2_api.go | 3 +- apps/service/jwt.go | 1 + apps/service/monitor.go | 1 + apps/service/server.go | 1 + apps/util/arr.go | 1 + apps/util/encrypt.go | 1 + apps/util/encrypt_test.go | 1 + apps/util/export.go | 1 + apps/util/file.go | 1 + apps/util/github.go | 1 + apps/util/hysteria2.go | 1 + apps/util/linux.go | 1 + apps/util/map.go | 1 + apps/util/rand.go | 1 + apps/util/string.go | 1 + docs/02-build-layer-and-package.md | 2 +- docs/README.md | 2 +- .../03-builder-layer.txt | 2 +- package/docs/README.md | 14 +- tools/build/{README.ru.md => README.md} | 16 +- 167 files changed, 632 insertions(+), 699 deletions(-) delete mode 100644 apps/frontend/src/lang/package/zh-cn.ts rename tools/build/{README.ru.md => README.md} (67%) diff --git a/README.md b/README.md index c530427..6a8fe49 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,68 @@ # HY2XS -HY2XS is a two-layer install package for a clean Debian 12 target: +HY2XS — двухслойный install package для развёртывания на чистом Debian 12 amd64. -- local builder layer: creates one transferable install archive -- install-only Bun/TypeScript orchestrator: runs on the target as a compiled artifact -- bundled HY2XS admin fork: shipped inside the package -- vanilla Hysteria2: downloaded from the official upstream during install +Состав baseline: -The baseline deliberately excludes target-side builds, update, rollback, uninstall, Telegram bot delivery, billing, and external access/profile delivery. +- production builder на Debian 12 amd64 создаёт один переносимый архив; +- install-only оркестратор на Bun + TypeScript поставляется на target как готовый compiled artifact; +- HY2XS admin поставляется в составе пакета как bundled fork; +- vanilla Hysteria2 скачивается с official upstream во время установки. -## Repository Layout +В baseline намеренно не входят target-side build, update, rollback, uninstall, Telegram bot delivery, billing и внешний контур выдачи access/profile. -- `builder/` - local shell-first packaging pipeline -- `orchestrator/` - Bun + TypeScript install-only orchestrator sources -- `package/` - package skeleton: thin entrypoint, templates, systemd units, package docs -- `apps/` - current HY2XS admin fork source -- `docs/` - baseline architecture and acceptance docs -- `hy2xs_implementation_plan-no_git/` - implementation roadmap and task breakdown -- `dist/` - final install archives generated by the builder +## Структура репозитория -## Build +- [`tools/build`](tools/build) — production builder и packaging pipeline. +- [`orchestrator`](orchestrator) — исходники install-only оркестратора на Bun + TypeScript. +- [`package`](package) — skeleton итогового install package: entrypoint, templates, systemd units, package docs. +- [`apps`](apps) — исходники HY2XS admin fork. +- [`docs`](docs) — архитектурная документация и acceptance. +- [`hy2xs_implementation_plan-no_git`](hy2xs_implementation_plan-no_git) — roadmap и task breakdown. +- [`dist`](dist) — итоговые install archives, создаются builder'ом и не хранятся в git. -On the local build machine: +## Сборка + +Production-сборка выполняется только на Debian 12 amd64: ```sh -PACKAGE_VERSION=0.1.0 ./builder/build.sh +PACKAGE_VERSION=0.1.0 ./tools/build/build.sh ``` -If tools are not in `PATH`, pass explicit binaries: +Builder сам проверяет ОС, архитектуру, структуру проекта, lock-файлы, версии Go/Bun/Node.js/pnpm и при необходимости скачивает локальный toolchain. -```sh -GO_BIN=/path/to/go PNPM_BIN=/path/to/pnpm BUN_BIN=/path/to/bun PACKAGE_VERSION=0.1.0 ./builder/build.sh -``` - -On the current Windows workstation the known tool paths are: - -```powershell -$env:PATH = "E:\Git\cmd;E:\go\bin;" + $env:PATH -E:\go\bin\go.exe test ./... -pnpm.cmd install --frozen-lockfile -``` - -The expected result is: +Ожидаемый результат: ```text dist/hy2xs-install-0.1.0.tar.gz ``` -The target server must not run `bun install`, `pnpm install`, TypeScript transpilation, frontend builds, or Go builds. +На target server не должно быть `bun install`, `pnpm install`, TypeScript transpilation, frontend build или Go build. -## Publication +Подробности сборки: [`tools/build/README.md`](tools/build/README.md). -Target repository: +## Публикация + +Целевой репозиторий: ```text https://git.ext.flamy.studio/flamy_dev/HY2XS_flamy.git ``` -Publish only after the full baseline acceptance is complete, including the Bun/TypeScript orchestrator artifact build and final package smoke checks. +Публиковать изменения нужно только после прохождения baseline acceptance, сборки оркестратора и smoke checks итогового пакета. -## Install +## Установка -On a clean Debian 12 target, unpack the archive and run as root: +На чистом Debian 12 target нужно распаковать архив и запустить от root: ```sh ./install.sh --non-interactive --domain example.com ``` -Useful baseline flags: +Полезные baseline-флаги: ```sh ./install.sh --non-interactive --domain example.com --port 443 --ssh-port 22 --ui-port 8080 ``` -The install flow writes `/etc/hysteria/post-install.env` with the resulting package, Hysteria, port, and path state. +После установки создаётся `/etc/hysteria/post-install.env` с фактическими параметрами deploy-состояния. diff --git a/apps/cmd/cmd.go b/apps/cmd/cmd.go index cb90da0..f44c3e7 100644 --- a/apps/cmd/cmd.go +++ b/apps/cmd/cmd.go @@ -48,3 +48,4 @@ func Execute() { os.Exit(1) } } + diff --git a/apps/cmd/reset.go b/apps/cmd/reset.go index fcd8fb7..ec8bbca 100644 --- a/apps/cmd/reset.go +++ b/apps/cmd/reset.go @@ -49,3 +49,4 @@ func runReset(cmd *cobra.Command, args []string) { fmt.Println(fmt.Sprintf("HY2XS admin Login Password: %s", password)) fmt.Println(fmt.Sprintf("HY2XS admin Connection Password: %s", fmt.Sprintf("%s.%s", username, password))) } + diff --git a/apps/cmd/server.go b/apps/cmd/server.go index ff5e8fa..44ad1e3 100644 --- a/apps/cmd/server.go +++ b/apps/cmd/server.go @@ -83,3 +83,4 @@ func initFile() error { } return nil } + diff --git a/apps/cmd/version.go b/apps/cmd/version.go index f265e02..fdec920 100644 --- a/apps/cmd/version.go +++ b/apps/cmd/version.go @@ -20,3 +20,4 @@ func init() { func runVersion(cmd *cobra.Command, args []string) { fmt.Println("HY2XS admin version", constant.Version) } + diff --git a/apps/controller/account.go b/apps/controller/account.go index 59df427..fd9fef0 100644 --- a/apps/controller/account.go +++ b/apps/controller/account.go @@ -209,7 +209,7 @@ func GetAccountInfo(c *gin.Context) { vo.Fail(err.Error(), c) return } - // 更新最近登录时间 + // Обновление времени последнего входа now := time.Now().UnixMilli() if err = service.UpdateAccount(entity.Account{ BaseEntity: entity.BaseEntity{Id: &accountInfoVo.Id}, @@ -255,12 +255,12 @@ func ImportAccount(c *gin.Context) { vo.Fail(constant.SysError, c) return } - // 文件大小 2MB + // Размер файла 2 MB if header.Size > 1024*1024*2 { vo.Fail("the file is too big", c) return } - // 文件后缀.json + // Расширение файла .json if !strings.HasSuffix(header.Filename, ".json") { vo.Fail("file format not supported", c) return @@ -297,7 +297,7 @@ func ExportAccount(c *gin.Context) { return } - // 下载 + // Скачивание if !util.Exists(filePath) { vo.Fail("file not exist", c) return @@ -333,3 +333,4 @@ func VerifyDefaultPass(c *gin.Context) { } vo.Success(account.Pass != nil && *account.Pass == "02f382b76ca1ab7aa06ab03345c7712fd5b971fb0c0f2aef98bac9cd", c) } + diff --git a/apps/controller/config.go b/apps/controller/config.go index a93c492..8d828a0 100644 --- a/apps/controller/config.go +++ b/apps/controller/config.go @@ -241,7 +241,7 @@ func ExportHysteria2Config(c *gin.Context) { return } - // 默认值 + // Значения по умолчанию config, err := dao.ListConfig("key in ?", []string{constant.HUIWebPort, constant.JwtSecret}) if err != nil { vo.Fail(err.Error(), c) @@ -324,7 +324,7 @@ func ImportHysteria2Config(c *gin.Context) { return } - // 默认值 + // Значения по умолчанию config, err := dao.ListConfig("key in ?", []string{constant.HUIWebPort, constant.JwtSecret}) if err != nil { vo.Fail(err.Error(), c) @@ -501,3 +501,4 @@ func UploadCertFile(c *gin.Context) { } vo.Success(certPath, c) } + diff --git a/apps/controller/hysteria2.go b/apps/controller/hysteria2.go index 06aa1c4..4a7c7d8 100644 --- a/apps/controller/hysteria2.go +++ b/apps/controller/hysteria2.go @@ -25,7 +25,7 @@ func Hysteria2Auth(c *gin.Context) { return } - // 更新最近连接时间 + // Обновление времени последнего подключения now := time.Now().UnixMilli() if err = service.UpdateAccount(entity.Account{ BaseEntity: entity.BaseEntity{Id: &id}, @@ -69,7 +69,7 @@ func Hysteria2Url(c *gin.Context) { vo.Fail(err.Error(), c) return } - // 生成二维码 + // Генерация QR-кода qrCode, err := qrcode.Encode(url, qrcode.Medium, 300) if err != nil { vo.Fail(err.Error(), c) @@ -150,3 +150,4 @@ func Hysteria2Subscribe(c *gin.Context) { c.String(200, configStr) } + diff --git a/apps/controller/log.go b/apps/controller/log.go index 66f4585..9ffb2d1 100644 --- a/apps/controller/log.go +++ b/apps/controller/log.go @@ -114,3 +114,4 @@ func ExportLog(c *gin.Context) { c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName)) c.File(filePath) } + diff --git a/apps/controller/monitor.go b/apps/controller/monitor.go index e3e6502..398ed50 100644 --- a/apps/controller/monitor.go +++ b/apps/controller/monitor.go @@ -23,3 +23,4 @@ func MonitorHysteria2(c *gin.Context) { } vo.Success(hysteria2MonitorVo, c) } + diff --git a/apps/controller/validator.go b/apps/controller/validator.go index e43cdf0..d0fd9a6 100644 --- a/apps/controller/validator.go +++ b/apps/controller/validator.go @@ -19,7 +19,7 @@ func init() { func validateStr(f validator.FieldLevel) bool { field := f.Field().String() - // 字符串必须6-32位是字母或者数字或部分特殊字符的组合 + // Строка должна быть длиной 6-32 символа и состоять из букв, цифр или разрешённых спецсимволов reg := "^[a-zA-Z0-9!@#$%^&*()_+-=]{6,32}$" compile := regexp.MustCompile(reg) return field == "" || compile.MatchString(field) @@ -39,3 +39,4 @@ func validateField[T interface{}](c *gin.Context, field T) (T, error) { } return field, nil } + diff --git a/apps/dao/account.go b/apps/dao/account.go index 37752fe..0986b1f 100644 --- a/apps/dao/account.go +++ b/apps/dao/account.go @@ -117,3 +117,4 @@ func ListAccount(query interface{}, args ...interface{}) ([]entity.Account, erro } return accounts, nil } + diff --git a/apps/dao/config.go b/apps/dao/config.go index 98155c6..daa5782 100644 --- a/apps/dao/config.go +++ b/apps/dao/config.go @@ -64,3 +64,4 @@ func UpsertConfig(configs []entity.Config) error { } return nil } + diff --git a/apps/dao/sqlite.go b/apps/dao/sqlite.go index f9cd0eb..0d868ac 100644 --- a/apps/dao/sqlite.go +++ b/apps/dao/sqlite.go @@ -117,3 +117,4 @@ func Paginate(pageNum *int64, pageSize *int64) func(db *gorm.DB) *gorm.DB { return db.Offset(int((num - 1) * size)).Limit(int(size)) } } + diff --git a/apps/frontend/.editorconfig b/apps/frontend/.editorconfig index dd76572..280a7a9 100644 --- a/apps/frontend/.editorconfig +++ b/apps/frontend/.editorconfig @@ -1,14 +1,14 @@ # http://editorconfig.org root = true -# 表示所有文件适用 +# Applies to all files [*] -charset = utf-8 # 设置文件字符集为 utf-8 -end_of_line = lf # 控制换行类型(lf | cr | crlf) -indent_style = tab # 缩进风格(tab | space) -insert_final_newline = true # 始终在文件末尾插入一个新行 +charset = utf-8 # File charset +end_of_line = lf # Line ending type: lf | cr | crlf +indent_style = tab # Indentation style: tab | space +insert_final_newline = true # Always insert a final newline -# 表示仅 md 文件适用以下规则 +# Applies only to md files [*.md] -max_line_length = off # 关闭最大行长度限制 -trim_trailing_whitespace = false # 关闭末尾空格修剪 +max_line_length = off # Disable max line length limit +trim_trailing_whitespace = false # Disable trailing whitespace trimming diff --git a/apps/frontend/.env.development b/apps/frontend/.env.development index af15405..21a637a 100644 --- a/apps/frontend/.env.development +++ b/apps/frontend/.env.development @@ -1,6 +1,6 @@ -## 开发环境 +## Development environment -# 变量必须以 VITE_ 为前缀才能暴露给外部读取 +# Variables must use the VITE_ prefix to be exposed externally NODE_ENV='development' VITE_APP_TITLE = 'h-ui' diff --git a/apps/frontend/.env.production b/apps/frontend/.env.production index 40e7f4a..353cfec 100644 --- a/apps/frontend/.env.production +++ b/apps/frontend/.env.production @@ -1,4 +1,4 @@ -## 生产环境 +## Production environment VITE_APP_TITLE = 'h-ui' VITE_APP_PORT = 80 diff --git a/apps/frontend/.eslintrc.cjs b/apps/frontend/.eslintrc.cjs index a2af090..5cb7027 100644 --- a/apps/frontend/.eslintrc.cjs +++ b/apps/frontend/.eslintrc.cjs @@ -18,9 +18,9 @@ module.exports = { }, plugins: ["vue", "@typescript-eslint"], rules: { - "vue/multi-word-component-names": "off", // 关闭组件名必须多字: https://eslint.vuejs.org/rules/multi-word-component-names.html - "@typescript-eslint/no-empty-function": "off", // 关闭空方法检查 - "@typescript-eslint/no-explicit-any": "off", // 关闭any类型的警告 + "vue/multi-word-component-names": "off", // Disable multi-word component name requirement: https://eslint.vuejs.org/rules/multi-word-component-names.html + "@typescript-eslint/no-empty-function": "off", // Disable empty function checks + "@typescript-eslint/no-explicit-any": "off", // Disable warnings for any "vue/no-v-model-argument": "off", "@typescript-eslint/no-non-null-assertion": "off", }, // https://eslint.org/docs/latest/use/configure/language-options#specifying-globals @@ -29,4 +29,4 @@ module.exports = { OptionType: "readonly", }, }; -цц \ No newline at end of file +цц diff --git a/apps/frontend/.prettierrc.cjs b/apps/frontend/.prettierrc.cjs index d39f28c..d03e2e9 100644 --- a/apps/frontend/.prettierrc.cjs +++ b/apps/frontend/.prettierrc.cjs @@ -1,36 +1,36 @@ module.exports = { - // (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always) + // Parentheses around a single arrow function parameter. Default: always. arrowParens: "always", - // 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false + // Put the closing bracket of a multi-line element at the end of the last line of attributes. bracketSameLine: false, - // 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar}) + // Print spaces between brackets in object literals. bracketSpacing: true, - // 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto) + // Format embedded code snippets. embeddedLanguageFormatting: "auto", - // 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css) + // HTML whitespace sensitivity. htmlWhitespaceSensitivity: "css", - // 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记,默认false + // Insert a special @format marker at the top of formatted files. insertPragma: false, - // 在 JSX 中使用单引号替代双引号,默认false + // Use single quotes in JSX. jsxSingleQuote: false, - // 每行最多字符数量,超出换行(默认80) + // Maximum line length. printWidth: 80, - // 超出打印宽度 (always | never | preserve ) + // Markdown prose wrapping. proseWrap: "preserve", - // 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;) + // Object property quote policy. quoteProps: "as-needed", - // 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件,默认false + // Format only files with @prettier or @format pragma. requirePragma: false, - // 结尾添加分号 + // Add semicolons. semi: true, - // 使用单引号 (true:单引号;false:双引号) + // Use single quotes. singleQuote: false, - // 缩进空格数,默认2个空格 + // Indentation width. tabWidth: 2, - // 元素末尾是否加逗号,默认es5: ES5中的 objects, arrays 等会添加逗号,TypeScript 中的 type 后不加逗号 + // Trailing commas. trailingComma: "es5", - // 指定缩进方式,空格或tab,默认false,即使用空格 + // Use tabs for indentation. useTabs: false, - // vue 文件中是否缩进 + + diff --git a/apps/frontend/src/components/ImputMultiple/index.vue b/apps/frontend/src/components/ImputMultiple/index.vue index 76376de..9c78de2 100644 --- a/apps/frontend/src/components/ImputMultiple/index.vue +++ b/apps/frontend/src/components/ImputMultiple/index.vue @@ -75,3 +75,5 @@ const handleClose = (tag: string): void => { flex-wrap: wrap; } + + diff --git a/apps/frontend/src/components/LangSelect/index.vue b/apps/frontend/src/components/LangSelect/index.vue index 7ad680c..a433b72 100644 --- a/apps/frontend/src/components/LangSelect/index.vue +++ b/apps/frontend/src/components/LangSelect/index.vue @@ -34,3 +34,5 @@ function handleLanguageChange(lang: string) { + + diff --git a/apps/frontend/src/components/MapAdd/index.vue b/apps/frontend/src/components/MapAdd/index.vue index bd68c67..b671064 100644 --- a/apps/frontend/src/components/MapAdd/index.vue +++ b/apps/frontend/src/components/MapAdd/index.vue @@ -178,3 +178,5 @@ const closeDialog = (): void => { flex-wrap: wrap; } + + diff --git a/apps/frontend/src/components/Pagination/index.vue b/apps/frontend/src/components/Pagination/index.vue index b81f55d..940a394 100644 --- a/apps/frontend/src/components/Pagination/index.vue +++ b/apps/frontend/src/components/Pagination/index.vue @@ -86,3 +86,5 @@ function handleCurrentChange(val: number) { } } + + diff --git a/apps/frontend/src/components/RightPanel/index.vue b/apps/frontend/src/components/RightPanel/index.vue index 08f5966..b124b5c 100644 --- a/apps/frontend/src/components/RightPanel/index.vue +++ b/apps/frontend/src/components/RightPanel/index.vue @@ -28,7 +28,7 @@ function addEventClick() { } function closeSidebar(evt: any) { - // 主题选择点击不关闭 + // Клик по выбору темы не закрывает панель let parent = evt.target.closest(".right-panel-container"); if (!parent) { show.value = false; @@ -134,3 +134,5 @@ onBeforeUnmount(() => { } } + + diff --git a/apps/frontend/src/components/SizeSelect/index.vue b/apps/frontend/src/components/SizeSelect/index.vue index f0800ba..6326073 100644 --- a/apps/frontend/src/components/SizeSelect/index.vue +++ b/apps/frontend/src/components/SizeSelect/index.vue @@ -4,14 +4,14 @@ import { useAppStore } from "@/store/modules/app"; const appStore = useAppStore(); const sizeOptions = ref([ - { label: "默认", value: "default" }, - { label: "大型", value: "large" }, - { label: "小型", value: "small" }, + { label: "Обычный", value: "default" }, + { label: "Крупный", value: "large" }, + { label: "Компактный", value: "small" }, ]); function handleSizeChange(size: string) { appStore.changeSize(size); - ElMessage.success("切换布局大小成功"); + ElMessage.success("Размер интерфейса изменён"); } @@ -34,3 +34,5 @@ function handleSizeChange(size: string) { + + diff --git a/apps/frontend/src/components/SvgIcon/index.vue b/apps/frontend/src/components/SvgIcon/index.vue index e9d145e..bdd508c 100644 --- a/apps/frontend/src/components/SvgIcon/index.vue +++ b/apps/frontend/src/components/SvgIcon/index.vue @@ -36,8 +36,10 @@ const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`); width: 1em; height: 1em; overflow: hidden; - vertical-align: -0.15em; /* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */ + vertical-align: -0.15em; /* Так как размер icon равен размеру шрифта, добавляем смещение для визуального выравнивания */ outline: none; - fill: currentcolor; /* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */ + fill: currentcolor; /* currentColor наследует текущий цвет элемента или родителя */ } + + diff --git a/apps/frontend/src/components/UnitSelect/index.vue b/apps/frontend/src/components/UnitSelect/index.vue index db4ae90..d5fa618 100644 --- a/apps/frontend/src/components/UnitSelect/index.vue +++ b/apps/frontend/src/components/UnitSelect/index.vue @@ -72,3 +72,5 @@ watch( + + diff --git a/apps/frontend/src/directive/index.ts b/apps/frontend/src/directive/index.ts index eec32b0..547cff0 100644 --- a/apps/frontend/src/directive/index.ts +++ b/apps/frontend/src/directive/index.ts @@ -2,8 +2,10 @@ import type { App } from "vue"; import { hasRole } from "./permission"; -// 全局注册 directive +// Глобальная регистрация directive export function setupDirective(app: App) { - // 使 v-hasRole 在所有组件中都可用 + // Делает v-hasRole доступным во всех компонентах app.directive("hasRole", hasRole); } + + diff --git a/apps/frontend/src/directive/permission/index.ts b/apps/frontend/src/directive/permission/index.ts index 4e42434..b9ba15f 100644 --- a/apps/frontend/src/directive/permission/index.ts +++ b/apps/frontend/src/directive/permission/index.ts @@ -2,14 +2,14 @@ import { useAccountStoreHook } from "@/store/modules/account"; import { Directive, DirectiveBinding } from "vue"; /** - * 角色权限 + * Ролевые права */ export const hasRole: Directive = { mounted(el: HTMLElement, binding: DirectiveBinding) { const { value } = binding; if (value) { - const requiredRoles = value; // DOM绑定需要的角色编码 + const requiredRoles = value; // Коды ролей, требуемые DOM-привязкой const { roles } = useAccountStoreHook(); const hasRole = roles.some((perm) => { return requiredRoles.includes(perm); @@ -23,3 +23,5 @@ export const hasRole: Directive = { } }, }; + + diff --git a/apps/frontend/src/lang/index.ts b/apps/frontend/src/lang/index.ts index 63ac05a..899af53 100644 --- a/apps/frontend/src/lang/index.ts +++ b/apps/frontend/src/lang/index.ts @@ -22,3 +22,5 @@ const i18n = createI18n({ }); export default i18n; + + diff --git a/apps/frontend/src/lang/package/en.ts b/apps/frontend/src/lang/package/en.ts index 1dacc22..a983c20 100644 --- a/apps/frontend/src/lang/package/en.ts +++ b/apps/frontend/src/lang/package/en.ts @@ -1,5 +1,5 @@ export default { - // 路由国际化 + // МаршрутЛокализация route: { account: "Account", accountList: "Account Manage", @@ -15,14 +15,14 @@ export default { info: "Info", infoAccount: "Account Info", }, - // 登录页面国际化 + // Локализация страницы входа login: { title: "HY2XS admin", username: "Username", password: "Password", login: "Login", }, - // 导航栏国际化 + // Локализация навигационной панели navbar: { logout: "Logout", }, @@ -322,3 +322,5 @@ export default { }, }, }; + + diff --git a/apps/frontend/src/lang/package/ru.ts b/apps/frontend/src/lang/package/ru.ts index 6cf7c4b..e4e1f52 100644 --- a/apps/frontend/src/lang/package/ru.ts +++ b/apps/frontend/src/lang/package/ru.ts @@ -256,3 +256,5 @@ export default { }, }, }; + + diff --git a/apps/frontend/src/lang/package/zh-cn.ts b/apps/frontend/src/lang/package/zh-cn.ts deleted file mode 100644 index d69e24a..0000000 --- a/apps/frontend/src/lang/package/zh-cn.ts +++ /dev/null @@ -1,312 +0,0 @@ -export default { - // 路由国际化 - route: { - account: "账户", - accountList: "账户管理", - hysteria: "Hysteria", - hysteriaList: "Hysteria 管理", - config: "系统", - configList: "系统设置", - monitor: "监控", - monitorSystem: "系统监控", - log: "日志", - logSystem: "系统日志", - logHysteria: "Hysteria 日志", - info: "信息", - infoAccount: "账户信息", - }, - // 登录页面国际化 - login: { - title: "HY2XS admin", - username: "用户名", - password: "密码", - login: "登 录", - }, - // 导航栏国际化 - navbar: { - logout: "注销", - }, - common: { - id: "编号", - createTime: "创建时间", - operate: "操作", - edit: "编辑", - delete: "删除", - deleted: "状态", - all: "全部", - enable: "正常", - disable: "禁用", - search: "搜索", - reset: "重设", - add: "新增", - confirm: "确定", - cancel: "取消", - copySuccess: "复制成功", - subscribe: "订阅链接", - subscribeQrCode: "订阅二维码", - nodeUrl: "节点 URL", - nodeQrCode: "节点二维码", - resetTraffic: "重设流量", - import: "导入", - export: "导出", - save: "保存", - update: "更新", - downloadSuccess: "下载成功", - wait: "正在更换版本,请等待一会儿", - enableSuccess: "Hysteria2 启动 !!", - disableSuccess: "Hysteria2 已关闭", - success: "成功", - refresh: "刷新", - yes: "是", - no: "否", - securityRisk: `安全隐患`, - defaultPassTip: `请尽快修改默认登录密码,建议设置强密码以保护您的账户安全。点击这里修改`, - noHttpsTip: `您的网站未启用 HTTPS,数据传输不安全,请尽快开启 HTTPS 以保护用户信息。点击这里开启`, - }, - info: { - expireTime: "年-月-日 时:分:秒", - greeting1: "微凉扑面,清新的空气,唤醒一天的活力🌅!", - greeting2: "上午好,", - greeting3: "下午好,", - greeting4: "晚上好,", - greeting5: "我愿成为流星,划破黑夜,只为照亮你的梦境,晚安🌛!", - }, - account: { - remark: "备注", - username: "用户名", - pass: "登录密码", - conPass: "连接密码", - quota: "配额", - download: "下载", - upload: "上传", - expireTime: "过期时间", - kickUtilTimeLast: "下线剩余时间", - kickUtilTime: "下线截止时间", - deviceNo: "限制设备数", - onlineStatus: "在线状态", - online: "在线", - offline: "离线", - device: "在线设备数", - role: "角色", - unit: "单位", - loginAt: "最近登录时间", - conAt: "最近连接时间", - createTime: "注册时间", - releaseSuccess: "解除成功", - kick: "下线", - kickTip: "强制用户下线", - releaseKick: "解除", - releaseKickTip: "解除下线状态", - }, - config: { - huiWebPort: "HY2XS admin Web 端口", - huiWebContext: "HY2XS admin Web 上下文", - hysteria2TrafficTime: "Hysteria2 流量倍数", - huiCrtPath: "HY2XS admin CRT 证书路径", - huiKeyPath: "HY2XS admin KEY 证书路径", - uploadCrtFile: "上传 CRT 证书", - uploadKeyFile: "上传 KEY 证书", - restartServer: "重启面板", - restartTip: "正在重启,请刷新", - useHysteria2Cert: "使用 Hysteria2 的证书", - huiHttps: "面板开启 https", - resetTrafficCron: "重设流量计划任务", - resetTrafficCronTip: - "计划任务表达式,参考:https://pkg.go.dev/github.com/robfig/cron/v3", - resetTrafficMonth: "每月运行一次,每月第一天午夜", - resetTrafficWeek: "每周运行一次,周六/周日午夜", - }, - monitor: { - huiVersion: "HY2XS admin 版本", - cpuPercent: "CPU 使用率", - memPercent: "内存使用率", - diskPercent: "磁盘使用率", - hysteria2UserTotal: "在线用户数", - hysteria2DeviceTotal: "在线设备数", - hysteria2Version: "Hysteria2 版本", - hysteria2Running: "Hysteria2 状态", - hysteria2RunningTrue: "运行", - hysteria2RunningFalse: "停止", - }, - log: { - numLine: "显示行数", - }, - hysteria: { - enable: "开启", - disable: "关闭", - addConfigItem: "添加配置项", - hysteria2Version: "Hysteria2 版本", - hysteria2Running: "Hysteria2 状态", - hysteria2ChangeVersion: "切换", - addOutbound: "添加出站规则", - extension: "扩展", - listen: "监听地址", - tls: "TLS", - obfs: "混淆", - quic: "QUIC 参数", - bandwidth: "带宽", - speedTest: "速度测试", - udp: "UDP", - resolver: "DNS 解析", - sniff: "协议嗅探 (Sniff)", - acl: "ACL", - outbounds: "出站规则", - http: "流量统计 API", - masquerade: "伪装", - config: { - enable: "开启/关闭", - remark: "别名", - portHopping: - "端口跳跃,多个单端口:1234,5678,9012;端口范围:20000-50000;两者的组合:1234,5000-6000,7044,8000-9000", - clashExtension: "Clash 订阅扩展", - listen: - "当只有端口没有 IP 地址时,服务器将监听所有可用的 IPv4 和 IPv6 地址。要仅监听 IPv4,可以使用 0.0.0.0:443。要仅监听 IPv6,可以使用 [::]:443。", - tlsType: "TLS 类型", - tls: { - cert: "CERT 路径", - key: "KEY 路径", - sniGuard: - "验证客户端发送的 SNI。 与证书信息匹配时才建立连接, 否则终止 TLS 握手。 设置为 strict 以启用该功能。 设置为 disable 以禁用该功能。 默认为 dns-san, 仅当证书中包含「证书主题背景的备用名称」扩展且该扩展中包含域名时才启用该功能。", - }, - acme: { - domains: "域名", - email: "邮箱", - ca: "要使用的 CA。可以是 letsencrypt 或 zerossl。", - listenHost: - "用于 ACME 服务器验证的监听地址(不含端口)。默认监听所有可用的地址。", - dir: "存储 ACME 账户密钥和证书的目录。", - type: "ACME 验证类型。可以是 http, tls 或 dns。", - http: { - altPort: - "用于 HTTP 挑战的监听端口。 (注意: 改为非 80 需要另行配置端口转发或者 HTTP 反向代理,否则证书会签署失败!)", - }, - tls: { - altPort: - "用于 TLS-ALPN 挑战的监听端口。 (注意: 改为非 443 需要另行配置端口转发或者 SNI Proxy,否则证书会签署失败!)", - }, - dns: { - name: "DNS 提供商。详细信息请参考 ACME DNS 配置。", - config: "ACME DNS 配置", - }, - disableHTTP: "禁用 HTTP 挑战。", - disableTLSALPN: "禁用 TLS-ALPN 挑战。", - altHTTPPort: - "用于 HTTP 挑战的监听端口。 (注意: 改为非 80 需要另行配置端口转发或者 HTTP 反向代理,否则证书会签署失败!)", - altTLSALPNPort: - "用于 TLS-ALPN 挑战的监听端口。 (注意: 改为非 443 需要另行配置端口转发或者 SNI Proxy,否则证书会签署失败!)", - }, - obfs: { - type: "类型", - salamander: { - password: "替换为你的混淆密码。", - }, - }, - quic: { - initStreamReceiveWindow: "初始的 QUIC 流接收窗口大小。", - maxStreamReceiveWindow: "最大的 QUIC 流接收窗口大小。", - initConnReceiveWindow: "初始的 QUIC 连接接收窗口大小。", - maxConnReceiveWindow: "最大的 QUIC 连接接收窗口大小。", - maxIdleTimeout: - "最长空闲超时时间。服务器会在多长时间没有收到任何客户端数据后关闭连接。", - maxIncomingStreams: "最大并发传入流的数量。", - disablePathMTUDiscovery: "禁用 MTU 探测。", - }, - bandwidth: { - up: "上传", - down: "下载", - }, - ignoreClientBandwidth: "忽略客户端带宽设置", - speedTest: "speedTest 启用后,服务端将允许客户端进行下载和上传速度测试。", - disableUDP: "disableUDP 启用后服务端禁用 UDP 转发,只支持 TCP。", - udpIdleTimeout: - "udpIdleTimeout 用于指定服务器对于每个 UDP 会话,在没有流量时保持本地 UDP 端口的时间长度。概念上与 NAT 的 UDP 会话超时时间相似。", - resolver: { - type: "类型", - tcp: { - addr: "TCP DNS 服务器地址。", - timeout: "DNS 查询超时时间。", - }, - udp: { - addr: "UDP DNS 服务器地址。", - timeout: "DNS 查询超时时间。", - }, - tls: { - addr: "DNS over TLS 服务器地址。", - timeout: "DNS 查询超时时间。", - sni: "DNS over TLS 服务器的 SNI。", - insecure: "禁用 TLS 证书验证。", - }, - https: { - addr: "DNS over HTTPS 服务器地址。", - timeout: "DNS 查询超时时间。", - sni: "DNS over TLS 服务器的 SNI。", - insecure: "禁用 TLS 证书验证。", - }, - }, - sniff: { - enable: "是否启用协议嗅探。", - timeout: - "嗅探超时时间。如果超过这个时间仍然无法确定协议/获取域名,将使用原地址发起连接。", - rewriteDomain: - "是否重写已经是域名的请求。如果启用,对于目标地址已经是域名的请求,仍会进行嗅探。", - tcpPorts: "TCP 端口列表。只有这些端口的 TCP 请求会被嗅探。", - udpPorts: "UDP 端口列表。只有这些端口的 UDP 请求会被嗅探。", - }, - aclType: "ACL 类型", - acl: { - file: "ACL 文件的路径。", - inline: "内联 ACL 规则的列表。", - geoip: - "可选。取消注释以启用。GeoIP 数据库文件的路径。如果省略这个字段,Hysteria 会自动下载最新的数据库到工作目录。", - geosite: - "可选。取消注释以启用。GeoSite 数据库文件的路径。如果省略这个字段,Hysteria 会自动下载最新的数据库到工作目录。", - geoUpdateInterval: - "可选。GeoIP/GeoSite 数据库刷新的间隔。默认为 168 小时(1 周)。仅在 GeoIP/GeoSite 数据库是自动下载的情况下生效。", - }, - outbounds: { - name: "出站规则的名称。在 ACL 中使用。", - type: "类型", - socks5: { - addr: "SOCKS5 代理地址。", - username: "可选。SOCKS5 代理用户名。", - password: "可选。SOCKS5 代理密码。", - }, - http: { - url: "HTTP/HTTPS 代理 URL。(可以是 http:// 或 https:// 开头)", - insecure: "可选。禁用 TLS 证书验证。仅适用于 HTTPS 代理。", - }, - direct: { - mode: "类型", - bindIPv4: "要绑定的本地 IPv4 地址。", - bindIPv6: "要绑定的本地 IPv6 地址。", - bindDevice: "要绑定的本地网卡。", - fastOpen: "启用 TCP 快速打开。", - }, - }, - trafficStats: { - listen: "监听地址。", - }, - masquerade: { - type: "类型", - file: { - dir: "用于提供文件的目录。", - }, - proxy: { - url: "要代理的网站的 URL。", - rewriteHost: - "是否重写 Host 头以匹配被代理的网站。如果目标网站通过 Host 识别请求的网站,这个选项是必须的。", - insecure: "禁用对代理网站的 TLS 验证。", - }, - string: { - content: "要返回的字符串。", - headers: "可选。要返回的 HTTP 头列表。", - statusCode: "可选。要返回的 HTTP 状态码。默认为 200。", - }, - listenHTTP: "HTTP (TCP) 监听地址。", - listenHTTPS: "HTTPS (TCP) 监听地址。", - forceHTTPS: - "是否强制使用 HTTPS。如果启用,HTTP 请求将被重定向到 HTTPS。", - }, - }, - }, -}; diff --git a/apps/frontend/src/layout/components/AppMain.vue b/apps/frontend/src/layout/components/AppMain.vue index 0de8378..56b9848 100644 --- a/apps/frontend/src/layout/components/AppMain.vue +++ b/apps/frontend/src/layout/components/AppMain.vue @@ -43,3 +43,5 @@ const tagsViewStore = useTagsViewStore(); } } + + diff --git a/apps/frontend/src/layout/components/Navbar.vue b/apps/frontend/src/layout/components/Navbar.vue index 8a2cd61..1178364 100644 --- a/apps/frontend/src/layout/components/Navbar.vue +++ b/apps/frontend/src/layout/components/Navbar.vue @@ -12,27 +12,27 @@ const accountStore = useAccountStore(); const route = useRoute(); const router = useRouter(); -const { device } = storeToRefs(appStore); // 设备类型:desktop-宽屏设备 || mobile-窄屏设备 +const { device } = storeToRefs(appStore); // Тип устройства: desktop — широкий экран, mobile — узкий экран /** - * 左侧菜单栏显示/隐藏 + * Показать или скрыть левое меню. */ function toggleSideBar() { appStore.toggleSidebar(true); } /** - * vueUse 全屏 + * Полноэкранный режим через VueUse. */ const { isFullscreen, toggle } = useFullscreen(); /** - * 注销 + * Выход из системы. */ function logout() { - ElMessageBox.confirm("确定注销并退出系统吗?", "提示", { - confirmButtonText: "确定", - cancelButtonText: "取消", + ElMessageBox.confirm("Выйти из системы?", "Подтверждение", { + confirmButtonText: "Выйти", + cancelButtonText: "Отмена", type: "warning", }).then(() => { accountStore @@ -48,9 +48,9 @@ function logout() {