From bb288c554f854c6de6dad59a6e4739b803e2258f Mon Sep 17 00:00:00 2001 From: Chillln <8766520@gmail.com> Date: Thu, 2 Oct 2025 14:23:11 +0800 Subject: [PATCH] Fix and update (#139) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(ui): 统一组件引用类型为ComponentRef 更新所有UI组件中的forwardRef类型,从ElementRef改为ComponentRef以保持一致性 迁移postcss配置至mjs格式并更新依赖版本 * refactor: 优化表单类型定义和验证逻辑 移除自定义的 asOptionalField 工具函数,直接使用 Zod 的 optional() 方法,并明确定义表单数据类型。 * style: 更新UI主题配置和样式变量 将主题风格从default切换为new-york,并重构CSS变量使用OKLCH色彩空间。同时添加tailwindcss-animate插件支持。 * style: 统一页面头部按钮组样式 优化多个页面头部按钮组的布局样式,增加响应式设计和flex-wrap支持 * fix(server): 修复对话框交互问题并优化SWR配置 修复对话框关闭逻辑并阻止外部交互,同时禁用SWR的自动重新验证功能以提升性能。 * feat: 添加日历组件及账单相关国际化 实现基于 react-day-picker 的日历组件,并添加账单管理相关的多语言支持 * style(components): 统一按钮样式并格式化代码 为删除和禁用按钮添加text-white类名,同时调整ServerCard组件中的代码缩进格式。 * perf(build): 优化Vite打包配置与代码分割策略 调整Vite构建配置,改进第三方依赖的分组逻辑并添加UUID支持到安装命令组件 * fix: 修正页面标题翻译不一致问题 将CronPage和ServicePage的标题从"Server"分别改为"Task"和"Service",并优化NotificationGroupPage的按钮组布局。 * fix(auth): 改进登录错误处理和国际化支持 优化登录错误提示,添加多语言支持并移除控制台错误日志。同时修复头部组件透明度样式问题。 * feat: 添加服务器操作下拉菜单 为服务器卡片添加统一的下拉菜单操作入口,整合终端、配置和安装命令功能。 * feat[alert-rule]: 优化告警规则组件性能 重构告警规则组件代码结构,提升渲染效率并减少内存占用。 * docs(i18n): 新增翻译字段 为界面添加"Add"、"Delete"、"AdvancedJSON"和"Save"等关键操作的翻译字段,支持中英文双语显示。 * perf(vite): 优化分包策略以提升构建性能 重构 manualChunks 逻辑,按功能类别分组依赖项,并增加大型库的独立分包规则。 * style: 统一危险操作按钮的文字颜色 在所有确认操作的弹窗按钮中添加白色文字样式,保持视觉一致性。 * fix(components): 调整下拉菜单对齐方式 根据菜单项状态动态设置下拉菜单的对齐方向和起始位置。 * fix(types): 修复在线用户API分页类型 添加ModelOnlineUserApi接口类型,包含分页信息,并移除index.ts中重复的类型定义。 * chore: auto-fix linting and formatting issues * feat(locales): 添加无过期相关翻译项 为英文和中文翻译文件添加"NoExpiry"、"SetNoExpiry"等无过期相关字段的翻译。 fix(components): 移除重复的图标按钮选项 从IconButton组件中删除重复的"more"图标选项。 * feat(ServerCard): 优化日期选择器并添加下拉提示 为日期选择器添加下拉布局和年份范围限制,并在公共笔记区域增加下拉项生效提示文本。 * chore: auto-fix linting and formatting issues * style: 优化多个组件的UI交互细节 统一按钮悬停样式并简化国际化文本调用,移除冗余的单位显示和空值判断逻辑。 * refactor(ServerCard): 移除网络路由相关代码 删除 ServerCard 组件中与 plan.networkRoute 相关的字段验证和错误显示逻辑。 * chore: auto-fix linting and formatting issues * feat(ui): 添加Switch组件并改进服务器表单交互 - 新增Radix UI Switch组件依赖及实现 - 将IPv4/IPv6输入改为开关控件,优化用户体验 - 添加"按量付费"选项和新的翻译字段 - 改进网络路由和备注输入的占位提示 - 修复暗黑模式下的按钮背景色 * style(components): 为禁止按钮添加白色文本样式 * chore: auto-fix linting and formatting issues * fix(ServerCard): 修复日期选择器样式和滚动问题 调整日期选择器的宽度和高度限制,添加滚动容器以解决内容溢出问题 * refactor(server-config): 简化复选框checked属性的布尔转换 使用!!操作符简化controllerField.value的布尔值转换,使代码更简洁 * feat(国际化): 添加告警规则和搜索框的国际化支持 为告警规则组件添加多语言支持,包括服务器监控选项、忽略提示和示例文本。同时将搜索框的占位文本替换为国际化字段。 * chore: auto-fix linting and formatting issues * fix(switch): 修正 Switch 组件 ref 类型定义错误 --------- Co-authored-by: Guccen <171530509+Chillln@users.noreply.github.com> --- bun.lockb | Bin 207761 -> 0 bytes components.json | 4 +- package.json | 104 +-- postcss.config.js | 6 - postcss.config.mjs | 5 + src/components/action-button-group.tsx | 14 +- src/components/alert-rule.tsx | 491 ++++++++++++- src/components/batch-move-server-icon.tsx | 19 +- src/components/cron.tsx | 23 +- src/components/ddns.tsx | 33 +- src/components/fm.tsx | 2 +- src/components/header-button-group.tsx | 16 +- src/components/header.tsx | 2 +- src/components/install-commands.tsx | 177 +++-- src/components/nat.tsx | 16 +- src/components/notifier.tsx | 26 +- src/components/profile.tsx | 6 +- src/components/server-config.tsx | 27 +- src/components/server.tsx | 712 ++++++++++++++++++- src/components/service.tsx | 8 +- src/components/terminal.tsx | 22 +- src/components/ui/alert-dialog.tsx | 12 +- src/components/ui/avatar.tsx | 6 +- src/components/ui/calendar.tsx | 184 +++++ src/components/ui/checkbox.tsx | 2 +- src/components/ui/command.tsx | 14 +- src/components/ui/dialog.tsx | 8 +- src/components/ui/drawer.tsx | 8 +- src/components/ui/dropdown-menu.tsx | 16 +- src/components/ui/form.tsx | 4 +- src/components/ui/label.tsx | 2 +- src/components/ui/navigation-menu.tsx | 12 +- src/components/ui/popover.tsx | 2 +- src/components/ui/scroll-area.tsx | 4 +- src/components/ui/select.tsx | 14 +- src/components/ui/separator.tsx | 2 +- src/components/ui/switch.tsx | 26 + src/components/ui/tabs.tsx | 6 +- src/components/xui/icon-button.tsx | 5 + src/components/xui/overlayless-sheet.tsx | 6 +- src/components/xui/virtulized-data-table.tsx | 2 +- src/hooks/useAuth.tsx | 10 +- src/index.css | 187 +++-- src/lib/i18n.ts | 4 +- src/locales/en/translation.json | 58 +- src/locales/zh-CN/translation.json | 58 +- src/routes/alert-rule.tsx | 2 +- src/routes/cron.tsx | 8 +- src/routes/ddns.tsx | 2 +- src/routes/nat.tsx | 2 +- src/routes/notification-group.tsx | 2 +- src/routes/notification.tsx | 2 +- src/routes/online-user.tsx | 4 +- src/routes/server-group.tsx | 2 +- src/routes/server.tsx | 135 ++-- src/routes/service.tsx | 8 +- src/routes/settings.tsx | 6 +- src/routes/user.tsx | 3 +- src/types/api.ts | 31 +- src/types/server.ts | 2 +- vite.config.ts | 86 ++- 61 files changed, 2214 insertions(+), 446 deletions(-) delete mode 100755 bun.lockb delete mode 100644 postcss.config.js create mode 100644 postcss.config.mjs create mode 100644 src/components/ui/calendar.tsx create mode 100644 src/components/ui/switch.tsx diff --git a/bun.lockb b/bun.lockb deleted file mode 100755 index ef833ddba4c9e5b7c6990ef83d29e719981cda4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 207761 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 diff --git a/components.json b/components.json index 4879b60..3151684 100644 --- a/components.json +++ b/components.json @@ -10,11 +10,13 @@ "cssVariables": true, "prefix": "" }, + "iconLibrary": "lucide", "aliases": { "components": "@/components", "utils": "@/lib/utils", "ui": "@/components/ui", "lib": "@/lib", "hooks": "@/hooks" - } + }, + "registries": {} } diff --git a/package.json b/package.json index 4271765..0a57257 100644 --- a/package.json +++ b/package.json @@ -13,68 +13,72 @@ "preview": "vite preview" }, "dependencies": { - "@hookform/resolvers": "^3.10.0", - "@radix-ui/react-alert-dialog": "^1.1.5", - "@radix-ui/react-avatar": "^1.1.2", - "@radix-ui/react-checkbox": "^1.1.3", - "@radix-ui/react-dialog": "^1.1.5", - "@radix-ui/react-dropdown-menu": "^2.1.5", - "@radix-ui/react-label": "^2.1.1", - "@radix-ui/react-navigation-menu": "^1.2.4", - "@radix-ui/react-popover": "^1.1.5", - "@radix-ui/react-scroll-area": "^1.2.2", - "@radix-ui/react-select": "^2.1.5", - "@radix-ui/react-separator": "^1.1.1", - "@radix-ui/react-slot": "^1.1.1", - "@radix-ui/react-tabs": "^1.1.2", - "@tanstack/react-table": "^8.20.6", + "@hookform/resolvers": "^5.2.2", + "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-navigation-menu": "^1.2.14", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@tailwindcss/postcss": "^4.1.13", + "@tanstack/react-table": "^8.21.3", "@trivago/prettier-plugin-sort-imports": "^5.2.2", - "@types/luxon": "^3.4.2", + "@types/luxon": "^3.7.1", "@xterm/addon-attach": "^0.11.0", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "cmdk": "^1.0.4", + "cmdk": "^1.1.1", "copy-to-clipboard": "^3.3.3", - "framer-motion": "^11.18.2", - "i18next": "^24.2.2", - "i18next-browser-languagedetector": "^8.0.2", + "date-fns": "^4.1.0", + "framer-motion": "^12.23.22", + "i18next": "^25.5.2", + "i18next-browser-languagedetector": "^8.2.0", "jotai-zustand": "^0.6.0", - "lucide-react": "^0.454.0", - "luxon": "^3.5.0", + "lucide-react": "^0.544.0", + "luxon": "^3.7.2", "next-themes": "^0.4.6", - "prettier-plugin-tailwindcss": "^0.6.11", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-hook-form": "^7.54.2", - "react-i18next": "^15.4.0", - "react-router-dom": "^7.1.5", - "react-virtuoso": "^4.12.3", - "sonner": "^1.7.4", - "swr": "^2.3.0", - "tailwind-merge": "^2.6.0", + "prettier-plugin-tailwindcss": "^0.6.14", + "react": "^19.1.1", + "react-day-picker": "^9.11.0", + "react-dom": "^19.1.1", + "react-hook-form": "^7.63.0", + "react-i18next": "^16.0.0", + "react-router-dom": "^7.9.3", + "react-virtuoso": "^4.14.1", + "sonner": "^2.0.7", + "swr": "^2.3.6", + "tailwind-merge": "^3.3.1", "tailwindcss-animate": "^1.0.7", "vaul": "^1.1.2", - "zod": "^3.24.1", - "zustand": "^5.0.3" + "zod": "^4.1.11", + "zustand": "^5.0.8" }, "devDependencies": { - "@eslint/js": "^9.19.0", - "@types/node": "^22.13.0", - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", - "@vitejs/plugin-react": "^4.3.4", - "autoprefixer": "^10.4.20", - "eslint": "^9.19.0", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.18", - "globals": "^15.14.0", - "postcss": "^8.5.1", - "swagger-typescript-api": "^13.0.23", - "tailwindcss": "^3.4.17", - "typescript": "~5.6.3", - "typescript-eslint": "^8.22.0", - "vite": "^6.0.11" + "@eslint/js": "^9.36.0", + "@types/node": "^24.5.2", + "@types/react": "^19.1.15", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.4", + "autoprefixer": "^10.4.21", + "eslint": "^9.36.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.22", + "globals": "^16.4.0", + "postcss": "^8.5.6", + "swagger-typescript-api": "^13.2.13", + "tailwindcss": "^4.1.13", + "typescript": "~5.9.2", + "typescript-eslint": "^8.44.1", + "vite": "^7.1.7" } } diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index d41ad63..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..521f73c --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +} diff --git a/src/components/action-button-group.tsx b/src/components/action-button-group.tsx index b1a5e98..09d794d 100644 --- a/src/components/action-button-group.tsx +++ b/src/components/action-button-group.tsx @@ -49,7 +49,7 @@ export function ActionButtonGroup({ {children} - + @@ -61,7 +61,10 @@ export function ActionButtonGroup({ {t("Close")} {t("Confirm")} @@ -95,7 +98,7 @@ export function BlockButtonGroup({ {children} - + @@ -107,7 +110,10 @@ export function BlockButtonGroup({ {t("Close")} {t("Confirm")} diff --git a/src/components/alert-rule.tsx b/src/components/alert-rule.tsx index e86e53d..468800d 100644 --- a/src/components/alert-rule.tsx +++ b/src/components/alert-rule.tsx @@ -32,11 +32,10 @@ import { import { IconButton } from "@/components/xui/icon-button" import { useNotification } from "@/hooks/useNotfication" import { conv } from "@/lib/utils" -import { asOptionalField } from "@/lib/utils" import { ModelAlertRule } from "@/types" import { triggerModes } from "@/types" import { zodResolver } from "@hookform/resolvers/zod" -import { useState } from "react" +import { useEffect, useState } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import { toast } from "sonner" @@ -53,16 +52,16 @@ interface AlertRuleCardProps { const ruleSchema = z.object({ type: z.string(), - min: asOptionalField(z.number()), - max: asOptionalField(z.number()), - cycle_start: asOptionalField(z.string()), - cycle_interval: asOptionalField(z.number()), - cycle_unit: asOptionalField(z.enum(["hour", "day", "week", "month", "year"])), - duration: asOptionalField(z.number()), + min: z.number().optional(), + max: z.number().optional(), + cycle_start: z.string().optional(), + cycle_interval: z.number().optional(), + cycle_unit: z.enum(["hour", "day", "week", "month", "year"]).optional(), + duration: z.number().optional(), cover: z.number().int().min(0), - ignore: asOptionalField(z.record(z.boolean())), - next_transfer_at: asOptionalField(z.record(z.string())), - last_cycle_status: asOptionalField(z.boolean()), + ignore: z.record(z.string(), z.boolean()).optional(), + next_transfer_at: z.record(z.string(), z.string()).optional(), + last_cycle_status: z.boolean().optional(), }) const alertRuleFormSchema = z.object({ @@ -87,13 +86,16 @@ const alertRuleFormSchema = z.object({ recover_trigger_tasks_raw: z.string(), notification_group_id: z.coerce.number().int(), trigger_mode: z.coerce.number().int().min(0), - enable: asOptionalField(z.boolean()), + enable: z.boolean().optional(), }) export const AlertRuleCard: React.FC = ({ data, mutate }) => { const { t } = useTranslation() - const form = useForm>({ - resolver: zodResolver(alertRuleFormSchema), + + type AlertRuleFormData = z.infer + + const form = useForm({ + resolver: zodResolver(alertRuleFormSchema) as any, defaultValues: data ? { ...data, @@ -119,7 +121,28 @@ export const AlertRuleCard: React.FC = ({ data, mutate }) => const [open, setOpen] = useState(false) - const onSubmit = async (values: z.infer) => { + // 结构化规则编辑状态:从已有数据或 rules_raw 初始化 + const initialRules = (() => { + try { + if (data?.rules) return data.rules as any[] + const raw = form.getValues("rules_raw") + return raw ? JSON.parse(raw) : [] + } catch { + return [] + } + })() + const [rulesUI, setRulesUI] = useState(initialRules) + + // 同步到 rules_raw(提交仍走 JSON 字符串) + useEffect(() => { + try { + form.setValue("rules_raw", JSON.stringify(rulesUI), { shouldDirty: true }) + } catch { + // ignore + } + }, [rulesUI]) + + const onSubmit = async (values: AlertRuleFormData) => { values.rules = JSON.parse(values.rules_raw) values.fail_trigger_tasks = conv.strToArr(values.fail_trigger_tasks_raw).map(Number) values.recover_trigger_tasks = conv.strToArr(values.recover_trigger_tasks_raw).map(Number) @@ -161,7 +184,10 @@ export const AlertRuleCard: React.FC = ({ data, mutate }) =>
- + = ({ data, mutate }) => )} /> - ( - - {t("Rules")} - - + = ({ server {t("Cancel")} - diff --git a/src/components/cron.tsx b/src/components/cron.tsx index df811f0..aa8680d 100644 --- a/src/components/cron.tsx +++ b/src/components/cron.tsx @@ -63,18 +63,31 @@ const cronFormSchema = z.object({ notification_group_id: z.coerce.number().int(), }) +type CronFormData = z.infer + export const CronCard: React.FC = ({ data, mutate }) => { const { t } = useTranslation() - const form = useForm>({ - resolver: zodResolver(cronFormSchema), + const form = useForm({ + resolver: zodResolver(cronFormSchema as any), defaultValues: data - ? data + ? { + task_type: data.task_type ?? 0, + name: data.name ?? "", + scheduler: data.scheduler ?? "", + command: (data as any).command ?? "", + servers: data.servers ?? [], + cover: data.cover ?? 0, + push_successful: (data as any).push_successful ?? false, + notification_group_id: data.notification_group_id ?? 0, + } : { - name: "", task_type: 0, + name: "", scheduler: "", + command: "", servers: [], cover: 0, + push_successful: false, notification_group_id: 0, }, resetOptions: { @@ -84,7 +97,7 @@ export const CronCard: React.FC = ({ data, mutate }) => { const [open, setOpen] = useState(false) - const onSubmit = async (values: z.infer) => { + const onSubmit = async (values: CronFormData) => { try { data?.id ? await updateCron(data.id, values) : await createCron(values) } catch (e) { diff --git a/src/components/ddns.tsx b/src/components/ddns.tsx index 3d56180..3bea2bd 100644 --- a/src/components/ddns.tsx +++ b/src/components/ddns.tsx @@ -67,21 +67,44 @@ const ddnsFormSchema = z.object({ webhook_headers: asOptionalField(z.string()), }) +type DDNSFormData = z.infer + export const DDNSCard: React.FC = ({ data, providers, mutate }) => { const { t } = useTranslation() - const form = useForm>({ - resolver: zodResolver(ddnsFormSchema), + const form = useForm({ + resolver: zodResolver(ddnsFormSchema as any), defaultValues: data ? { - ...data, - domains_raw: conv.arrToStr(data.domains), + max_retries: data.max_retries ?? 3, + enable_ipv4: (data as any).enable_ipv4 ?? false, + enable_ipv6: (data as any).enable_ipv6 ?? false, + name: data.name ?? "", + provider: data.provider ?? "dummy", + domains: data.domains ?? [], + domains_raw: conv.arrToStr(data.domains ?? []), + access_id: (data as any).access_id ?? "", + access_secret: (data as any).access_secret ?? "", + webhook_url: (data as any).webhook_url ?? "", + webhook_method: (data as any).webhook_method, + webhook_request_type: (data as any).webhook_request_type, + webhook_request_body: (data as any).webhook_request_body ?? "", + webhook_headers: (data as any).webhook_headers ?? "", } : { max_retries: 3, + enable_ipv4: false, + enable_ipv6: false, name: "", provider: "dummy", domains: [], domains_raw: "", + access_id: "", + access_secret: "", + webhook_url: "", + webhook_method: undefined, + webhook_request_type: undefined, + webhook_request_body: "", + webhook_headers: "", }, resetOptions: { keepDefaultValues: false, @@ -90,7 +113,7 @@ export const DDNSCard: React.FC = ({ data, providers, mutate }) = const [open, setOpen] = useState(false) - const onSubmit = async (values: z.infer) => { + const onSubmit = async (values: DDNSFormData) => { try { values.domains = conv.strToArr(values.domains_raw) data?.id ? await updateDDNSProfile(data.id, values) : await createDDNSProfile(values) diff --git a/src/components/fm.tsx b/src/components/fm.tsx index c9dc2d1..c1555b3 100644 --- a/src/components/fm.tsx +++ b/src/components/fm.tsx @@ -37,7 +37,7 @@ import { import { ColumnDef } from "@tanstack/react-table" import { Row, flexRender } from "@tanstack/react-table" import { File, Folder } from "lucide-react" -import { HTMLAttributes, useEffect, useRef, useState } from "react" +import { HTMLAttributes, JSX, useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" import { toast } from "sonner" diff --git a/src/components/header-button-group.tsx b/src/components/header-button-group.tsx index b60bd93..ca51997 100644 --- a/src/components/header-button-group.tsx +++ b/src/components/header-button-group.tsx @@ -51,6 +51,7 @@ export function HeaderButtonGroup({ { toast(t("Error"), { description: t("Results.NoRowsAreSelected"), @@ -63,7 +64,7 @@ export function HeaderButtonGroup({ <> - + @@ -75,7 +76,10 @@ export function HeaderButtonGroup({ {t("Close")} {t("Confirm")} @@ -114,6 +118,7 @@ export function HeaderBlockButtonGroup({ { toast(t("Error"), { description: t("Results.NoRowsAreSelected"), @@ -126,7 +131,7 @@ export function HeaderBlockButtonGroup({ <> - + @@ -138,7 +143,10 @@ export function HeaderBlockButtonGroup({ {t("Close")} {t("Confirm")} diff --git a/src/components/header.tsx b/src/components/header.tsx index 774ef5e..bbd6a68 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -403,7 +403,7 @@ function Overview() { {!profile &&

{t("LoginFirst")}

}

{t("CurrentTime")}

-

{timeString}

+

{timeString}

) diff --git a/src/components/install-commands.tsx b/src/components/install-commands.tsx index 4b9a432..3df734b 100644 --- a/src/components/install-commands.tsx +++ b/src/components/install-commands.tsx @@ -10,7 +10,7 @@ import useSettings from "@/hooks/useSetting" import { copyToClipboard } from "@/lib/utils" import { ModelProfile, ModelSetting } from "@/types" import i18next from "i18next" -import { Check, Clipboard } from "lucide-react" +import { Check, Copy, Download } from "lucide-react" import { forwardRef, useState } from "react" import { useTranslation } from "react-i18next" import { toast } from "sonner" @@ -21,82 +21,137 @@ enum OSTypes { Windows, } -export const InstallCommandsMenu = forwardRef((props, ref) => { - const [copy, setCopy] = useState(false) - const { data: settings } = useSettings() - const { profile } = useAuth() +type InstallCommandsMenuProps = ButtonProps & { + uuid?: string + iconOnly?: boolean + menuItem?: boolean +} - const { t } = useTranslation() +export const InstallCommandsMenu = forwardRef( + ({ uuid, iconOnly = false, menuItem = false, ...props }, ref) => { + const [copy, setCopy] = useState(false) + const { data: settings } = useSettings() + const { profile } = useAuth() - const switchState = async (type: number) => { - if (!copy) { - try { - setCopy(true) - if (!profile) throw new Error("Profile is not found.") - if (!settings?.config) throw new Error("Settings is not found.") - await copyToClipboard(generateCommand(type, settings!.config, profile) || "") - } catch (e: Error | any) { - console.error(e) - toast(t("Error"), { - description: e.message, - }) - } finally { - setTimeout(() => { - setCopy(false) - }, 2 * 1000) + const { t } = useTranslation() + + const switchState = async (type: number) => { + if (!copy) { + try { + setCopy(true) + if (!profile) throw new Error("Profile is not found.") + if (!settings?.config) throw new Error("Settings is not found.") + await copyToClipboard( + generateCommand(type, settings!.config, profile, uuid) || "", + ) + } catch (e: Error | any) { + console.error(e) + toast(t("Error"), { + description: e.message, + }) + } finally { + setTimeout(() => { + setCopy(false) + }, 2 * 1000) + } } } - } - return ( - - - - - - { - switchState(OSTypes.Linux) - }} + return ( + + + {menuItem ? ( + + ) : iconOnly ? ( + + ) : ( + + )} + + - Linux - - { - switchState(OSTypes.macOS) - }} - > - macOS - - { - switchState(OSTypes.Windows) - }} - > - Windows - - - - ) -}) + { + switchState(OSTypes.Linux) + }} + > + Linux + + { + switchState(OSTypes.macOS) + }} + > + macOS + + { + switchState(OSTypes.Windows) + }} + > + Windows + + + + ) + }, +) const generateCommand = ( type: number, { install_host, tls }: ModelSetting, { agent_secret }: ModelProfile, + uuid?: string, ) => { if (!install_host) throw new Error(i18next.t("Results.InstallHostRequired")) if (!agent_secret) throw new Error(i18next.t("Results.AgentSecretRequired")) - const env = `NZ_SERVER=${install_host} NZ_TLS=${tls || false} NZ_CLIENT_SECRET=${agent_secret}` - const env_win = `$env:NZ_SERVER=\"${install_host}\";$env:NZ_TLS=\"${tls || false}\";$env:NZ_CLIENT_SECRET=\"${agent_secret}\";` + const envParts = [ + `NZ_SERVER=${install_host}`, + `NZ_TLS=${tls || false}`, + `NZ_CLIENT_SECRET=${agent_secret}`, + ] + if (uuid) envParts.push(`NZ_UUID=${uuid}`) + const env = envParts.join(" ") + + const envWinParts = [ + `$env:NZ_SERVER=\"${install_host}\";`, + `$env:NZ_TLS=\"${tls || false}\";`, + `$env:NZ_CLIENT_SECRET=\"${agent_secret}\";`, + ] + if (uuid) envWinParts.push(`$env:NZ_UUID=\"${uuid}\";`) + const env_win = envWinParts.join("") switch (type) { case OSTypes.Linux: diff --git a/src/components/nat.tsx b/src/components/nat.tsx index f82677f..6bbab76 100644 --- a/src/components/nat.tsx +++ b/src/components/nat.tsx @@ -46,12 +46,20 @@ const natFormSchema = z.object({ domain: z.string(), }) +type NatFormData = z.infer + export const NATCard: React.FC = ({ data, mutate }) => { const { t } = useTranslation() - const form = useForm>({ - resolver: zodResolver(natFormSchema), + const form = useForm({ + resolver: zodResolver(natFormSchema as any), defaultValues: data - ? data + ? { + name: data.name ?? "", + enabled: (data as any).enabled ?? false, + server_id: data.server_id ?? 0, + host: data.host ?? "", + domain: data.domain ?? "", + } : { name: "", enabled: false, @@ -66,7 +74,7 @@ export const NATCard: React.FC = ({ data, mutate }) => { const [open, setOpen] = useState(false) - const onSubmit = async (values: z.infer) => { + const onSubmit = async (values: NatFormData) => { try { data?.id ? await updateNAT(data.id, values) : await createNAT(values) } catch (e) { diff --git a/src/components/notifier.tsx b/src/components/notifier.tsx index 2b60583..af3b93d 100644 --- a/src/components/notifier.tsx +++ b/src/components/notifier.tsx @@ -61,10 +61,21 @@ const notificationFormSchema = z.object({ export const NotifierCard: React.FC = ({ data, mutate }) => { const { t } = useTranslation() - const form = useForm>({ - resolver: zodResolver(notificationFormSchema), + type notificationFormData = z.infer + + const form = useForm({ + resolver: zodResolver(notificationFormSchema) as any, defaultValues: data - ? data + ? { + name: data.name ?? "", + url: data.url ?? "", + request_method: data.request_method ?? 1, + request_type: data.request_type ?? 1, + request_header: data.request_header ?? "", + request_body: data.request_body ?? "", + verify_tls: (data as any).verify_tls ?? false, + skip_check: (data as any).skip_check ?? false, + } : { name: "", url: "", @@ -72,6 +83,8 @@ export const NotifierCard: React.FC = ({ data, mutate }) => { request_type: 1, request_header: "", request_body: "", + verify_tls: false, + skip_check: false, }, resetOptions: { keepDefaultValues: false, @@ -80,7 +93,7 @@ export const NotifierCard: React.FC = ({ data, mutate }) => { const [open, setOpen] = useState(false) - const onSubmit = async (values: z.infer) => { + const onSubmit = async (values: notificationFormData) => { try { data?.id ? await updateNotification(data.id, values) : await createNotification(values) } catch (e) { @@ -110,7 +123,10 @@ export const NotifierCard: React.FC = ({ data, mutate }) => { - + { const { t } = useTranslation() const { profile, setProfile } = useMainStore() - const form = useForm>({ - resolver: zodResolver(profileFormSchema), + const form = useForm({ + resolver: zodResolver(profileFormSchema) as any, defaultValues: { original_password: "", new_password: "", @@ -57,7 +57,7 @@ export const ProfileCard = ({ className }: { className: string }) => { const [open, setOpen] = useState(false) - const onSubmit = async (values: z.infer) => { + const onSubmit = async (values: any) => { try { await updateProfile(values) } catch (e) { diff --git a/src/components/server-config.tsx b/src/components/server-config.tsx index 7ef9dc6..d0fd738 100644 --- a/src/components/server-config.tsx +++ b/src/components/server-config.tsx @@ -27,6 +27,7 @@ import { IconButton } from "@/components/xui/icon-button" import { asOptionalField } from "@/lib/utils" import { ModelServerTaskResponse } from "@/types" import { zodResolver } from "@hookform/resolvers/zod" +import { CogIcon } from "lucide-react" import { useEffect, useState } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" @@ -58,7 +59,7 @@ const agentConfigSchema = z.object({ ), ), ip_report_period: asOptionalField(z.coerce.number().int().min(30)), - nic_allowlist: asOptionalField(z.record(z.boolean())), + nic_allowlist: asOptionalField(z.record(z.string(), z.boolean())), nic_allowlist_raw: asOptionalField( z.string().refine( (val) => { @@ -102,9 +103,10 @@ for (let i = 0; i < boolFields.length; i += 2) { interface ServerConfigCardProps extends ButtonProps { sid: number + menuItem?: boolean } -export const ServerConfigCard = ({ sid, ...props }: ServerConfigCardProps) => { +export const ServerConfigCard = ({ sid, menuItem = false, ...props }: ServerConfigCardProps) => { const { t } = useTranslation() const [data, setData] = useState(undefined) const [loading, setLoading] = useState(true) @@ -129,8 +131,8 @@ export const ServerConfigCard = ({ sid, ...props }: ServerConfigCardProps) => { if (open) fetchData() }, [open]) - const form = useForm({ - resolver: zodResolver(agentConfigSchema), + const form = useForm({ + resolver: zodResolver(agentConfigSchema) as any, defaultValues: { ...data, hard_drive_partition_allowlist_raw: JSON.stringify( @@ -155,7 +157,7 @@ export const ServerConfigCard = ({ sid, ...props }: ServerConfigCardProps) => { } }, [data, form]) - const onSubmit = async (values: AgentConfig) => { + const onSubmit = async (values: any) => { let resp: ModelServerTaskResponse = {} try { values.nic_allowlist = values.nic_allowlist_raw @@ -186,7 +188,18 @@ export const ServerConfigCard = ({ sid, ...props }: ServerConfigCardProps) => { return ( - + {menuItem ? ( + + ) : ( + + )} {loading ? ( @@ -283,7 +296,7 @@ export const ServerConfigCard = ({ sid, ...props }: ServerConfigCardProps) => {
{ @@ -69,8 +79,8 @@ const serverFormSchema = z.object({ export const ServerCard: React.FC = ({ data, mutate }) => { const { t } = useTranslation() - const form = useForm>({ - resolver: zodResolver(serverFormSchema), + const form = useForm({ + resolver: zodResolver(serverFormSchema) as any, defaultValues: { ...data, ddns_profiles_raw: data.ddns_profiles ? conv.arrToStr(data.ddns_profiles) : undefined, @@ -85,7 +95,131 @@ export const ServerCard: React.FC = ({ data, mutate }) => { const [open, setOpen] = useState(false) - const onSubmit = async (values: z.infer) => { + type PublicNote = { + billingDataMod: { + startDate: string + endDate: string + autoRenewal: string + cycle: string + amount: string + } + planDataMod: { + bandwidth: string + trafficVol: string + trafficType: string + IPv4: string + IPv6: string + networkRoute: string + extra: string + } + } + + const defaultPublicNote: PublicNote = { + billingDataMod: { + startDate: "", + endDate: "", + autoRenewal: "", + cycle: "", + amount: "", + }, + planDataMod: { + bandwidth: "", + trafficVol: "", + trafficType: "", + IPv4: "0", + IPv6: "0", + networkRoute: "", + extra: "", + }, + } + + const parsePublicNote = (s?: string): PublicNote => { + if (!s) return defaultPublicNote + try { + const obj = JSON.parse(s) + return { + billingDataMod: { + startDate: obj?.billingDataMod?.startDate ?? "", + endDate: obj?.billingDataMod?.endDate ?? "", + autoRenewal: obj?.billingDataMod?.autoRenewal ?? "", + cycle: obj?.billingDataMod?.cycle ?? "", + amount: obj?.billingDataMod?.amount ?? "", + }, + planDataMod: { + bandwidth: obj?.planDataMod?.bandwidth ?? "", + trafficVol: obj?.planDataMod?.trafficVol ?? "", + trafficType: obj?.planDataMod?.trafficType ?? "", + IPv4: obj?.planDataMod?.IPv4 === "1" ? "1" : "0", + IPv6: obj?.planDataMod?.IPv6 === "1" ? "1" : "0", + networkRoute: obj?.planDataMod?.networkRoute ?? "", + extra: obj?.planDataMod?.extra ?? "", + }, + } + } catch { + return defaultPublicNote + } + } + + const [publicNoteObj, setPublicNoteObj] = useState( + parsePublicNote(data?.public_note), + ) + const [publicNoteErrors, setPublicNoteErrors] = useState< + Partial< + Record< + | "billing.startDate" + | "billing.endDate" + | "billing.autoRenewal" + | "billing.cycle" + | "billing.amount" + | "plan.bandwidth" + | "plan.trafficVol" + | "plan.trafficType" + | "plan.IPv4" + | "plan.IPv6" + | "plan.extra", + string + > + > + >({}) + + const isValidISOLike = (v: string) => { + if (!v) return true + // special marker for "no expiry" + if (v === "0000-00-00T23:59:59+08:00") return true + const d = new Date(v) + return !isNaN(d.getTime()) + } + + const validatePublicNote = (pn: PublicNote) => { + const errs: Partial> = {} + + if (pn.billingDataMod.startDate && !isValidISOLike(pn.billingDataMod.startDate)) { + errs["billing.startDate"] = t("Validation.InvalidDate") + } + if (pn.billingDataMod.endDate && !isValidISOLike(pn.billingDataMod.endDate)) { + errs["billing.endDate"] = t("Validation.InvalidDate") + } + if (pn.billingDataMod.autoRenewal && !/^(0|1)$/.test(pn.billingDataMod.autoRenewal)) { + errs["billing.autoRenewal"] = t("Validation.MustBe0Or1") + } + if (pn.billingDataMod.cycle && !/^(Day|Week|Month|Year)$/i.test(pn.billingDataMod.cycle)) { + errs["billing.cycle"] = t("Validation.MustBeDayWeekMonthYear") + } + // amount 允许任意非空字符串或空 + if (pn.planDataMod.trafficType && !/^(1|2)$/.test(pn.planDataMod.trafficType)) { + errs["plan.trafficType"] = t("Validation.MustBe1Or2") + } + if (!/^(0|1)$/.test(pn.planDataMod.IPv4)) { + errs["plan.IPv4"] = t("Validation.MustBe0Or1") + } + if (!/^(0|1)$/.test(pn.planDataMod.IPv6)) { + errs["plan.IPv6"] = t("Validation.MustBe0Or1") + } + + return { errors: errs, valid: Object.keys(errs).length === 0 } + } + + const onSubmit = async (values: any) => { try { values.ddns_profiles = values.ddns_profiles_raw ? conv.strToArr(values.ddns_profiles_raw).map(Number) @@ -93,6 +227,36 @@ export const ServerCard: React.FC = ({ data, mutate }) => { values.override_ddns_domains = values.override_ddns_domains_raw ? JSON.parse(values.override_ddns_domains_raw) : undefined + + // validate structured fields + const { errors, valid } = validatePublicNote(publicNoteObj) + if (!valid) { + setPublicNoteErrors(errors) + toast(t("Error"), { description: t("Validation.InvalidForm") }) + return + } + setPublicNoteErrors({}) + + // normalize datetime-local to ISO string if provided + const normalizeISO = (v: string) => { + if (!v) return v + // keep special "no expiry" value as-is + if (v === "0000-00-00T23:59:59+08:00") return v + const date = new Date(v) + return isNaN(date.getTime()) ? v : date.toISOString() + } + const pnNormalized: PublicNote = { + billingDataMod: { + ...publicNoteObj.billingDataMod, + startDate: normalizeISO(publicNoteObj.billingDataMod.startDate), + endDate: normalizeISO(publicNoteObj.billingDataMod.endDate), + // keep others as-is + }, + planDataMod: { ...publicNoteObj.planDataMod }, + } + + // serialize structured public note back to JSON string + values.public_note = JSON.stringify(pnNormalized) await updateServer(data!.id!, values) } catch (e) { console.error(e) @@ -111,7 +275,11 @@ export const ServerCard: React.FC = ({ data, mutate }) => { - + e.preventDefault()} + onEscapeKeyDown={(e) => e.preventDefault()} + >
@@ -236,19 +404,527 @@ export const ServerCard: React.FC = ({ data, mutate }) => { )} /> - ( - - {t("Public") + t("Note")} - -