@@ -6,7 +6,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
66import { useToast } from "@/components/ui/use-toast"
77import { useRouter } from "next/navigation"
88import { Tabs , TabsContent , TabsList , TabsTrigger } from "@/components/ui/tabs"
9- import { Download , Upload } from "lucide-react"
9+ import { Download , RefreshCw } from "lucide-react"
1010import {
1111 AlertDialog ,
1212 AlertDialogAction ,
@@ -624,84 +624,34 @@ export default function ConfigPage() {
624624 URL . revokeObjectURL ( url )
625625 }
626626
627- const importConfig = ( event : React . ChangeEvent < HTMLInputElement > ) => {
628- const file = event . target . files ?. [ 0 ]
629- if ( ! file ) return
630-
631- const reader = new FileReader ( )
632- reader . onload = ( e ) => {
633- try {
634- const content = e . target ?. result as string
635- const newConfig = JSON . parse ( content )
636-
637- // 验证配置结构
638- if ( ! newConfig . MAP || typeof newConfig . MAP !== 'object' ) {
639- throw new Error ( '配置文件缺少 MAP 字段或格式不正确' )
640- }
641-
642- if ( ! newConfig . Compression ||
643- typeof newConfig . Compression !== 'object' ||
644- ! newConfig . Compression . Gzip ||
645- ! newConfig . Compression . Brotli ) {
646- throw new Error ( '配置文件压缩设置格式不正确' )
647- }
627+ const pullConfigFromD1 = async ( ) => {
628+ try {
629+ const response = await fetch ( '/admin/api/config/pull' , {
630+ method : 'POST' ,
631+ } )
648632
649- // 如果没有安全配置,添加默认配置
650- if ( ! newConfig . Security ) {
651- newConfig . Security = {
652- IPBan : {
653- Enabled : false ,
654- ErrorThreshold : 10 ,
655- WindowMinutes : 5 ,
656- BanDurationMinutes : 5 ,
657- CleanupIntervalMinutes : 1
658- }
659- }
660- }
633+ if ( ! response . ok ) {
634+ const errorText = await response . text ( )
635+ throw new Error ( errorText || '从 D1 拉取配置失败' )
636+ }
661637
662- // 验证路径映射
663- for ( const [ path , target ] of Object . entries ( newConfig . MAP ) ) {
664- if ( ! path . startsWith ( '/' ) ) {
665- throw new Error ( `路径 ${ path } 必须以/开头` )
666- }
638+ const pulledConfig = await response . json ( )
667639
668- if ( typeof target === 'string' ) {
669- try {
670- new URL ( target )
671- } catch {
672- throw new Error ( `路径 ${ path } 的目标URL格式不正确` )
673- }
674- } else if ( target && typeof target === 'object' ) {
675- const mapping = target as PathMapping
676- if ( ! mapping . DefaultTarget || typeof mapping . DefaultTarget !== 'string' ) {
677- throw new Error ( `路径 ${ path } 的默认目标格式不正确` )
678- }
679- try {
680- new URL ( mapping . DefaultTarget )
681- } catch {
682- throw new Error ( `路径 ${ path } 的默认目标URL格式不正确` )
683- }
684- } else {
685- throw new Error ( `路径 ${ path } 的目标格式不正确` )
686- }
687- }
640+ // 使用 setConfig 而不是 updateConfig,因为拉取的配置已经在服务端更新过了
641+ isConfigFromApiRef . current = true
642+ setConfig ( pulledConfig )
688643
689- // 使用setConfig而不是updateConfig,因为导入的配置不应触发自动保存
690- isConfigFromApiRef . current = true
691- setConfig ( newConfig )
692- toast ( {
693- title : "成功" ,
694- description : "配置已导入" ,
695- } )
696- } catch ( error ) {
697- toast ( {
698- title : "错误" ,
699- description : error instanceof Error ? error . message : "配置文件格式错误" ,
700- variant : "destructive" ,
701- } )
702- }
644+ toast ( {
645+ title : "成功" ,
646+ description : "已从 D1 拉取最新配置" ,
647+ } )
648+ } catch ( error ) {
649+ toast ( {
650+ title : "错误" ,
651+ description : error instanceof Error ? error . message : "从 D1 拉取配置失败" ,
652+ variant : "destructive" ,
653+ } )
703654 }
704- reader . readAsText ( file )
705655 }
706656
707657 const handleEditPath = ( path : string , target : PathMapping | string ) => {
@@ -986,18 +936,10 @@ export default function ConfigPage() {
986936 < Download className = "w-4 h-4 mr-2" />
987937 导出配置
988938 </ Button >
989- < label >
990- < Button variant = "outline" className = "cursor-pointer" >
991- < Upload className = "w-4 h-4 mr-2" />
992- 导入配置
993- </ Button >
994- < input
995- type = "file"
996- className = "hidden"
997- accept = ".json"
998- onChange = { importConfig }
999- />
1000- </ label >
939+ < Button onClick = { pullConfigFromD1 } variant = "outline" >
940+ < RefreshCw className = "w-4 h-4 mr-2" />
941+ 从 D1 拉取配置
942+ </ Button >
1001943 { saving && (
1002944 < div className = "flex items-center text-sm text-muted-foreground" >
1003945 < span className = "animate-pulse mr-2" > ●</ span >
0 commit comments