import { z } from "zod"; import { ReactNode } from "react"; import { Globe, Users, Mail, FileText, Server, HardDrive, Shield } from "lucide-react"; import { FieldConfig, SectionConfig } from "@/types/admin-panel"; // 1) 使用 zod 定义“通用配置”结构与规则(与表单值结构一致,使用嵌套对象) export const commonConfigSchema = z.object({ site: z.object({ name: z.string().min(2, "网站名称至少2个字符").max(50, "网站名称最多50个字符"), description: z.string().max(200, "网站描述最多200个字符").optional().or(z.literal("")), keywords: z.string().optional().or(z.literal("")), url: z.string() .refine((url) => { if (!url || url === "") return true; // 允许空值 return url.startsWith("http://") || url.startsWith("https://"); }, "无效的URL格式,必须以http://或https://开头") .optional().or(z.literal("")), logo: z.string().url("请输入有效的Logo地址").optional().or(z.literal("")), icp: z.string().optional().or(z.literal("")), icp_url: z.string().url("请输入有效的备案链接").optional().or(z.literal("")), color_style: z.enum(["light", "dark", "auto"]).default("light"), }), user: z.object({ default_avatar: z.string().url("请输入有效的头像URL").optional().or(z.literal("")), default_role: z.enum(["user", "editor", "admin"]).default("user"), register_invite_code: z.boolean().default(false), register_email_verification: z.boolean().default(false), open_login: z.boolean().default(true), open_reset_password: z.boolean().default(true), }), email: z.object({ smtp_host: z.string().optional().or(z.literal("")), smtp_port: z.number().int().min(1).max(65535).default(465), smtp_user: z.string().optional().or(z.literal("")), smtp_password: z.string().optional().or(z.literal("")), smtp_from: z.string().email("请输入有效的发信地址").optional().or(z.literal("")), smtp_from_name: z.string().optional().or(z.literal("")), smtp_from_email: z.string() .refine((email) => { if (!email || email === "") return true; // 允许空值 return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); }, "无效的邮箱格式") .optional().or(z.literal("")), system_template: z.string().default("default"), }), blog: z.object({ default_author: z.string().optional().or(z.literal("")), default_category: z.string().optional().or(z.literal("")), default_tag: z.string().optional().or(z.literal("")), open_comment: z.boolean().default(true), }), logging: z.object({ level: z.enum(["trace", "debug", "info", "warn", "error"]).default("info"), max_files: z.number().int().min(1).max(1000).default(10), max_file_size: z.number().int().min(1).max(10240).default(10), }), cache: z.object({ ttl: z.number().int().min(1).max(31_536_000).default(3600), max_size: z.number().int().min(1).max(1_048_576).default(1024), }), switch: z.object({ open_register: z.boolean().default(true), open_login: z.boolean().default(true), open_reset_password: z.boolean().default(true), open_comment: z.boolean().default(true), open_like: z.boolean().default(true), open_share: z.boolean().default(true), open_view: z.boolean().default(true), }) }); // 2) 最小化配置,用于生成 FieldConfig(避免重复手写 FieldConfig) type Meta = Omit & { defaultValue?: any }; const makeField = (id: string, meta: Meta): FieldConfig => ({ id, ...meta, value: meta.defaultValue }); const sectionIcons: Record = { site: , user: , email: , blog: , logging: , cache: , switch: , }; const sectionTitles: Record = { site: "网站信息", user: "用户设置", email: "邮件设置", blog: "博客设置", logging: "日志设置", cache: "缓存设置", switch: "功能开关", }; // 3) 通用配置的字段元数据(用于生成表单) export const commonFieldsMeta: Array<{ id: string; meta: Meta }> = [ // site { id: "site.name", meta: { label: "网站名称", type: "input", validation: { required: true, minLength: 2, maxLength: 50 } } }, { id: "site.description", meta: { label: "网站描述", type: "textarea", rows: 3 } }, { id: "site.keywords", meta: { label: "关键词", type: "input", description: "逗号分隔,如:blog,tech,ai" } }, { id: "site.url", meta: { label: "站点URL", type: "url" } }, { id: "site.logo", meta: { label: "Logo地址", type: "url" } }, { id: "site.icp", meta: { label: "ICP备案号", type: "input" } }, { id: "site.icp_url", meta: { label: "备案链接", type: "url" } }, { id: "site.color_style", meta: { label: "配色风格", type: "select", options: [ { label: "浅色", value: "light" }, { label: "深色", value: "dark" }, { label: "自动", value: "auto" } ] } }, // user { id: "user.default_avatar", meta: { label: "默认头像URL", type: "url" } }, { id: "user.default_role", meta: { label: "默认角色", type: "select", options: [ { label: "用户", value: "user" }, { label: "编辑", value: "editor" }, { label: "管理员", value: "admin" } ] } }, { id: "user.register_invite_code", meta: { label: "注册需邀请码", type: "switch" } }, { id: "user.register_email_verification", meta: { label: "注册需邮箱验证", type: "switch" } }, { id: "user.open_login", meta: { label: "开启登录", type: "switch" } }, { id: "user.open_reset_password", meta: { label: "开启重置密码", type: "switch" } }, // email { id: "email.smtp_host", meta: { label: "SMTP 主机", type: "input" } }, { id: "email.smtp_port", meta: { label: "SMTP 端口", type: "number", min: 1, max: 65535 } }, { id: "email.smtp_user", meta: { label: "SMTP 用户名", type: "input" } }, { id: "email.smtp_password", meta: { label: "SMTP 密码", type: "password" } }, { id: "email.smtp_from", meta: { label: "发信地址", type: "email" } }, { id: "email.smtp_from_name", meta: { label: "发信人名称", type: "input" } }, { id: "email.smtp_from_email", meta: { label: "发信邮箱", type: "email" } }, { id: "email.system_template", meta: { label: "系统模板", type: "input" } }, // blog { id: "blog.default_author", meta: { label: "默认作者", type: "input" } }, { id: "blog.default_category", meta: { label: "默认分类", type: "input" } }, { id: "blog.default_tag", meta: { label: "默认标签", type: "input" } }, { id: "blog.open_comment", meta: { label: "开启评论", type: "switch" } }, // logging { id: "logging.level", meta: { label: "日志级别", type: "select", options: [ { label: "跟踪", value: "trace" }, { label: "调试", value: "debug" }, { label: "信息", value: "info" }, { label: "警告", value: "warn" }, { label: "错误", value: "error" } ] } }, { id: "logging.max_files", meta: { label: "最大文件数", type: "number", min: 1, max: 1000 } }, { id: "logging.max_file_size", meta: { label: "单文件大小(MB)", type: "number", min: 1, max: 10240 } }, // cache { id: "cache.ttl", meta: { label: "TTL(秒)", type: "number", min: 1, max: 31536000 } }, { id: "cache.max_size", meta: { label: "最大容量(MB)", type: "number", min: 1, max: 1048576 } }, // switch { id: "switch.open_register", meta: { label: "开放注册", type: "switch" } }, { id: "switch.open_login", meta: { label: "开放登录", type: "switch" } }, { id: "switch.open_reset_password", meta: { label: "开放重置密码", type: "switch" } }, { id: "switch.open_comment", meta: { label: "开放评论", type: "switch" } }, { id: "switch.open_like", meta: { label: "开放点赞", type: "switch" } }, { id: "switch.open_share", meta: { label: "开放分享", type: "switch" } }, { id: "switch.open_view", meta: { label: "开放浏览", type: "switch" } }, ]; // 4) 根据元数据分组生成 Section 列表(供 dynamic-admin-config 使用) export function buildCommonSectionsFromMeta(): SectionConfig[] { const groupMap: Record = {}; for (const { id, meta } of commonFieldsMeta) { const [group] = id.split("."); if (!groupMap[group]) groupMap[group] = []; groupMap[group].push(makeField(id, meta)); } return Object.entries(groupMap).map(([group, fields]) => ({ id: `common-${group}`, title: sectionTitles[group] || group, icon: sectionIcons[group], fields, })); } // 5) 将 zod 校验错误转换为 AdminPanel 的错误映射 export function zodErrorsToAdminErrors(result: z.ZodSafeParseError | z.ZodSafeParseSuccess): Record { if (result.success) return {}; const errors: Record = {}; for (const issue of result.error.issues) { const path = issue.path.join("."); if (path) { errors[path] = issue.message; } } return errors; } export type CommonConfig = z.infer;