-
+
-
+
-
+
-
+

@@ -138,3 +138,5 @@ function logout() {
}
}
+
+
diff --git a/apps/frontend/src/layout/components/Settings/index.vue b/apps/frontend/src/layout/components/Settings/index.vue
index d426198..92d3b4a 100644
--- a/apps/frontend/src/layout/components/Settings/index.vue
+++ b/apps/frontend/src/layout/components/Settings/index.vue
@@ -5,21 +5,21 @@ import IconEpSunny from "~icons/ep/sunny";
import IconEpMoon from "~icons/ep/moon";
/**
- * 暗黑模式
+ * Тёмная тема.
*/
const settingsStore = useSettingsStore();
const isDark = useDark();
const toggleDark = () => useToggle(isDark);
/**
- * 切换布局
+ * Переключение layout.
*/
function changeLayout(layout: string) {
settingsStore.changeSetting({ key: "layout", value: layout });
window.document.body.setAttribute("layout", settingsStore.layout);
}
-// 主题颜色
+// Цвета темы
const themeColors = ref
([
"#409EFF",
"#304156",
@@ -30,7 +30,7 @@ const themeColors = ref([
]);
/**
- * 切换主题颜色
+ * Переключение цвета темы.
*/
function changeThemeColor(color: string) {
settingsStore.changeSetting({ key: "themeColor", value: color });
@@ -51,8 +51,8 @@ onMounted(() => {
-
项目配置
-
主题
+
Настройки проекта
+
Тема
{
/>
-
界面设置
+
Настройки интерфейса
- 开启 Tags-View
+ Включить вкладки
- 固定 Header
+ Зафиксировать header
- 侧边栏 Logo
+ Логотип в боковом меню
-
主题颜色
+
Цвет темы
- {
}
}
+
+
diff --git a/apps/frontend/src/layout/components/Sidebar/Link.vue b/apps/frontend/src/layout/components/Sidebar/Link.vue
index d80b289..c112217 100644
--- a/apps/frontend/src/layout/components/Sidebar/Link.vue
+++ b/apps/frontend/src/layout/components/Sidebar/Link.vue
@@ -35,3 +35,5 @@ function push() {
+
+
diff --git a/apps/frontend/src/layout/components/Sidebar/Logo.vue b/apps/frontend/src/layout/components/Sidebar/Logo.vue
index ca4ed53..da76e70 100644
--- a/apps/frontend/src/layout/components/Sidebar/Logo.vue
+++ b/apps/frontend/src/layout/components/Sidebar/Logo.vue
@@ -51,3 +51,5 @@ const logo = ref(new URL(`../../../assets/logo.png`, import.meta.url).href);
opacity: 0;
}
+
+
diff --git a/apps/frontend/src/layout/components/Sidebar/SidebarItem.vue b/apps/frontend/src/layout/components/Sidebar/SidebarItem.vue
index 06ebf71..c518042 100644
--- a/apps/frontend/src/layout/components/Sidebar/SidebarItem.vue
+++ b/apps/frontend/src/layout/components/Sidebar/SidebarItem.vue
@@ -8,7 +8,7 @@ import SvgIcon from "@/components/SvgIcon/index.vue";
const props = defineProps({
/**
- * 路由(eg:level_3_1)
+ * Route, например level_3_1
*/
item: {
type: Object,
@@ -16,7 +16,7 @@ const props = defineProps({
},
/**
- * 父层级完整路由路径(eg:/level/level_3/level_3_1)
+ * Полный path родительского уровня, например /level/level_3/level_3_1
*/
basePath: {
type: String,
@@ -24,34 +24,34 @@ const props = defineProps({
},
});
-const onlyOneChild = ref(); // 临时变量,唯一子路由
+const onlyOneChild = ref(); // Временная переменная для единственного дочернего route
/**
- * 判断当前路由是否只有一个子路由
+ * Проверяет, есть ли у текущего route только один дочерний route
*
- * 1:如果只有一个子路由: 返回 true
- * 2:如果无子路由 :返回 true
+ * 1: если дочерний route один, возвращает true
+ * 2: если дочерних routes нет, возвращает true
*
- * @param children 子路由数组
- * @param parent 当前路由
+ * @param children Массив дочерних routes
+ * @param parent Текущий route
*/
function hasOneShowingChild(children = [], parent: any) {
- // 需要显示的子路由数组
+ // Массив дочерних routes для отображения
const showingChildren = children.filter((item: any) => {
if (item.meta?.hidden) {
- return false; // 过滤不显示的子路由
+ return false; // Фильтрация скрытого дочернего route
} else {
- onlyOneChild.value = item; // 唯一子路由赋值(多个子路由情况 onlyOneChild 变量是用不上的)
+ onlyOneChild.value = item; // Запись единственного дочернего route, при нескольких routes переменная onlyOneChild не используется
return true;
}
});
- // 1:如果只有一个子路由, 返回 true
+ // 1: если дочерний route один, возвращает true
if (showingChildren.length === 1) {
return true;
}
- // 2:如果无子路由, 复制当前路由信息作为其子路由,满足只拥有一个子路由的条件,所以返回 true
+ // 2: если дочерних routes нет, копирует текущий route как дочерний и возвращает true
if (showingChildren.length === 0) {
onlyOneChild.value = { ...parent, path: "", noShowingChildren: true };
return true;
@@ -60,9 +60,9 @@ function hasOneShowingChild(children = [], parent: any) {
}
/**
- * 解析路径
+ * Разбор path
*
- * @param routePath 路由路径
+ * @param routePath Route path
*/
function resolvePath(routePath: string) {
if (isExternal(routePath)) {
@@ -71,14 +71,14 @@ function resolvePath(routePath: string) {
if (isExternal(props.basePath)) {
return props.basePath;
}
- // 完整路径 = 父级路径(/level/level_3) + 路由路径
- const fullPath = path.resolve(props.basePath, routePath); // 相对路径 → 绝对路径
+ // Полный path = parent path (/level/level_3) + route path
+ const fullPath = path.resolve(props.basePath, routePath); // Relative path → absolute path
return fullPath;
}
-
+
+
+
diff --git a/apps/frontend/src/layout/components/Sidebar/index.vue b/apps/frontend/src/layout/components/Sidebar/index.vue
index 86b807b..e95ebeb 100644
--- a/apps/frontend/src/layout/components/Sidebar/index.vue
+++ b/apps/frontend/src/layout/components/Sidebar/index.vue
@@ -43,3 +43,5 @@ const route = useRoute();
+
+
diff --git a/apps/frontend/src/layout/components/TagsView/ScrollPane.vue b/apps/frontend/src/layout/components/TagsView/ScrollPane.vue
index f2b7e7a..7c8a9c7 100644
--- a/apps/frontend/src/layout/components/TagsView/ScrollPane.vue
+++ b/apps/frontend/src/layout/components/TagsView/ScrollPane.vue
@@ -119,3 +119,5 @@ defineExpose({
}
}
+
+
diff --git a/apps/frontend/src/layout/components/TagsView/index.vue b/apps/frontend/src/layout/components/TagsView/index.vue
index ee7c5ef..c4f2415 100644
--- a/apps/frontend/src/layout/components/TagsView/index.vue
+++ b/apps/frontend/src/layout/components/TagsView/index.vue
@@ -41,12 +41,12 @@ watch(
moveToCurrentTag();
},
{
- //初始化立即执行
+ // Выполнить сразу при инициализации
immediate: true,
}
);
-const tagMenuVisible = ref(false); // 标签操作菜单显示状态
+const tagMenuVisible = ref(false); // Состояние отображения меню действий с вкладкой
watch(tagMenuVisible, (value) => {
if (value) {
document.body.addEventListener("click", closeTagMenu);
@@ -264,7 +264,7 @@ onMounted(() => {
-
+
@@ -371,3 +371,5 @@ onMounted(() => {
}
}
+
+
diff --git a/apps/frontend/src/layout/components/index.ts b/apps/frontend/src/layout/components/index.ts
index 6e86a8f..98fbe81 100644
--- a/apps/frontend/src/layout/components/index.ts
+++ b/apps/frontend/src/layout/components/index.ts
@@ -2,3 +2,5 @@ export { default as Navbar } from "./Navbar.vue";
export { default as AppMain } from "./AppMain.vue";
export { default as Settings } from "./Settings/index.vue";
export { default as TagsView } from "./TagsView/index.vue";
+
+
diff --git a/apps/frontend/src/layout/index.vue b/apps/frontend/src/layout/index.vue
index dda75b3..1695a19 100644
--- a/apps/frontend/src/layout/index.vue
+++ b/apps/frontend/src/layout/index.vue
@@ -11,11 +11,11 @@ import { useSettingsStore } from "@/store/modules/settings";
const { width } = useWindowSize();
/**
- * 响应式布局容器固定宽度
+ * Фиксированная ширина контейнера адаптивного layout
*
- * 大屏(>=1200px)
- * 中屏(>=992px)
- * 小屏(>=768px)
+ * Большой экран(>=1200px)
+ * Средний экран(>=992px)
+ * Малый экран(>=768px)
*/
const WIDTH = 992;
@@ -41,7 +41,7 @@ watchEffect(() => {
appStore.toggleDevice("desktop");
if (width.value >= 1200) {
- //大屏
+ //Большой экран
appStore.openSideBar(true);
} else {
appStore.closeSideBar(true);
@@ -56,7 +56,7 @@ function handleOutsideClick() {
-
+
-
+
@@ -127,3 +127,5 @@ function handleOutsideClick() {
opacity: 0.3;
}
+
+
diff --git a/apps/frontend/src/main.ts b/apps/frontend/src/main.ts
index 3b57f7c..7caa488 100644
--- a/apps/frontend/src/main.ts
+++ b/apps/frontend/src/main.ts
@@ -6,21 +6,23 @@ import { setupDirective } from "@/directive";
import "@/permission";
-// 本地SVG图标
+// Локальные SVG-иконки
import "virtual:svg-icons-register";
-// 国际化
+// Локализация
import i18n from "@/lang/index";
-// 样式
+// Стили
import "element-plus/theme-chalk/dark/css-vars.css";
import "@/styles/index.scss";
import "uno.css";
const app = createApp(App);
-// 全局注册 自定义指令(directive)
+// Глобальная регистрация пользовательских директив
setupDirective(app);
-// 全局注册 状态管理(store)
+// Глобальная регистрация store
setupStore(app);
app.use(router).use(i18n).mount("#app");
+
+
diff --git a/apps/frontend/src/permission.ts b/apps/frontend/src/permission.ts
index a5f0d60..737e252 100644
--- a/apps/frontend/src/permission.ts
+++ b/apps/frontend/src/permission.ts
@@ -5,11 +5,11 @@ import { usePermissionStoreHook } from "@/store/modules/permission";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
-NProgress.configure({ showSpinner: false }); // 进度条
+NProgress.configure({ showSpinner: false }); // Индикатор загрузки
const permissionStore = usePermissionStoreHook();
-// 白名单路由
+// Разрешённые маршруты
const whiteList = ["/login", "/register"];
router.beforeEach(async (to, from, next) => {
@@ -17,14 +17,14 @@ router.beforeEach(async (to, from, next) => {
const hasToken = localStorage.getItem("accessToken");
if (hasToken) {
if (to.path === "/login") {
- // 如果已登录,跳转首页
+ // Если пользователь уже вошёл, перейти на главную страницу
next({ path: "/" });
NProgress.done();
} else {
const AccountStore = useAccountStoreHook();
const hasRoles = AccountStore.roles && AccountStore.roles.length > 0;
if (hasRoles) {
- // 未匹配到任何路由,跳转404
+ // Если маршрут не найден, перейти на 404
if (to.matched.length === 0) {
from.name ? next({ name: from.name }) : next("/404");
} else {
@@ -39,7 +39,7 @@ router.beforeEach(async (to, from, next) => {
});
next({ ...to, replace: true });
} catch (error) {
- // 移除 token 并跳转登录页
+ // Удалить token и перейти на страницу входа
await AccountStore.resetToken();
next(`/login?redirect=${to.path}`);
NProgress.done();
@@ -47,7 +47,7 @@ router.beforeEach(async (to, from, next) => {
}
}
} else {
- // 未登录可以访问白名单页面
+ // Без входа доступны только разрешённые страницы
if (whiteList.indexOf(to.path) !== -1) {
next();
} else {
@@ -60,3 +60,5 @@ router.beforeEach(async (to, from, next) => {
router.afterEach(() => {
NProgress.done();
});
+
+
diff --git a/apps/frontend/src/router/index.ts b/apps/frontend/src/router/index.ts
index 82c246a..8f6e34b 100644
--- a/apps/frontend/src/router/index.ts
+++ b/apps/frontend/src/router/index.ts
@@ -7,7 +7,7 @@ import {
export const Layout = () => import("@/layout/index.vue");
-// 静态路由
+// Статические маршруты
export const constantRoutes: RouteRecordRaw[] = [
{
path: "/redirect",
@@ -183,17 +183,17 @@ export const asyncRoutes: any[] = [
];
/**
- * 创建路由
+ * Создание маршрутизатора
*/
const router = createRouter({
history: createWebHashHistory(),
routes: constantRoutes as RouteRecordRaw[],
- // 刷新时,滚动条位置还原
+ // Восстановление позиции прокрутки при обновлении
scrollBehavior: () => ({ left: 0, top: 0 }),
});
/**
- * 重置路由
+ * Сброс маршрутов
*/
export function resetRouter() {
router.replace({ path: "/login" });
@@ -201,3 +201,5 @@ export function resetRouter() {
}
export default router;
+
+
diff --git a/apps/frontend/src/settings.ts b/apps/frontend/src/settings.ts
index 58e7da0..234f53d 100644
--- a/apps/frontend/src/settings.ts
+++ b/apps/frontend/src/settings.ts
@@ -1,46 +1,46 @@
-// 系统设置
+// Системные настройки
interface DefaultSettings {
/**
- * 系统title
+ * Заголовок системы
*/
title: string;
/**
- * 是否显示设置
+ * Показывать настройки
*/
showSettings: boolean;
/**
- * 是否显示多标签导航
+ * Показывать навигацию по вкладкам
*/
tagsView: boolean;
/**
- *是否固定头部
+ *Фиксировать header
*/
fixedHeader: boolean;
/**
- * 是否显示侧边栏Logo
+ * Показывать логотип в боковом меню
*/
sidebarLogo: boolean;
/**
- * 导航栏布局
+ * Layout навигации
*/
layout: string;
/**
- * 主题颜色
+ * Цвет темы
*/
themeColor: string;
/**
- * 主题模式
+ * Тема интерфейса
*/
theme: string;
/**
- * 布局大小
+ * Размер интерфейса
*/
size: string;
/**
- * 语言
+ * Язык
*/
language: string;
}
@@ -54,10 +54,10 @@ const defaultSettings: DefaultSettings = {
layout: "left",
themeColor: "#409EFF",
/**
- * 主题模式
+ * Тема интерфейса
*
- * dark:暗黑模式
- * light: 明亮模式
+ * dark:Тёмная тема
+ * light: Светлая тема
*/
theme: "dark",
size: "default", // default |large |small
@@ -65,3 +65,5 @@ const defaultSettings: DefaultSettings = {
};
export default defaultSettings;
+
+
diff --git a/apps/frontend/src/store/index.ts b/apps/frontend/src/store/index.ts
index e22b67c..9af53e4 100644
--- a/apps/frontend/src/store/index.ts
+++ b/apps/frontend/src/store/index.ts
@@ -3,9 +3,11 @@ import { createPinia } from "pinia";
const store = createPinia();
-// 全局注册 store
+// Глобальная регистрация store
export function setupStore(app: App) {
app.use(store);
}
export { store };
+
+
diff --git a/apps/frontend/src/store/modules/account.ts b/apps/frontend/src/store/modules/account.ts
index f9b6e4d..991c9e7 100644
--- a/apps/frontend/src/store/modules/account.ts
+++ b/apps/frontend/src/store/modules/account.ts
@@ -13,10 +13,10 @@ export const useAccountStore = defineStore("account", () => {
const token = useStorage("accessToken", "");
const id = ref(0);
const username = ref("");
- const roles = ref>([]); // 用户角色编码集合 → 判断路由权限
+ const roles = ref>([]); // Коды ролей пользователя для проверки доступа к маршрутам
/**
- * 登录
+ * Вход
*
* @returns
*/
@@ -34,7 +34,7 @@ export const useAccountStore = defineStore("account", () => {
});
}
- // 查询当前
+ // Запрос текущего пользователя
function getAccountInfo() {
return new Promise((resolve, reject) => {
getAccountInfoApi()
@@ -56,7 +56,7 @@ export const useAccountStore = defineStore("account", () => {
});
}
- // 注销
+ // Выход
function logout() {
return new Promise((resolve, reject) => {
resetRouter();
@@ -65,7 +65,7 @@ export const useAccountStore = defineStore("account", () => {
});
}
- // 重置
+ // Сброс
function resetToken() {
token.value = "";
id.value = 0;
@@ -85,7 +85,9 @@ export const useAccountStore = defineStore("account", () => {
};
});
-// 非setup
+// Вне setup
export function useAccountStoreHook() {
return useAccountStore(store);
}
+
+
diff --git a/apps/frontend/src/store/modules/app.ts b/apps/frontend/src/store/modules/app.ts
index d128c10..0d28057 100644
--- a/apps/frontend/src/store/modules/app.ts
+++ b/apps/frontend/src/store/modules/app.ts
@@ -19,7 +19,7 @@ export const useAppStore = defineStore("app", () => {
});
/**
- * 根据语言标识读取对应的语言包
+ * Загрузка пакета локализации по коду языка
*/
const locale = computed(() => {
return language?.value == "en" ? en : ru;
@@ -56,7 +56,7 @@ export const useAppStore = defineStore("app", () => {
size.value = val;
}
/**
- * 切换语言
+ * Переключение языка
*
* @param val
*/
@@ -78,3 +78,5 @@ export const useAppStore = defineStore("app", () => {
openSideBar,
};
});
+
+
diff --git a/apps/frontend/src/store/modules/permission.ts b/apps/frontend/src/store/modules/permission.ts
index 49a177d..ebfb07b 100644
--- a/apps/frontend/src/store/modules/permission.ts
+++ b/apps/frontend/src/store/modules/permission.ts
@@ -9,13 +9,13 @@ const Layout = () => import("@/layout/index.vue");
/**
* Use meta.role to determine if the current user has permission
*
- * @param roles 用户角色集合
- * @param route 路由
+ * @param roles Набор ролей пользователя
+ * @param route Маршрут
* @returns
*/
const hasPermission = (roles: string[], route: RouteRecordRaw) => {
if (route.meta && route.meta.roles) {
- // 角色【超级管理员】拥有所有权限,忽略校验
+ // Роль admin имеет все права, проверка пропускается
if (roles.includes("admin")) {
return true;
}
@@ -29,19 +29,19 @@ const hasPermission = (roles: string[], route: RouteRecordRaw) => {
};
/**
- * 递归过滤有权限的异步(动态)路由
+ * Рекурсивная фильтрация доступных async/dynamic routes
*
- * @param routes 接口返回的异步(动态)路由
- * @param roles 用户角色集合
- * @returns 返回用户有权限的异步(动态)路由
+ * @param routes Async/dynamic routes, возвращённые API
+ * @param roles Набор ролей пользователя
+ * @returns Возвращает async/dynamic routes, доступные пользователю
*/
const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => {
const asyncRoutes: RouteRecordRaw[] = [];
routes.forEach((route) => {
- const tmpRoute = { ...route }; // ES6扩展运算符复制新对象
+ const tmpRoute = { ...route }; // Копирование объекта через spread operator ES6
- // 判断用户(角色)是否有该路由的访问权限
+ // Проверка доступа пользователя/роли к маршруту
if (hasPermission(roles, tmpRoute)) {
if (tmpRoute.component?.toString() == "Layout") {
tmpRoute.component = Layout;
@@ -76,13 +76,13 @@ export const usePermissionStore = defineStore("permission", () => {
}
/**
- * 生成动态路由
+ * Генерация dynamic routes
*
- * @param roles 用户角色集合
+ * @param roles Набор ролей пользователя
* @returns
*/
function generateRoutes(roles: string[]) {
- // 根据角色获取有访问权限的路由
+ // Получение доступных routes по ролям
const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
setRoutes(accessedRoutes);
return accessedRoutes;
@@ -91,7 +91,9 @@ export const usePermissionStore = defineStore("permission", () => {
return { routes, setRoutes, generateRoutes };
});
-// 非setup
+// Вне setup
export function usePermissionStoreHook() {
return usePermissionStore(store);
}
+
+
diff --git a/apps/frontend/src/store/modules/settings.ts b/apps/frontend/src/store/modules/settings.ts
index c9c8451..c8e082c 100644
--- a/apps/frontend/src/store/modules/settings.ts
+++ b/apps/frontend/src/store/modules/settings.ts
@@ -54,3 +54,5 @@ export const useSettingsStore = defineStore("setting", () => {
changeSetting,
};
});
+
+
diff --git a/apps/frontend/src/store/modules/tagsView.ts b/apps/frontend/src/store/modules/tagsView.ts
index 93c09a5..e5098aa 100644
--- a/apps/frontend/src/store/modules/tagsView.ts
+++ b/apps/frontend/src/store/modules/tagsView.ts
@@ -127,7 +127,7 @@ export const useTagsViewStore = defineStore("tagsView", () => {
return;
}
visitedViews.value = visitedViews.value.filter((item, index) => {
- // affix:true 固定tag,例如“首页”
+ // affix:true Фиксированная вкладка, например главная
if (index >= currIndex || (item.meta && item.meta.affix)) {
return true;
}
@@ -152,7 +152,7 @@ export const useTagsViewStore = defineStore("tagsView", () => {
return;
}
visitedViews.value = visitedViews.value.filter((item, index) => {
- // affix:true 固定tag,例如“首页”
+ // affix:true Фиксированная вкладка, например главная
if (index <= currIndex || (item.meta && item.meta.affix)) {
return true;
}
@@ -216,3 +216,5 @@ export const useTagsViewStore = defineStore("tagsView", () => {
delAllCachedViews,
};
});
+
+
diff --git a/apps/frontend/src/styles/dark.scss b/apps/frontend/src/styles/dark.scss
index 5b719ff..a825093 100644
--- a/apps/frontend/src/styles/dark.scss
+++ b/apps/frontend/src/styles/dark.scss
@@ -31,3 +31,5 @@ html.dark {
}
}
}
+
+
diff --git a/apps/frontend/src/styles/element-plus.scss b/apps/frontend/src/styles/element-plus.scss
index 65008af..b93cd34 100644
--- a/apps/frontend/src/styles/element-plus.scss
+++ b/apps/frontend/src/styles/element-plus.scss
@@ -1,10 +1,10 @@
:root {
- // 这里可以设置你自定义的颜色变量
- // 这个是element主要按钮:active的颜色,当主题更改后此变量的值也随之更改
+ // Здесь можно задать пользовательские переменные цвета
+ // Цвет active-состояния основной кнопки Element Plus, меняется вместе с темой
--el-color-primary-dark: #0d84ff;
}
-// 覆盖 element-plus 的样式
+// Переопределение стилей Element Plus
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
font-weight: 400 !important;
@@ -32,17 +32,19 @@
box-sizing: content-box;
}
-// 选中行背景色值
+// Цвет фона выбранной строки
.el-table__body tr.current-row td {
background-color: #e1f3d8b5 !important;
}
-// card 的header统一高度
+// Единая высота header у card
.el-card__header {
height: 60px !important;
}
-// 表格表头和表体未对齐
+// Исправление несовпадения header и body таблицы
.el-table__header col[name="gutter"] {
display: table-cell !important;
}
+
+
diff --git a/apps/frontend/src/styles/index.scss b/apps/frontend/src/styles/index.scss
index 0b5b9e5..c3d2dce 100644
--- a/apps/frontend/src/styles/index.scss
+++ b/apps/frontend/src/styles/index.scss
@@ -15,3 +15,5 @@
box-shadow: var(--el-box-shadow-light);
}
}
+
+
diff --git a/apps/frontend/src/styles/reset.scss b/apps/frontend/src/styles/reset.scss
index 9b19e4c..40f2fed 100644
--- a/apps/frontend/src/styles/reset.scss
+++ b/apps/frontend/src/styles/reset.scss
@@ -26,7 +26,7 @@ body {
height: 100%;
margin: 0;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
- "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
+ "Microsoft YaHei", "Arial", Arial, sans-serif;
line-height: inherit;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
@@ -44,7 +44,7 @@ svg {
}
svg {
- vertical-align: -0.15em; //因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果
+ vertical-align: -0.15em; //Так как размер icon равен размеру шрифта, добавляем смещение для визуального выравнивания
}
ul,
@@ -73,3 +73,5 @@ a:active,
div:focus {
outline: none;
}
+
+
diff --git a/apps/frontend/src/styles/sidebar.scss b/apps/frontend/src/styles/sidebar.scss
index efaa4e9..0edeedb 100644
--- a/apps/frontend/src/styles/sidebar.scss
+++ b/apps/frontend/src/styles/sidebar.scss
@@ -203,3 +203,5 @@
}
}
}
+
+
diff --git a/apps/frontend/src/styles/variables.module.scss b/apps/frontend/src/styles/variables.module.scss
index 7feccc4..fb221c7 100644
--- a/apps/frontend/src/styles/variables.module.scss
+++ b/apps/frontend/src/styles/variables.module.scss
@@ -1,6 +1,8 @@
-// 导出 variables.module.scss 变量提供给TypeScript使用
+// Экспорт variables.module.scss для использования в TypeScript
:export {
menuBg: $menuBg;
menuText: $menuText;
menuActiveText: $menuActiveText;
}
+
+
diff --git a/apps/frontend/src/styles/variables.scss b/apps/frontend/src/styles/variables.scss
index e11df30..d2a0e6a 100644
--- a/apps/frontend/src/styles/variables.scss
+++ b/apps/frontend/src/styles/variables.scss
@@ -1,4 +1,4 @@
-// 全局SCSS变量
+// Глобальные SCSS-переменные
:root {
--menuBg: #304156;
@@ -20,3 +20,5 @@ $subMenuActiveText: var(--subMenuActiveText);
$subMenuHover: var(--subMenuHover);
$sideBarWidth: 210px;
+
+
diff --git a/apps/frontend/src/types/env.d.ts b/apps/frontend/src/types/env.d.ts
index b74dc75..60d3115 100644
--- a/apps/frontend/src/types/env.d.ts
+++ b/apps/frontend/src/types/env.d.ts
@@ -7,7 +7,7 @@ declare module "*.vue" {
export default component;
}
-// 环境变量 TypeScript的智能提示
+// TypeScript-подсказки для переменных окружения
interface ImportMetaEnv {
VITE_APP_TITLE: string;
VITE_APP_PORT: string;
@@ -17,3 +17,5 @@ interface ImportMetaEnv {
interface ImportMeta {
readonly env: ImportMetaEnv;
}
+
+
diff --git a/apps/frontend/src/types/global.d.ts b/apps/frontend/src/types/global.d.ts
index a659e31..fc836f6 100644
--- a/apps/frontend/src/types/global.d.ts
+++ b/apps/frontend/src/types/global.d.ts
@@ -16,3 +16,5 @@ declare global {
}
}
export {};
+
+
diff --git a/apps/frontend/src/utils/byte.ts b/apps/frontend/src/utils/byte.ts
index 1a60be3..8b3f8e9 100644
--- a/apps/frontend/src/utils/byte.ts
+++ b/apps/frontend/src/utils/byte.ts
@@ -1,11 +1,11 @@
/**
- * 格式化字节大小
- * @param bytes 字节数
- * @param decimals 小数位数,默认为 2
- * @returns 格式化后的字节大小字符串
+ * Форматирование размера в байтах
+ * @param bytes Количество байт
+ * @param decimals Количество знаков после запятой, по умолчанию 2
+ * @returns Отформатированная строка размера
*/
export const formatBytes = (bytes: number, decimals = 2): string => {
- // 检查是否为特殊值
+ // Проверка специального значения
if (bytes === -1) {
return "Unlimited";
}
@@ -13,21 +13,21 @@ export const formatBytes = (bytes: number, decimals = 2): string => {
return "0 Bytes";
}
- // 计算单位和大小
+ // Расчёт единицы и размера
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
- // 返回格式化后的字符串
+ // Возврат отформатированной строки
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
};
export const calculateBytes = (value = 0, unit = "Bytes"): number => {
- // 将单位转换为大写,并去除空格
+ // Приведение единицы к верхнему регистру и удаление пробелов
const formattedUnit = unit.toUpperCase().trim();
- // 定义存储单位和对应的字节数的映射关系
+ // Маппинг единиц хранения на количество байт
const unitToBytes: Record = {
BYTES: 1,
KB: 1024 ** 1,
@@ -40,7 +40,7 @@ export const calculateBytes = (value = 0, unit = "Bytes"): number => {
YB: 1024 ** 8,
};
- // 检查传入的单位是否存在于映射关系中
+ // Проверка наличия единицы в маппинге
if (!Object.prototype.hasOwnProperty.call(unitToBytes, formattedUnit)) {
throw new Error("Invalid unit");
}
@@ -49,47 +49,49 @@ export const calculateBytes = (value = 0, unit = "Bytes"): number => {
return -1;
}
- // 计算并返回字节数
+ // Расчёт и возврат количества байт
return value * unitToBytes[formattedUnit];
};
/**
- * 格式化存储容量单位
- * @param bytes 存储容量(字节数)
- * @param decimals 小数位数,默认为 2
- * @returns 格式化后的存储容量值
+ * Форматирование единицы хранения
+ * @param bytes Объём хранения в байтах
+ * @param decimals Количество знаков после запятой, по умолчанию 2
+ * @returns Отформатированное значение объёма хранения
*/
export const formatStorageCapacity = (bytes: number, decimals = 2): number => {
- // 检查输入是否有效
+ // Проверка корректности входных данных
if (!bytes || bytes <= 0) {
return bytes;
}
- // 计算存储单位
+ // Расчёт единицы хранения
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const i = Math.floor(Math.log(bytes) / Math.log(k));
- // 格式化存储容量值并返回
+ // Форматирование и возврат объёма хранения
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
};
/**
- * 格式化存储容量单位
- * @param bytes 存储容量(字节数)
- * @returns 格式化后的存储单位
+ * Форматирование единицы хранения
+ * @param bytes Объём хранения в байтах
+ * @returns Отформатированная единица хранения
*/
export const formatStorageUnit = (bytes: number): string => {
- // 检查输入是否有效
+ // Проверка корректности входных данных
if (!bytes || bytes <= 0) {
return "Bytes";
}
- // 计算存储单位
+ // Расчёт единицы хранения
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
- // 返回格式化后的存储单位
+ // Возврат отформатированной единицы хранения
return sizes[i];
};
+
+
diff --git a/apps/frontend/src/utils/copy.ts b/apps/frontend/src/utils/copy.ts
index ad65f59..3040c18 100644
--- a/apps/frontend/src/utils/copy.ts
+++ b/apps/frontend/src/utils/copy.ts
@@ -1,5 +1,5 @@
/**
- * 浅拷贝,忽略 null,支持嵌套对象
+ * Поверхностное копирование с игнорированием null и поддержкой вложенных объектов
* @param target
* @param source
*/
@@ -26,7 +26,7 @@ export const assignWith = (target: T, source: Partial): void => {
};
/**
- * 深拷贝,忽略 null,支持嵌套对象
+ * Глубокое копирование с игнорированием null и поддержкой вложенных объектов
* @param source
*/
export const deepCopy = (source: Partial): T => {
@@ -49,3 +49,5 @@ export const deepCopy = (source: Partial): T => {
return objCopy as T;
};
+
+
diff --git a/apps/frontend/src/utils/i18n.ts b/apps/frontend/src/utils/i18n.ts
index a2d094d..d67f34b 100644
--- a/apps/frontend/src/utils/i18n.ts
+++ b/apps/frontend/src/utils/i18n.ts
@@ -2,7 +2,7 @@
import i18n from "@/lang/index";
export function translateRouteTitleI18n(title: any) {
- // 判断是否存在国际化配置,如果没有原生返回
+ // Проверяет наличие локализованного заголовка, иначе возвращает исходное значение
const hasKey = i18n.global.te("route." + title);
if (hasKey) {
const translatedTitle = i18n.global.t("route." + title);
@@ -10,3 +10,5 @@ export function translateRouteTitleI18n(title: any) {
}
return title;
}
+
+
diff --git a/apps/frontend/src/utils/index.ts b/apps/frontend/src/utils/index.ts
index 0d62660..8d1ed22 100644
--- a/apps/frontend/src/utils/index.ts
+++ b/apps/frontend/src/utils/index.ts
@@ -37,3 +37,5 @@ export function isExternal(path: string) {
const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
return isExternal;
}
+
+
diff --git a/apps/frontend/src/utils/request.ts b/apps/frontend/src/utils/request.ts
index a0fa112..a2d0d99 100644
--- a/apps/frontend/src/utils/request.ts
+++ b/apps/frontend/src/utils/request.ts
@@ -2,14 +2,14 @@ import axios, { InternalAxiosRequestConfig, AxiosResponse } from "axios";
import { useAccountStoreHook } from "@/store/modules/account";
const dynamicBase = (window as any).__dynamic_base__ || "";
-// 创建 axios 实例
+// Создание axios instance
const service = axios.create({
baseURL: `${dynamicBase}${import.meta.env.VITE_APP_BASE_API}`,
timeout: 50000,
headers: { "Content-Type": "application/json;charset=utf-8" },
});
-// 请求拦截器
+// Request interceptor
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const accountStore = useAccountStoreHook();
@@ -23,40 +23,42 @@ service.interceptors.request.use(
}
);
-// 响应拦截器
+// Response interceptor
service.interceptors.response.use(
(response: AxiosResponse) => {
const { code, message } = response.data;
if (code === 20000) {
return response.data;
}
- // 响应数据为二进制流处理(文件导出)
+ // Обработка бинарного ответа при экспорте файлов
if (response.data instanceof ArrayBuffer || response.data instanceof Blob) {
return response;
}
- ElMessage.error(message || "系统出错");
+ ElMessage.error(message || "Системная ошибка");
return Promise.reject(new Error(message || "Error"));
},
(error: any) => {
if (error.response.data) {
const { code, msg } = error.response.data;
- // token 过期,重新登录
+ // Token истёк, нужен повторный вход
if (code === "A0230") {
- ElMessageBox.confirm("当前页面已失效,请重新登录", "提示", {
- confirmButtonText: "确定",
+ ElMessageBox.confirm("Текущая сессия истекла, войдите снова", "Подтверждение", {
+ confirmButtonText: "ОК",
type: "warning",
}).then(() => {
localStorage.clear();
window.location.href = "/";
});
} else {
- ElMessage.error(msg || "系统出错");
+ ElMessage.error(msg || "Системная ошибка");
}
}
return Promise.reject(error.message);
}
);
-// 导出 axios 实例
+// Export axios instance
export default service;
+
+
diff --git a/apps/frontend/src/utils/scroll-to.ts b/apps/frontend/src/utils/scroll-to.ts
index c4e48fc..12d46db 100644
--- a/apps/frontend/src/utils/scroll-to.ts
+++ b/apps/frontend/src/utils/scroll-to.ts
@@ -67,3 +67,5 @@ export const scrollTo = (to: number, duration: number, callback?: any) => {
};
animateScroll();
};
+
+
diff --git a/apps/frontend/src/utils/time.ts b/apps/frontend/src/utils/time.ts
index f950375..e0e0507 100644
--- a/apps/frontend/src/utils/time.ts
+++ b/apps/frontend/src/utils/time.ts
@@ -1,7 +1,7 @@
/**
- * 将时间戳转换为格式化日期时间字符串(YYYY-MM-DD HH:mm:ss)
- * @param timestamp 时间戳
- * @returns 格式化日期时间字符串
+ * Преобразует timestamp в строку даты и времени формата YYYY-MM-DD HH:mm:ss.
+ * @param timestamp Timestamp.
+ * @returns Отформатированная строка даты и времени.
*/
export const timestampToDateTime = (timestamp: number): string => {
const date = new Date(timestamp);
@@ -35,24 +35,24 @@ export const calculateTimeDifference = (timestamp: number): string => {
const parts: string[] = [];
if (days > 0) {
- parts.push(`${days}天`);
+ parts.push(`${days} дн.`);
}
if (remainingHours > 0) {
- parts.push(`${remainingHours}小时`);
+ parts.push(`${remainingHours} ч.`);
}
if (remainingMinutes > 0) {
- parts.push(`${remainingMinutes}分钟`);
+ parts.push(`${remainingMinutes} мин.`);
}
if (remainingSeconds > 0) {
- parts.push(`${remainingSeconds}秒`);
+ parts.push(`${remainingSeconds} с.`);
}
return parts.join(" ");
};
/**
- * 获取一小时后的时间戳
- * @returns 一周后的时间戳
+ * Возвращает timestamp через один час.
+ * @returns Timestamp через один час.
*/
export const getHourLater = (): number => {
const date = new Date();
@@ -61,8 +61,8 @@ export const getHourLater = (): number => {
};
/**
- * 获取一天后的时间戳
- * @returns 一周后的时间戳
+ * Возвращает timestamp через один день.
+ * @returns Timestamp через один день.
*/
export const getDayLater = (): number => {
const date = new Date();
@@ -71,8 +71,8 @@ export const getDayLater = (): number => {
};
/**
- * 获取一周后的时间戳
- * @returns 一周后的时间戳
+ * Возвращает timestamp через одну неделю.
+ * @returns Timestamp через одну неделю.
*/
export const getWeekLater = (): number => {
const date = new Date();
@@ -81,8 +81,8 @@ export const getWeekLater = (): number => {
};
/**
- * 获取一个月后的时间戳
- * @returns 一个月后的时间戳
+ * Возвращает timestamp через один месяц.
+ * @returns Timestamp через один месяц.
*/
export const getMonthLater = (): number => {
const date = new Date();
@@ -91,11 +91,13 @@ export const getMonthLater = (): number => {
};
/**
- * 获取一年后的时间戳
- * @returns 一年后的时间戳
+ * Возвращает timestamp через один год.
+ * @returns Timestamp через один год.
*/
export const getYearLater = (): number => {
const date = new Date();
date.setFullYear(date.getFullYear() + 1);
return date.getTime();
};
+
+
diff --git a/apps/frontend/src/views/account/list/index.vue b/apps/frontend/src/views/account/list/index.vue
index 975b2e4..beb5790 100644
--- a/apps/frontend/src/views/account/list/index.vue
+++ b/apps/frontend/src/views/account/list/index.vue
@@ -493,9 +493,9 @@ import { useRoute } from "vue-router";
const { t } = useI18n();
const route = useRoute();
-const queryFormRef = ref(ElForm); // 查询表单
-const dataFormRef = ref(ElForm); // 用户表单
-const kickFormRef = ref(ElForm); // 下线表单
+const queryFormRef = ref(ElForm); // Форма поиска
+const dataFormRef = ref(ElForm); // Форма пользователя
+const kickFormRef = ref(ElForm); // Форма отключения пользователя
const dataFormPassRef = ref(ElInput);
const dataFormConPassRef = ref(ElInput);
@@ -694,7 +694,7 @@ const resetDataForm = () => {
};
/**
- * 查询
+ * Поиск
*/
const handleQuery = async () => {
state.loading = true;
@@ -708,7 +708,7 @@ const handleQuery = async () => {
};
/**
- * 重置
+ * Сброс
*/
const resetQuery = () => {
queryFormRef.value.resetFields();
@@ -716,7 +716,7 @@ const resetQuery = () => {
};
/**
- * 保存
+ * Сохранение
**/
const handleAdd = () => {
state.dialog = {
@@ -726,7 +726,7 @@ const handleAdd = () => {
};
/**
- * 修改
+ * Изменение
**/
const handleUpdate = async (row: { [key: string]: any }) => {
const id = row.id;
@@ -744,7 +744,7 @@ const setQuota = (newQuota: number) => {
};
/**
- * 表单提交
+ * Отправка формы
*/
const submitForm = () => {
dataFormRef.value.validate((valid: any) => {
@@ -769,7 +769,7 @@ const submitForm = () => {
};
/**
- * 下线表单提交
+ * Отправка формы отключения пользователя
*/
const submitKickForm = () => {
kickFormRef.value.validate((valid: any) => {
@@ -785,7 +785,7 @@ const submitKickForm = () => {
};
/**
- * 删除
+ * Удаление
*/
const handleDelete = (row: { [key: string]: any }) => {
const id = row.id;
@@ -811,7 +811,7 @@ const handleDelete = (row: { [key: string]: any }) => {
};
/**
- * 强制用户下线
+ * Принудительное отключение пользователя
* @param row
*/
const handleKick = (row: { [key: string]: any }) => {
@@ -823,7 +823,7 @@ const handleKick = (row: { [key: string]: any }) => {
};
/**
- * 解除下线状态
+ * Снятие статуса отключения
* @param row
*/
const confirmReleaseKick = (row: { [key: string]: any }) => {
@@ -834,7 +834,7 @@ const confirmReleaseKick = (row: { [key: string]: any }) => {
};
/**
- * 关闭用户弹窗
+ * Закрытие окна пользователя
*/
const closeDialog = () => {
dialog.value.visible = false;
@@ -847,7 +847,7 @@ const closeDialog = () => {
};
/**
- * 关闭下线弹窗
+ * Закрытие окна отключения
*/
const closeDialogKick = () => {
dialogKick.value.visible = false;
@@ -856,7 +856,7 @@ const closeDialogKick = () => {
};
/**
- * 导入
+ * Импорт
*/
const handleImport = (params: UploadRequestOptions) => {
if (state.fileList.length > 0) {
@@ -881,7 +881,7 @@ const beforeImport = (file: UploadRawFile) => {
};
/**
- * 导出
+ * Экспорт
*/
const handleExport = () => {
exportAccountApi().then((res) => {
@@ -894,7 +894,7 @@ const handleExport = () => {
a.href = url;
let dis = res.headers["content-disposition"];
a.download = dis.split("attachment; filename=")[1];
- // 模拟点击下载
+ // Имитация клика для скачивания
a.click();
window.URL.revokeObjectURL(url);
ElMessage.success(t("common.success"));
@@ -955,7 +955,7 @@ const resetTraffic = async (row: { [key: string]: any }) => {
};
onMounted(() => {
- // 初始化用户列表数据
+ // Инициализация списка пользователей
handleQuery();
if (route.query.focus === "change-pass") {
nextTick(() => {
@@ -973,3 +973,5 @@ onMounted(() => {
}
});
+
+
diff --git a/apps/frontend/src/views/config/list/index.vue b/apps/frontend/src/views/config/list/index.vue
index d0acf4c..1c1a2b1 100644
--- a/apps/frontend/src/views/config/list/index.vue
+++ b/apps/frontend/src/views/config/list/index.vue
@@ -406,7 +406,7 @@ const handleExport = async () => {
a.href = url;
let dis = response.headers["content-disposition"];
a.download = dis.split("attachment; filename=")[1];
- // 模拟点击下载
+ // Имитация клика для скачивания
a.click();
window.URL.revokeObjectURL(url);
ElMessage.success(t("common.success"));
@@ -484,3 +484,5 @@ onMounted(() => {
margin: 0 auto;
}
+
+
diff --git a/apps/frontend/src/views/error-page/401.vue b/apps/frontend/src/views/error-page/401.vue
index f1afe6e..5cff666 100644
--- a/apps/frontend/src/views/error-page/401.vue
+++ b/apps/frontend/src/views/error-page/401.vue
@@ -1,4 +1,4 @@
-
+
+
+
diff --git a/apps/frontend/src/views/log/system/index.vue b/apps/frontend/src/views/log/system/index.vue
index 0ddd70d..8b0124e 100644
--- a/apps/frontend/src/views/log/system/index.vue
+++ b/apps/frontend/src/views/log/system/index.vue
@@ -94,7 +94,7 @@ const handleExport = async () => {
a.href = url;
let dis = response.headers["content-disposition"];
a.download = dis.split("attachment; filename=")[1];
- // 模拟点击下载
+ // Имитация клика для скачивания
a.click();
window.URL.revokeObjectURL(url);
ElMessage.success(t("common.success"));
@@ -109,3 +109,5 @@ onMounted(() => {
+
+
diff --git a/apps/frontend/src/views/login/index.vue b/apps/frontend/src/views/login/index.vue
index e5a46df..44a1fe6 100644
--- a/apps/frontend/src/views/login/index.vue
+++ b/apps/frontend/src/views/login/index.vue
@@ -76,10 +76,10 @@ import router from "@/router";
import LangSelect from "@/components/LangSelect/index.vue";
import SvgIcon from "@/components/SvgIcon/index.vue";
-// 状态管理依赖
+// Зависимость store
import { useAccountStore } from "@/store/modules/account";
-// API依赖
+// Зависимость API
import { LocationQuery, LocationQueryValue, useRoute } from "vue-router";
import { AccountLoginDto } from "@/api/account/types";
@@ -87,25 +87,25 @@ const accountStore = useAccountStore();
const route = useRoute();
/**
- * 按钮loading
+ * Состояние загрузки кнопки
*/
const loading = ref(false);
/**
- * 是否大写锁定
+ * Включён ли Caps Lock
*/
const isCapslock = ref(false);
/**
- * 密码是否可见
+ * Видимость пароля
*/
const passVisible = ref(false);
/**
- * 登录表单引用
+ * Ссылка на форму входа
*/
const loginFormRef = ref(ElForm);
/**
- * 登录表单
+ * Форма входа
*/
const loginForm = ref({
username: "",
@@ -140,7 +140,7 @@ const loginRules = {
};
/**
- * 检查输入大小写状态
+ * Проверка состояния регистра ввода
*/
const checkCapslock = (e: any) => {
const { key } = e;
@@ -148,7 +148,7 @@ const checkCapslock = (e: any) => {
};
/**
- * 登录
+ * Вход
*/
const handleLogin = () => {
loginFormRef.value.validate((valid: boolean) => {
@@ -208,7 +208,7 @@ const handleLogin = () => {
.el-input {
background: transparent;
- // 子组件 scoped 无效,使用 :deep
+ // scoped не действует на дочерний компонент, используется :deep
:deep(.el-input__wrapper) {
padding: 0;
background: transparent;
@@ -226,7 +226,7 @@ const handleLogin = () => {
-webkit-text-fill-color: #fff !important;
}
- // 设置输入框自动填充的延迟属性
+ // Настройка задержки автозаполнения поля ввода
&:-webkit-autofill,
&:-webkit-autofill:hover,
&:-webkit-autofill:focus,
@@ -238,3 +238,5 @@ const handleLogin = () => {
}
}
+
+
diff --git a/apps/frontend/src/views/monitor/system/index.vue b/apps/frontend/src/views/monitor/system/index.vue
index 453eacd..5be085f 100644
--- a/apps/frontend/src/views/monitor/system/index.vue
+++ b/apps/frontend/src/views/monitor/system/index.vue
@@ -220,3 +220,5 @@ onMounted(() => {
}
}
+
+
diff --git a/apps/frontend/src/views/redirect/index.vue b/apps/frontend/src/views/redirect/index.vue
index 2b61386..9e201c6 100644
--- a/apps/frontend/src/views/redirect/index.vue
+++ b/apps/frontend/src/views/redirect/index.vue
@@ -13,3 +13,5 @@ const { path } = params;
router.replace({ path: "/" + path, query });
+
+
diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json
index c4776f4..33eb7cf 100644
--- a/apps/frontend/tsconfig.json
+++ b/apps/frontend/tsconfig.json
@@ -17,7 +17,7 @@
},
"types": ["vite/client", "element-plus/global", "unplugin-icons/types/vue"],
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
- "allowSyntheticDefaultImports": true /* 允许默认导入 */,
+ "allowSyntheticDefaultImports": true /* Allow default imports. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */
},
"include": [
diff --git a/apps/frontend/vite.config.ts b/apps/frontend/vite.config.ts
index baa902e..2a9510c 100644
--- a/apps/frontend/vite.config.ts
+++ b/apps/frontend/vite.config.ts
@@ -33,7 +33,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
},
},
css: {
- // CSS 预处理器
+ // CSS preprocessor
preprocessorOptions: {
//define global scss variable
scss: {
@@ -47,9 +47,9 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
server: {
host: "0.0.0.0",
port: Number(env.VITE_APP_PORT),
- open: true, // 运行是否自动打开浏览器
+ open: true, // Automatically open browser on start
proxy: {
- // 反向代理解决跨域
+ // Reverse proxy for CORS in development
[env.VITE_APP_BASE_API]: {
target: "http://127.0.0.1:8081",
changeOrigin: true,
@@ -63,7 +63,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
/* options */
}),
AutoImport({
- // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
+ // Auto import Vue helpers: ref, reactive, toRef, etc.
imports: ["vue", "@vueuse/core"],
eslintrc: {
enabled: false, // Default `false`
@@ -71,36 +71,36 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
globalsPropValue: true, // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable')
},
resolvers: [
- // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
+ // Auto import Element Plus helpers with styles: ElMessage, ElMessageBox, etc.
ElementPlusResolver(),
- // 自动导入图标组件
+ // Auto import icon components
IconsResolver({}),
],
- vueTemplate: true, // 是否在 vue 模板中自动导入
- dts: path.resolve(pathSrc, "types", "auto-imports.d.ts"), // 自动导入组件类型声明文件位置,默认根目录; false 关闭自动生成
+ vueTemplate: true, // Auto import in Vue templates
+ dts: path.resolve(pathSrc, "types", "auto-imports.d.ts"), // Type declarations for auto imports; false disables generation
}),
Components({
resolvers: [
- // 自动注册图标组件
+ // Auto register icon components
IconsResolver({
- enabledCollections: ["ep"], //@iconify-json/ep 是 Element Plus 的图标库
+ enabledCollections: ["ep"], // @iconify-json/ep is the Element Plus icon collection
}),
- // 自动导入 Element Plus 组件
+ // Auto import Element Plus components
ElementPlusResolver(),
],
- dts: path.resolve(pathSrc, "types", "components.d.ts"), // 自动导入组件类型声明文件位置,默认根目录; false 关闭自动生成
+ dts: path.resolve(pathSrc, "types", "components.d.ts"), // Type declarations for auto components; false disables generation
}),
Icons({
- // 自动安装图标库
+ // Auto install icon collections
autoInstall: true,
}),
createSvgIconsPlugin({
- // 指定需要缓存的图标文件夹
+ // Icon folders to cache
iconDirs: [path.resolve(pathSrc, "assets/icons")],
- // 指定symbolId格式
+ // symbolId format
symbolId: "icon-[dir]-[name]",
}),
],
diff --git a/apps/main.go b/apps/main.go
index e87ba4a..0743871 100644
--- a/apps/main.go
+++ b/apps/main.go
@@ -5,3 +5,4 @@ import "hy2xs-admin/cmd"
func main() {
cmd.Execute()
}
+
diff --git a/apps/middleware/admin.go b/apps/middleware/admin.go
index 27ce1af..64c0aa1 100644
--- a/apps/middleware/admin.go
+++ b/apps/middleware/admin.go
@@ -24,3 +24,4 @@ func AdminHandler() gin.HandlerFunc {
c.Next()
}
}
+
diff --git a/apps/middleware/cron.go b/apps/middleware/cron.go
index 84297a7..cc770c5 100644
--- a/apps/middleware/cron.go
+++ b/apps/middleware/cron.go
@@ -31,3 +31,4 @@ func InitCron() error {
c.Start()
return nil
}
+
diff --git a/apps/middleware/filter.go b/apps/middleware/filter.go
index 2cc7a51..1748723 100644
--- a/apps/middleware/filter.go
+++ b/apps/middleware/filter.go
@@ -23,3 +23,4 @@ func FilterHandler() gin.HandlerFunc {
c.Next()
}
}
+
diff --git a/apps/middleware/jwt.go b/apps/middleware/jwt.go
index 0047d70..66bf478 100644
--- a/apps/middleware/jwt.go
+++ b/apps/middleware/jwt.go
@@ -36,3 +36,4 @@ func JWTHandler() gin.HandlerFunc {
c.Next()
}
}
+
diff --git a/apps/middleware/log.go b/apps/middleware/log.go
index a4b385a..6eb7fe2 100644
--- a/apps/middleware/log.go
+++ b/apps/middleware/log.go
@@ -41,3 +41,4 @@ func LogHandler() gin.HandlerFunc {
c.Next()
}
}
+
diff --git a/apps/middleware/rate_limiter.go b/apps/middleware/rate_limiter.go
index eeb7c77..7a047b6 100644
--- a/apps/middleware/rate_limiter.go
+++ b/apps/middleware/rate_limiter.go
@@ -24,3 +24,4 @@ func RateLimiterHandler() gin.HandlerFunc {
c.Next()
}
}
+
diff --git a/apps/model/bo/account.go b/apps/model/bo/account.go
index b04b911..e9229c8 100644
--- a/apps/model/bo/account.go
+++ b/apps/model/bo/account.go
@@ -28,3 +28,4 @@ type AccountExport struct {
ConAt int64 `json:"conAt"`
Remark string `json:"remark"`
}
+
diff --git a/apps/model/bo/hysteria2.go b/apps/model/bo/hysteria2.go
index 9005662..502f304 100644
--- a/apps/model/bo/hysteria2.go
+++ b/apps/model/bo/hysteria2.go
@@ -203,3 +203,4 @@ type serverConfigMasquerade struct {
ListenHTTPS *string `yaml:"listenHTTPS,omitempty" json:"listenHTTPS" validate:"omitempty"`
ForceHTTPS *bool `yaml:"forceHTTPS,omitempty" json:"forceHTTPS" validate:"omitempty"`
}
+
diff --git a/apps/model/bo/hysteria2_api.go b/apps/model/bo/hysteria2_api.go
index 6bfc578..477f308 100644
--- a/apps/model/bo/hysteria2_api.go
+++ b/apps/model/bo/hysteria2_api.go
@@ -4,3 +4,4 @@ type Hysteria2UserTraffic struct {
Tx int64 `json:"tx"` // upload
Rx int64 `json:"rx"` // download
}
+
diff --git a/apps/model/bo/subscribe.go b/apps/model/bo/subscribe.go
index 7b286d5..6d4a740 100644
--- a/apps/model/bo/subscribe.go
+++ b/apps/model/bo/subscribe.go
@@ -25,3 +25,4 @@ type ClashConfig struct {
Proxies []interface{} `yaml:"proxies"`
ProxyGroups []ProxyGroup `yaml:"proxy-groups"`
}
+
diff --git a/apps/model/constant/client.go b/apps/model/constant/client.go
index a7b3697..d80931f 100644
--- a/apps/model/constant/client.go
+++ b/apps/model/constant/client.go
@@ -6,3 +6,4 @@ const (
V2rayN = "v2rayn"
NekoBox = "nekobox"
)
+
diff --git a/apps/model/constant/code.go b/apps/model/constant/code.go
index e42e6a4..e336639 100644
--- a/apps/model/constant/code.go
+++ b/apps/model/constant/code.go
@@ -7,3 +7,4 @@ const (
CodeForbiddenError int = 50403
CodeInvalidError int = 50001
)
+
diff --git a/apps/model/constant/config.go b/apps/model/constant/config.go
index 5e2f4c3..0bd7d29 100644
--- a/apps/model/constant/config.go
+++ b/apps/model/constant/config.go
@@ -14,3 +14,4 @@ const (
ResetTrafficCron = "RESET_TRAFFIC_CRON"
ClashExtension = "CLASH_EXTENSION"
)
+
diff --git a/apps/model/constant/error.go b/apps/model/constant/error.go
index 468de5e..56a072f 100644
--- a/apps/model/constant/error.go
+++ b/apps/model/constant/error.go
@@ -12,3 +12,4 @@ const (
WrongPassword string = "wrong password"
ConfigNotExist string = "config not exist"
)
+
diff --git a/apps/model/constant/system.go b/apps/model/constant/system.go
index f41ac13..33810b7 100644
--- a/apps/model/constant/system.go
+++ b/apps/model/constant/system.go
@@ -18,3 +18,4 @@ const (
Version = "v0.0.22"
)
+
diff --git a/apps/model/dto/account.go b/apps/model/dto/account.go
index 50b2915..e8f2e37 100644
--- a/apps/model/dto/account.go
+++ b/apps/model/dto/account.go
@@ -34,3 +34,4 @@ type AccountUpdateDto struct {
Deleted *int64 `json:"deleted" form:"deleted" validate:"omitempty,oneof=0 1"`
Remark *string `json:"remark" form:"remark" validate:"omitempty,min=0,max=32"`
}
+
diff --git a/apps/model/dto/config.go b/apps/model/dto/config.go
index 0646770..a139463 100644
--- a/apps/model/dto/config.go
+++ b/apps/model/dto/config.go
@@ -16,3 +16,4 @@ type ConfigUpdateDto struct {
type ConfigsUpdateDto struct {
ConfigUpdateDtos []ConfigUpdateDto `json:"configUpdateDtos" form:"configUpdateDtos" validate:"required"`
}
+
diff --git a/apps/model/dto/dto.go b/apps/model/dto/dto.go
index c1f234d..88c0c7f 100644
--- a/apps/model/dto/dto.go
+++ b/apps/model/dto/dto.go
@@ -1,12 +1,13 @@
package dto
type BaseDto struct {
- PageNum *int64 `json:"pageNum" form:"pageNum" validate:"required,gt=0"` // 页号
- PageSize *int64 `json:"pageSize" form:"pageSize" validate:"required,gt=0"` // 页大小
- StartTime *int64 `json:"startTime" form:"startTime" validate:"omitempty,gt=0"` // 开始时间
- EndTime *int64 `json:"endTime" form:"endTime" validate:"omitempty,gt=0"` // 结束时间
+ PageNum *int64 `json:"pageNum" form:"pageNum" validate:"required,gt=0"` // Номер страницы
+ PageSize *int64 `json:"pageSize" form:"pageSize" validate:"required,gt=0"` // Размер страницы
+ StartTime *int64 `json:"startTime" form:"startTime" validate:"omitempty,gt=0"` // Время начала
+ EndTime *int64 `json:"endTime" form:"endTime" validate:"omitempty,gt=0"` // Время окончания
}
type IdDto struct {
- Id *int64 `json:"id" form:"id" validate:"required,gt=0"` // 主键
+ Id *int64 `json:"id" form:"id" validate:"required,gt=0"` // Первичный ключ
}
+
diff --git a/apps/model/dto/hysteria2.go b/apps/model/dto/hysteria2.go
index 96592a5..e41d8b2 100644
--- a/apps/model/dto/hysteria2.go
+++ b/apps/model/dto/hysteria2.go
@@ -8,7 +8,7 @@ type Hysteria2AuthDto struct {
type Hysteria2KickDto struct {
Ids []int64 `json:"ids" form:"ids" validate:"required"`
- KickUtilTime *int64 `json:"kickUtilTime" form:"kickUtilTime" validate:"required"` // 解禁时间
+ KickUtilTime *int64 `json:"kickUtilTime" form:"kickUtilTime" validate:"required"` // Время снятия блокировки
}
type Hysteria2VersionDto struct {
@@ -25,3 +25,4 @@ type Hysteria2UrlDto struct {
AccountId *int64 `json:"accountId" form:"accountId" validate:"required,gt=0"`
Hostname *string `json:"hostname" form:"hostname" validate:"required,min=1,max=255"`
}
+
diff --git a/apps/model/dto/log.go b/apps/model/dto/log.go
index 980843e..58d9694 100644
--- a/apps/model/dto/log.go
+++ b/apps/model/dto/log.go
@@ -7,3 +7,4 @@ type LogDto struct {
type LogExportDto struct {
Option *int `json:"option" form:"option" validate:"required,oneof=0 1"`
}
+
diff --git a/apps/model/dto/server.go b/apps/model/dto/server.go
index 66b93b8..5e59346 100644
--- a/apps/model/dto/server.go
+++ b/apps/model/dto/server.go
@@ -3,3 +3,4 @@ package dto
type ServerDto struct {
Port *int64 `json:"port" form:"port" validate:"required,min=1,max=65535"`
}
+
diff --git a/apps/model/entity/account.go b/apps/model/entity/account.go
index bb98785..1a7d0b7 100644
--- a/apps/model/entity/account.go
+++ b/apps/model/entity/account.go
@@ -18,3 +18,4 @@ type Account struct {
ConAt *int64 `gorm:"column:con_at;default:0" json:"conAt"`
Remark *string `gorm:"column:remark;default:''" json:"remark"`
}
+
diff --git a/apps/model/entity/config.go b/apps/model/entity/config.go
index e4ee747..aac7b87 100644
--- a/apps/model/entity/config.go
+++ b/apps/model/entity/config.go
@@ -6,3 +6,4 @@ type Config struct {
Remark *string `gorm:"column:remark;default:''" json:"remark"`
BaseEntity `gorm:"embedded"`
}
+
diff --git a/apps/model/entity/entity.go b/apps/model/entity/entity.go
index 897be17..eb07537 100644
--- a/apps/model/entity/entity.go
+++ b/apps/model/entity/entity.go
@@ -7,3 +7,4 @@ type BaseEntity struct {
CreateTime *time.Time `gorm:"column:create_time;default:null" json:"createTime"`
UpdateTime *time.Time `gorm:"column:update_time;default:null" json:"updateTime"`
}
+
diff --git a/apps/model/vo/account.go b/apps/model/vo/account.go
index 415a5e6..1cd3f08 100644
--- a/apps/model/vo/account.go
+++ b/apps/model/vo/account.go
@@ -29,3 +29,4 @@ type AccountInfoVo struct {
Username string `json:"username"`
Roles []string `json:"roles"`
}
+
diff --git a/apps/model/vo/config.go b/apps/model/vo/config.go
index 3f1019c..c61da54 100644
--- a/apps/model/vo/config.go
+++ b/apps/model/vo/config.go
@@ -5,3 +5,4 @@ type ConfigVo struct {
Value string `json:"value"`
Remark string `json:"remark"`
}
+
diff --git a/apps/model/vo/hysteria2.go b/apps/model/vo/hysteria2.go
index e37e074..2bc0c9b 100644
--- a/apps/model/vo/hysteria2.go
+++ b/apps/model/vo/hysteria2.go
@@ -38,3 +38,4 @@ type Hysteria2AcmePathVo struct {
CrtPath string `json:"crtPath"`
KeyPath string `json:"keyPath"`
}
+
diff --git a/apps/model/vo/jwt.go b/apps/model/vo/jwt.go
index 7b0440a..caea165 100644
--- a/apps/model/vo/jwt.go
+++ b/apps/model/vo/jwt.go
@@ -4,3 +4,4 @@ type JwtVo struct {
TokenType string `json:"tokenType"`
AccessToken string `json:"accessToken"`
}
+
diff --git a/apps/model/vo/log.go b/apps/model/vo/log.go
index f4fc456..2220665 100644
--- a/apps/model/vo/log.go
+++ b/apps/model/vo/log.go
@@ -21,3 +21,4 @@ type LogHysteria2Vo struct {
Msg string `json:"msg"`
Time string `json:"time"`
}
+
diff --git a/apps/model/vo/monitor.go b/apps/model/vo/monitor.go
index fefbc12..c0259fd 100644
--- a/apps/model/vo/monitor.go
+++ b/apps/model/vo/monitor.go
@@ -8,8 +8,9 @@ type SystemMonitorVo struct {
}
type Hysteria2MonitorVo struct {
- UserTotal int64 `json:"userTotal"` // 在线用户数
- DeviceTotal int64 `json:"deviceTotal"` // 在线设备数
- Version string `json:"version"` // 版本
- Running bool `json:"running"` // 运行状态
+ UserTotal int64 `json:"userTotal"` // Количество пользователей онлайн
+ DeviceTotal int64 `json:"deviceTotal"` // Количество устройств онлайн
+ Version string `json:"version"` // Версия
+ Running bool `json:"running"` // Статус выполнения
}
+
diff --git a/apps/model/vo/result.go b/apps/model/vo/result.go
index 3e9e331..49c0b87 100644
--- a/apps/model/vo/result.go
+++ b/apps/model/vo/result.go
@@ -44,3 +44,4 @@ func Fail(message string, c *gin.Context) {
Data: nil,
})
}
+
diff --git a/apps/model/vo/vo.go b/apps/model/vo/vo.go
index 6fa534a..4f5c816 100644
--- a/apps/model/vo/vo.go
+++ b/apps/model/vo/vo.go
@@ -6,3 +6,4 @@ type BaseVo struct {
Id int64 `json:"id"`
CreateTime time.Time `json:"createTime"`
}
+
diff --git a/apps/proxy/hysteria2.go b/apps/proxy/hysteria2.go
index a8be8d1..b91a0a6 100644
--- a/apps/proxy/hysteria2.go
+++ b/apps/proxy/hysteria2.go
@@ -56,3 +56,4 @@ func (h *Hysteria2Process) Release() error {
}
return nil
}
+
diff --git a/apps/proxy/hysteria2_api.go b/apps/proxy/hysteria2_api.go
index 9c40600..283d914 100644
--- a/apps/proxy/hysteria2_api.go
+++ b/apps/proxy/hysteria2_api.go
@@ -24,7 +24,7 @@ func NewHysteria2Api(apiPort int64) *Hysteria2Api {
}
}
-// ListUsers 每个用户的流量信息
+// ListUsers Информация о трафике каждого пользователя
func (h *Hysteria2Api) ListUsers(clear bool, secret string) (map[string]bo.Hysteria2UserTraffic, error) {
var users map[string]bo.Hysteria2UserTraffic
if !NewHysteria2Instance().IsRunning() {
@@ -64,7 +64,7 @@ func (h *Hysteria2Api) ListUsers(clear bool, secret string) (map[string]bo.Hyste
return users, nil
}
-// KickUsers 踢下线
+// KickUsers Принудительное отключение
func (h *Hysteria2Api) KickUsers(keys []string, secret string) error {
if !NewHysteria2Instance().IsRunning() {
return nil
@@ -98,7 +98,7 @@ func (h *Hysteria2Api) KickUsers(keys []string, secret string) error {
return nil
}
-// OnlineUsers 在线用户
+// OnlineUsers Пользователи онлайн
func (h *Hysteria2Api) OnlineUsers(secret string) (map[string]int64, error) {
var onlineUsers map[string]int64
if !NewHysteria2Instance().IsRunning() {
@@ -134,3 +134,4 @@ func (h *Hysteria2Api) OnlineUsers(secret string) (map[string]int64, error) {
}
return onlineUsers, nil
}
+
diff --git a/apps/proxy/process.go b/apps/proxy/process.go
index 22b5b24..30453c6 100644
--- a/apps/proxy/process.go
+++ b/apps/proxy/process.go
@@ -59,7 +59,7 @@ func (p *process) start(name string, arg ...string) error {
return errors.New("cmd err")
}
- // 获取命令的 stdout 和 stderr
+ // Получение stdout и stderr команды
stdout, err := cmd.StdoutPipe()
if err != nil {
logrus.Errorf("Error obtaining stdout: %v", err)
@@ -179,7 +179,7 @@ func (p *process) release() error {
}
func (p *process) handleLogs(stdout, stderr io.ReadCloser) {
- // 日志
+ // Логи
stdoutChan := make(chan string)
stderrChan := make(chan string)
@@ -221,9 +221,10 @@ func (p *process) handleLogs(stdout, stderr io.ReadCloser) {
}
}
- // 当两个 channel 都关闭时,退出循环
+ // Когда оба channel закрыты, выходим из цикла
if stdoutChan == nil && stderrChan == nil {
break
}
}
}
+
diff --git a/apps/router/account.go b/apps/router/account.go
index c83130c..639e838 100644
--- a/apps/router/account.go
+++ b/apps/router/account.go
@@ -21,3 +21,4 @@ func initAccountAdminRouter(accountApi *gin.RouterGroup) {
account.GET("/verifyDefaultPass", controller.VerifyDefaultPass)
}
}
+
diff --git a/apps/router/auth.go b/apps/router/auth.go
index 569a477..c1ab846 100644
--- a/apps/router/auth.go
+++ b/apps/router/auth.go
@@ -11,3 +11,4 @@ func initAuthRouter(authApi *gin.RouterGroup) {
auth.POST("/login", controller.Login)
}
}
+
diff --git a/apps/router/config.go b/apps/router/config.go
index 7a8e6e4..3d6ee0d 100644
--- a/apps/router/config.go
+++ b/apps/router/config.go
@@ -22,3 +22,4 @@ func initConfigRouter(configApi *gin.RouterGroup) {
config.POST("/uploadCertFile", controller.UploadCertFile)
}
}
+
diff --git a/apps/router/hysteria2.go b/apps/router/hysteria2.go
index ce2ceb6..8576758 100644
--- a/apps/router/hysteria2.go
+++ b/apps/router/hysteria2.go
@@ -24,3 +24,4 @@ func initHysteria2Router(hysteria2Api *gin.RouterGroup) {
hysteria2.GET("/hysteria2Url", controller.Hysteria2Url)
}
}
+
diff --git a/apps/router/log.go b/apps/router/log.go
index 452ddc9..50bc08a 100644
--- a/apps/router/log.go
+++ b/apps/router/log.go
@@ -13,3 +13,4 @@ func initLogRouter(accountApi *gin.RouterGroup) {
account.POST("/exportLog", controller.ExportLog)
}
}
+
diff --git a/apps/router/monitor.go b/apps/router/monitor.go
index 96792ee..24ce4df 100644
--- a/apps/router/monitor.go
+++ b/apps/router/monitor.go
@@ -12,3 +12,4 @@ func initMonitorRouter(accountApi *gin.RouterGroup) {
account.GET("/monitorHysteria2", controller.MonitorHysteria2)
}
}
+
diff --git a/apps/router/router.go b/apps/router/router.go
index 69cdeaf..03b068b 100644
--- a/apps/router/router.go
+++ b/apps/router/router.go
@@ -39,3 +39,4 @@ func Router(router *gin.Engine, huiWebContext *string) {
}
}
}
+
diff --git a/apps/service/account.go b/apps/service/account.go
index 0657e38..796d44c 100644
--- a/apps/service/account.go
+++ b/apps/service/account.go
@@ -156,3 +156,4 @@ func GetAccountInfo(c *gin.Context) (vo.AccountInfoVo, error) {
Roles: myClaims.AccountBo.Roles,
}, nil
}
+
diff --git a/apps/service/config.go b/apps/service/config.go
index 611765b..b86d8d1 100644
--- a/apps/service/config.go
+++ b/apps/service/config.go
@@ -25,7 +25,7 @@ func UpdateConfig(key string, value string) error {
logrus.Errorf("hysteria2 config is empty")
return errors.New("hysteria2 config is empty")
}
- // 启动Hysteria2
+ // Запуск Hysteria2
if err = StartHysteria2(); err != nil {
return err
}
@@ -73,7 +73,7 @@ func GetHysteria2Config() (bo.Hysteria2ServerConfig, error) {
}
func UpdateHysteria2Config(hysteria2ServerConfig bo.Hysteria2ServerConfig) error {
- // 默认值
+ // Значения по умолчанию
config, err := dao.ListConfig("key in ?", []string{constant.HUIWebPort, constant.JwtSecret})
if err != nil {
return err
@@ -195,3 +195,4 @@ func GetAuthHttpUrl() (string, error) {
}
return fmt.Sprintf("%s://127.0.0.1:%d%s/hui/hysteria2/auth", protocol, port, webContext), nil
}
+
diff --git a/apps/service/cron.go b/apps/service/cron.go
index e5c979a..13e0685 100644
--- a/apps/service/cron.go
+++ b/apps/service/cron.go
@@ -32,10 +32,10 @@ func CronHandleAccount() {
return
}
- // 保存流量数据
+ // Сохранение данных трафика
go saveAccountTraffic(apiPort, *jwtSecretConfig.Value)
- // 踢下线
+ // Принудительное отключение
go kickAccount(apiPort, *jwtSecretConfig.Value)
}
}()
@@ -138,3 +138,4 @@ func kickAccount(apiPort int64, jwtSecret string) {
wg.Wait()
}
}
+
diff --git a/apps/service/forward.go b/apps/service/forward.go
index a975e41..359141f 100644
--- a/apps/service/forward.go
+++ b/apps/service/forward.go
@@ -113,9 +113,9 @@ func nftForward(rules string, target string, option string) error {
return fmt.Errorf("no network interface detected")
}
// nft list ruleset
- // 创建表:nft add table inet hui_hysteria_porthopping
- // 创建链:nft add chain inet hui_hysteria_porthopping prerouting { type nat hook prerouting priority dstnat\; policy accept\; }
- // 添加规则:nft add rule inet hui_hysteria_porthopping prerouting iifname enp1s0 udp dport {30000-40000} counter redirect to :444 comment hui_hysteria_porthopping
+ // Создать таблицу: nft add table inet hui_hysteria_porthopping
+ // Создать chain: nft add chain inet hui_hysteria_porthopping prerouting { type nat hook prerouting priority dstnat\; policy accept\; }
+ // Добавить rule: nft add rule inet hui_hysteria_porthopping prerouting iifname enp1s0 udp dport {30000-40000} counter redirect to :444 comment hui_hysteria_porthopping
_, err := util.Exec(fmt.Sprintf("nft %s rule inet %s prerouting iifname %s udp dport {%s} counter redirect to :%s comment %s", option, Table, ingressInterface, rules, target, Comment))
if err != nil {
return err
@@ -232,3 +232,4 @@ func iptablesRules(protocol string) ([]string, error) {
rules := strings.Split(output, "\n")
return rules, nil
}
+
diff --git a/apps/service/hysteria2.go b/apps/service/hysteria2.go
index 7135061..50555cb 100644
--- a/apps/service/hysteria2.go
+++ b/apps/service/hysteria2.go
@@ -147,3 +147,4 @@ func Hysteria2AcmePath() (vo.Hysteria2AcmePathVo, error) {
}
return vo.Hysteria2AcmePathVo{}, errors.New("cert not found")
}
+
diff --git a/apps/service/hysteria2_api.go b/apps/service/hysteria2_api.go
index 0c718bb..d6ec7f3 100644
--- a/apps/service/hysteria2_api.go
+++ b/apps/service/hysteria2_api.go
@@ -24,7 +24,7 @@ func Hysteria2Auth(conPass string) (int64, string, error) {
return 0, "", err
}
- // 限制设备数
+ // Ограничение количества устройств
onlineUsers, err := Hysteria2Online()
if err != nil {
return 0, "", err
@@ -280,3 +280,4 @@ func Hysteria2Url(accountId int64, hostname string) (string, error) {
}
return fmt.Sprintf("hysteria2://%s@%s%s", *account.ConPass, hostname, *hysteria2Config.Listen) + urlConfig, nil
}
+
diff --git a/apps/service/jwt.go b/apps/service/jwt.go
index 7ec344c..7594991 100644
--- a/apps/service/jwt.go
+++ b/apps/service/jwt.go
@@ -58,3 +58,4 @@ func GetToken(c *gin.Context) string {
}
return strings.SplitN(tokenStr, " ", 2)[1]
}
+
diff --git a/apps/service/monitor.go b/apps/service/monitor.go
index 345334b..99bff06 100644
--- a/apps/service/monitor.go
+++ b/apps/service/monitor.go
@@ -62,3 +62,4 @@ func MonitorHysteria2() (vo.Hysteria2MonitorVo, error) {
hysteria2MonitorVo.Running = running
return hysteria2MonitorVo, nil
}
+
diff --git a/apps/service/server.go b/apps/service/server.go
index 7c22617..650630c 100644
--- a/apps/service/server.go
+++ b/apps/service/server.go
@@ -67,3 +67,4 @@ func GetServerPortAndCert() (int64, string, string, error) {
return port, crtPath, keyPath, nil
}
+
diff --git a/apps/util/arr.go b/apps/util/arr.go
index a3c7cf0..5d33fb8 100644
--- a/apps/util/arr.go
+++ b/apps/util/arr.go
@@ -30,3 +30,4 @@ func SplitArr[T any](arr []T, num int) [][]T {
return segments
}
+
diff --git a/apps/util/encrypt.go b/apps/util/encrypt.go
index 7747158..dcb6595 100644
--- a/apps/util/encrypt.go
+++ b/apps/util/encrypt.go
@@ -15,3 +15,4 @@ func SHA224String(password string) string {
}
return str
}
+
diff --git a/apps/util/encrypt_test.go b/apps/util/encrypt_test.go
index fb0f98d..1db2cba 100644
--- a/apps/util/encrypt_test.go
+++ b/apps/util/encrypt_test.go
@@ -5,3 +5,4 @@ import "testing"
func TestSHA224String(t *testing.T) {
println(SHA224String("sysadmin"))
}
+
diff --git a/apps/util/export.go b/apps/util/export.go
index 74da5e5..09b2e38 100644
--- a/apps/util/export.go
+++ b/apps/util/export.go
@@ -38,3 +38,4 @@ func ExportFile(filePath string, data any, t int) error {
}
return nil
}
+
diff --git a/apps/util/file.go b/apps/util/file.go
index a6a79cc..399bec8 100644
--- a/apps/util/file.go
+++ b/apps/util/file.go
@@ -86,3 +86,4 @@ func FindFile(dir, filename string) (string, error) {
}
return result, nil
}
+
diff --git a/apps/util/github.go b/apps/util/github.go
index 7f78afc..b43436c 100644
--- a/apps/util/github.go
+++ b/apps/util/github.go
@@ -55,3 +55,4 @@ func ListRelease(owner, repo string) ([]*github.RepositoryRelease, error) {
}
return releases, nil
}
+
diff --git a/apps/util/hysteria2.go b/apps/util/hysteria2.go
index 374bf30..1a41fb3 100644
--- a/apps/util/hysteria2.go
+++ b/apps/util/hysteria2.go
@@ -63,3 +63,4 @@ func DownloadHysteria2(version string) error {
}
return nil
}
+
diff --git a/apps/util/linux.go b/apps/util/linux.go
index 8db725d..4e205a5 100644
--- a/apps/util/linux.go
+++ b/apps/util/linux.go
@@ -98,3 +98,4 @@ func VerifyPort(port string) error {
}
return nil
}
+
diff --git a/apps/util/map.go b/apps/util/map.go
index 9b86a32..7169d42 100644
--- a/apps/util/map.go
+++ b/apps/util/map.go
@@ -21,3 +21,4 @@ func SplitMap[T any](inputMap map[string]T, chunkSize int) []map[string]T {
return segments
}
+
diff --git a/apps/util/rand.go b/apps/util/rand.go
index 00bc91d..754f710 100644
--- a/apps/util/rand.go
+++ b/apps/util/rand.go
@@ -17,3 +17,4 @@ func RandomString(length int) (string, error) {
return string(bytes), nil
}
+
diff --git a/apps/util/string.go b/apps/util/string.go
index 2a145a1..5ebbafd 100644
--- a/apps/util/string.go
+++ b/apps/util/string.go
@@ -34,3 +34,4 @@ func CompareVersion(version1, version2 string) int {
// The version number is exactly the same
return 0
}
+
diff --git a/docs/02-build-layer-and-package.md b/docs/02-build-layer-and-package.md
index 6613464..f649749 100644
--- a/docs/02-build-layer-and-package.md
+++ b/docs/02-build-layer-and-package.md
@@ -66,7 +66,7 @@ project/
├── tools/
│ └── build/
│ ├── build.sh
-│ ├── README.ru.md
+│ ├── README.md
│ └── lib/
├── orchestrator/
│ ├── package.json
diff --git a/docs/README.md b/docs/README.md
index 7383e4f..8697920 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -17,7 +17,7 @@
В этой редакции зафиксированы два слоя:
-1. **Builder layer** — работает **локально**, на отдельной машине.
+1. **Builder layer** — работает на отдельном **Debian 12 amd64 build host**.
Он собирает итоговый пакет, подготавливает **наш форк HY2XS admin**, компилирует **Bun/TypeScript оркестратор** в install-артефакт, упаковывает шаблоны, unit-файлы и примеры конфигов.
2. **Runtime / target layer** — работает **на чистом Debian 12**.
diff --git a/hy2xs_implementation_plan-no_git/03-builder-layer.txt b/hy2xs_implementation_plan-no_git/03-builder-layer.txt
index 1b0ccf1..18ae506 100644
--- a/hy2xs_implementation_plan-no_git/03-builder-layer.txt
+++ b/hy2xs_implementation_plan-no_git/03-builder-layer.txt
@@ -27,7 +27,7 @@ Baseline packaging: shell-first.
- Создать tools/build/lib/verify.sh.
- Создать tools/build/lib/package.sh.
- Создать tools/build/lib/deps.sh.
-- Создать tools/build/README.ru.md.
+- Создать tools/build/README.md.
Production-дополнение.
- Проверять Debian 12 amd64.
diff --git a/package/docs/README.md b/package/docs/README.md
index 1125691..1f3c2be 100644
--- a/package/docs/README.md
+++ b/package/docs/README.md
@@ -1,11 +1,19 @@
# HY2XS install package
-This package is generated by the local builder layer. It is intended for a clean Debian 12 target and contains a compiled install-only orchestrator, bundled HY2XS admin files, templates, systemd units, and package metadata.
+Этот пакет создаётся production builder'ом на Debian 12 amd64.
-Run as root:
+Пакет предназначен для чистого Debian 12 target и содержит:
+
+- compiled install-only orchestrator;
+- bundled HY2XS admin;
+- templates;
+- systemd units;
+- package metadata и checksums.
+
+Запускать от root:
```sh
./install.sh --non-interactive --domain example.com
```
-No target-side JavaScript or TypeScript build step is part of the baseline.
+В baseline нет target-side JavaScript, TypeScript, frontend или Go build step.
diff --git a/tools/build/README.ru.md b/tools/build/README.md
similarity index 67%
rename from tools/build/README.ru.md
rename to tools/build/README.md
index e10d5a2..84edde7 100644
--- a/tools/build/README.ru.md
+++ b/tools/build/README.md
@@ -2,9 +2,9 @@
## Назначение
-`tools/build/build.sh` собирает один переносимый install package HY2XS для production-развёртывания.
+[`build.sh`](build.sh) собирает переносимый установочный пакет HY2XS для production-развёртывания.
-Итоговый артефакт создаётся в `dist/hy2xs-install-.tar.gz` и предназначен для установки на чистый Debian 12 amd64 без target-side сборки.
+Итоговый архив создаётся в [`dist`](../../dist) и предназначен для установки на чистый Debian 12 amd64 без сборки на целевом сервере.
## Поддерживаемая среда сборки
@@ -57,7 +57,7 @@ PACKAGE_VERSION=0.1.0 BUILD_ID=prod-20260425-001 ./tools/build/build.sh
## Локальный toolchain
-Builder ставит управляемый toolchain в `.toolchain/` и не требует ручной установки Go/Bun/Node/pnpm в систему.
+Builder ставит управляемый toolchain в [`.toolchain`](../../.toolchain) и не требует ручной установки Go/Bun/Node/pnpm в систему.
Если нужная версия уже установлена глобально, builder может использовать её. Если версия не совпадает, будет скачана локальная версия.
@@ -66,8 +66,8 @@ Builder ставит управляемый toolchain в `.toolchain/` и не
- Builder не ставит HY2XS на сервер.
- Builder не выполняет target install.
- Builder не собирает ничего на target machine.
-- Builder не вендорит Hysteria2 binary в пакет: Hysteria2 скачивается install layer'ом с official upstream.
-- Итоговый package не должен содержать build scripts, `.toolchain` или временные каталоги.
+- Builder не вендорит бинарь Hysteria2 в пакет: Hysteria2 скачивается install layer'ом с official upstream.
+- Итоговый пакет не должен содержать build scripts, `.toolchain` или временные каталоги.
## Результат
@@ -77,7 +77,7 @@ Builder ставит управляемый toolchain в `.toolchain/` и не
dist/hy2xs-install-.tar.gz
```
-Его нужно перенести на target Debian 12 amd64, распаковать и запустить `install.sh` от root.
+Его нужно перенести на target Debian 12 amd64, распаковать и запустить [`install.sh`](../../package/install.sh) от root.
## Диагностика
@@ -85,5 +85,5 @@ dist/hy2xs-install-.tar.gz
1. Проверьте, что host — Debian 12 amd64.
2. Проверьте доступ к `go.dev`, `github.com`, `nodejs.org`, npm registry и apt repositories.
-3. Удалите `.toolchain/` и повторите запуск, если toolchain скачался повреждённым.
-4. Проверьте lock-файлы `orchestrator/bun.lock`, `apps/frontend/pnpm-lock.yaml`, `apps/go.sum`.
+3. Удалите [`.toolchain`](../../.toolchain) и повторите запуск, если toolchain скачался повреждённым.
+4. Проверьте lock-файлы [`bun.lock`](../../orchestrator/bun.lock), [`pnpm-lock.yaml`](../../apps/frontend/pnpm-lock.yaml), [`go.sum`](../../apps/go.sum).