diff --git a/CHANGELOG.md b/CHANGELOG.md index ccc2d98..36be7b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,12 @@ + # Changelog # [1.1.0](https://github.com/perfect-panel/ppanel-web/compare/v1.0.2...v1.1.0) (2025-07-06) - ### ✨ Features -* **view**: Add AnyTLS protocol support and enhance node configuration options ([bcfb10a](https://github.com/perfect-panel/ppanel-web/commit/bcfb10a)) +- **view**: Add AnyTLS protocol support and enhance node configuration options ([bcfb10a](https://github.com/perfect-panel/ppanel-web/commit/bcfb10a)) diff --git a/apps/admin/app/dashboard/rules/import-yaml-rules.tsx b/apps/admin/app/dashboard/rules/import-yaml-rules.tsx index 7ac6434..57d029d 100644 --- a/apps/admin/app/dashboard/rules/import-yaml-rules.tsx +++ b/apps/admin/app/dashboard/rules/import-yaml-rules.tsx @@ -88,12 +88,32 @@ export default function ImportYamlRules({ onImportSuccess }: ImportYamlRulesProp if (!groups[policyGroup]) { groups[policyGroup] = []; } - groups[policyGroup].push(cleanRule); + + // 不插入 MATCH 规则,只用于标识默认规则组 + if (!rule.trim().startsWith('MATCH,')) { + groups[policyGroup].push(cleanRule); + } } return groups; }; + const checkIfDefaultRule = (originalRules: string[], groupName: string): boolean => { + return originalRules.some((rule) => { + const trimmedRule = rule.trim(); + if (!trimmedRule.startsWith('MATCH,')) return false; + + // 检查 MATCH 规则是否属于当前组 + const parts = trimmedRule.split(','); + if (parts.length >= 3) { + const ruleGroup = parts[2]?.trim(); + return ruleGroup === groupName; + } + + return groupName === 'default'; + }); + }; + const handleImport = async () => { if (!yamlContent) { toast.error(t('pleaseUploadFile')); @@ -130,12 +150,17 @@ export default function ImportYamlRules({ onImportSuccess }: ImportYamlRulesProp for (let i = 0; i < groups.length; i++) { const group = groups[i]; if (!group?.name || !group?.rules.length) continue; + + const isDefault = checkIfDefaultRule(allRules, group.name); + await createRuleGroup({ name: group.name, rules: group?.rules.join('\n'), enable: false, tags: [], icon: '', + type: 'auto', + default: isDefault, }); setImportProgress(i + 1); } diff --git a/apps/admin/app/dashboard/rules/page.tsx b/apps/admin/app/dashboard/rules/page.tsx index 08d265d..b2f2458 100644 --- a/apps/admin/app/dashboard/rules/page.tsx +++ b/apps/admin/app/dashboard/rules/page.tsx @@ -50,6 +50,8 @@ export default function Page() { enable: false, tags: values.tags || [], icon: values.icon || '', + type: values.type || 'auto', + default: false, }); toast.success(t('createSuccess')); ref.current?.refresh(); @@ -99,6 +101,22 @@ export default function Page() { ); }, }, + { + accessorKey: 'default', + header: t('defaultRule'), + cell: ({ row }) => ( + { + await updateRuleGroup({ + ...row.original, + default: checked, + } as API.UpdateRuleGroupRequest); + ref.current?.refresh(); + }} + /> + ), + }, { accessorKey: 'icon', header: t('appIcon'), @@ -122,6 +140,16 @@ export default function Page() { accessorKey: 'name', header: t('name'), }, + { + accessorKey: 'type', + header: t('type'), + cell: ({ row }) => { + const type = row.original.type; + if (type === 'auto') return t('auto'); + if (type === 'ban') return t('ban'); + return type || '--'; + }, + }, { accessorKey: 'tags', header: t('tags'), @@ -163,6 +191,8 @@ export default function Page() { rules: values.rules, enable: row.enable, icon: values.icon, + type: values.type, + default: row.default, }); toast.success(t('updateSuccess')); ref.current?.refresh(); diff --git a/apps/admin/app/dashboard/rules/rule-form.tsx b/apps/admin/app/dashboard/rules/rule-form.tsx index 695b34e..8796e04 100644 --- a/apps/admin/app/dashboard/rules/rule-form.tsx +++ b/apps/admin/app/dashboard/rules/rule-form.tsx @@ -13,6 +13,13 @@ import { FormMessage, } from '@workspace/ui/components/form'; import { ScrollArea } from '@workspace/ui/components/scroll-area'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@workspace/ui/components/select'; import { Sheet, SheetContent, @@ -36,6 +43,7 @@ const formSchema = z.object({ tags: z.array(z.string()).default([]), rules: z.string().default(''), icon: z.string().default(''), + type: z.string().default('auto'), }); interface RuleFormProps { @@ -147,6 +155,32 @@ export default function RuleForm>({ )} /> + ( + + {t('type')} + + + + + + )} + />