增加ip 的白名單

總共7個IP地址,分佈在4個地點:
岡山:1個IP
汐止:2個IP
新竹:2個IP
璟茂:2個IP
This commit is contained in:
2025-08-01 12:59:44 +08:00
parent b261cc277a
commit 2282eed9a1
9 changed files with 8617 additions and 16 deletions

View File

@@ -0,0 +1,210 @@
"use client"
import { useState, useEffect } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Switch } from "@/components/ui/switch"
import { Shield, ShieldAlert, Globe, Save, RefreshCw } from "lucide-react"
import { toast } from "@/components/ui/use-toast"
interface IpInfo {
ip: string
isAllowed: boolean
enableIpWhitelist: boolean
allowedIps: string[]
timestamp: string
}
export default function IpWhitelistPage() {
const [ipInfo, setIpInfo] = useState<IpInfo | null>(null)
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [enableWhitelist, setEnableWhitelist] = useState(false)
const [allowedIps, setAllowedIps] = useState("")
useEffect(() => {
fetchIpInfo()
}, [])
const fetchIpInfo = async () => {
try {
setLoading(true)
const response = await fetch('/api/ip')
if (!response.ok) {
throw new Error('無法獲取IP信息')
}
const data = await response.json()
setIpInfo(data)
setEnableWhitelist(data.enableIpWhitelist)
setAllowedIps(data.allowedIps.join(', '))
} catch (error) {
console.error("無法獲取IP信息:", error)
toast({
title: "錯誤",
description: "無法獲取IP信息",
variant: "destructive",
})
} finally {
setLoading(false)
}
}
const handleSave = async () => {
try {
setSaving(true)
// 這裡應該調用API來保存設置
// 由於這是前端演示,我們只顯示成功消息
toast({
title: "成功",
description: "IP白名單設置已保存",
})
} catch (error) {
console.error("保存失敗:", error)
toast({
title: "錯誤",
description: "保存設置失敗",
variant: "destructive",
})
} finally {
setSaving(false)
}
}
if (loading) {
return (
<div className="min-h-screen bg-gradient-to-b from-slate-900 via-blue-900 to-indigo-900 flex items-center justify-center">
<div className="text-white text-center">
<RefreshCw className="w-8 h-8 animate-spin mx-auto mb-4" />
<p>...</p>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gradient-to-b from-slate-900 via-blue-900 to-indigo-900 p-4">
<div className="container mx-auto max-w-4xl">
<div className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">IP </h1>
<p className="text-blue-200">IP地址</p>
</div>
<div className="grid gap-6 md:grid-cols-2">
{/* 當前IP狀態 */}
<Card className="bg-slate-800/50 border-blue-800/30">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<Globe className="w-5 h-5" />
IP狀態
</CardTitle>
</CardHeader>
<CardContent>
{ipInfo && (
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-blue-200">IP地址:</span>
<span className="text-white font-mono">{ipInfo.ip}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-blue-200">:</span>
<div className="flex items-center gap-2">
{ipInfo.enableIpWhitelist && !ipInfo.isAllowed ? (
<ShieldAlert className="w-4 h-4 text-red-400" />
) : (
<Shield className="w-4 h-4 text-green-400" />
)}
<span className={ipInfo.enableIpWhitelist && !ipInfo.isAllowed ? "text-red-400" : "text-green-400"}>
{ipInfo.enableIpWhitelist && !ipInfo.isAllowed ? "拒絕" : "允許"}
</span>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-blue-200">:</span>
<span className="text-white text-sm">
{new Date(ipInfo.timestamp).toLocaleString('zh-TW')}
</span>
</div>
</div>
)}
</CardContent>
</Card>
{/* 白名單設置 */}
<Card className="bg-slate-800/50 border-blue-800/30">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<Shield className="w-5 h-5" />
</CardTitle>
<CardDescription className="text-blue-200">
IP地址
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<Label htmlFor="enable-whitelist" className="text-blue-200">
IP白名單
</Label>
<Switch
id="enable-whitelist"
checked={enableWhitelist}
onCheckedChange={setEnableWhitelist}
/>
</div>
<div className="space-y-2">
<Label htmlFor="allowed-ips" className="text-blue-200">
IP地址
</Label>
<Input
id="allowed-ips"
value={allowedIps}
onChange={(e) => setAllowedIps(e.target.value)}
placeholder="192.168.1.0/24, 10.0.0.50, 172.16.0.0/16"
className="bg-slate-700 border-blue-600 text-white"
/>
<p className="text-xs text-blue-300">
IP (192.168.1.100)IP範圍 (192.168.1.0/24)IP用逗號分隔
</p>
</div>
<Button
onClick={handleSave}
disabled={saving}
className="w-full bg-blue-600 hover:bg-blue-700"
>
{saving ? (
<RefreshCw className="w-4 h-4 animate-spin mr-2" />
) : (
<Save className="w-4 h-4 mr-2" />
)}
</Button>
</CardContent>
</Card>
</div>
{/* 使用說明 */}
<Card className="mt-6 bg-slate-800/50 border-blue-800/30">
<CardHeader>
<CardTitle className="text-white">使</CardTitle>
</CardHeader>
<CardContent>
<div className="text-blue-200 space-y-2 text-sm">
<p> <strong>IP白名單</strong>IP才能訪問系統</p>
<p> <strong>IP格式支援</strong></p>
<ul className="ml-6 space-y-1">
<li> IP192.168.1.100</li>
<li> IP範圍192.168.1.0/24 (CIDR格式)</li>
<li> IP 192.168.1.100, 10.0.0.50</li>
</ul>
<p> <strong></strong></p>
</div>
</CardContent>
</Card>
</div>
</div>
)
}

29
app/api/ip/route.ts Normal file
View File

@@ -0,0 +1,29 @@
import { NextRequest, NextResponse } from 'next/server'
import { getClientIp, isIpAllowed } from '@/lib/ip-utils'
// 強制動態渲染
export const dynamic = 'force-dynamic'
export async function GET(request: NextRequest) {
try {
const clientIp = getClientIp(request)
const allowedIps = process.env.ALLOWED_IPS || ''
const enableIpWhitelist = process.env.ENABLE_IP_WHITELIST === 'true'
const isAllowed = enableIpWhitelist ? isIpAllowed(clientIp, allowedIps) : true
return NextResponse.json({
ip: clientIp,
isAllowed,
enableIpWhitelist,
allowedIps: enableIpWhitelist ? allowedIps.split(',').map(ip => ip.trim()) : [],
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Error getting IP info:', error)
return NextResponse.json(
{ error: '無法獲取IP信息' },
{ status: 500 }
)
}
}