230 lines
6.9 KiB
TypeScript
230 lines
6.9 KiB
TypeScript
"use client"
|
|
|
|
import { format } from "date-fns"
|
|
import { Suspense, useCallback, useState } from "react"
|
|
import { toast } from "sonner"
|
|
import {
|
|
clear,
|
|
createProxyChains,
|
|
createProxyPort,
|
|
getGatewayPage,
|
|
updateGateway,
|
|
} from "@/actions/gateway"
|
|
import { Auth } from "@/components/auth"
|
|
import { DataTable, useDataTable } from "@/components/data-table"
|
|
import { Page } from "@/components/page"
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
import { ScopeChannelWriteClearExpired, ScopeProxyWrite } from "@/lib/scopes"
|
|
import type { Gateway } from "@/models/gateway"
|
|
import CreatePage from "./create"
|
|
|
|
export default function GatewayPage() {
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const getGatewayPageWrapper = useCallback((page: number, size: number) => {
|
|
return getGatewayPage({ page, size })
|
|
}, [])
|
|
|
|
const table = useDataTable(getGatewayPageWrapper)
|
|
|
|
const handleToggle = async (id: number, status: number) => {
|
|
setLoading(true)
|
|
try {
|
|
const result = await updateGateway({
|
|
id: id,
|
|
status: status === 0 ? 1 : 0,
|
|
})
|
|
if (result.success) {
|
|
toast.success(status === 0 ? "启用成功" : "停用成功")
|
|
table.refresh()
|
|
} else {
|
|
toast.error(result.message || "操作失败")
|
|
}
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : error
|
|
toast.error(`操作失败: ${message}`)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const clearExpired = async (val: Gateway) => {
|
|
setLoading(true)
|
|
try {
|
|
const result = await clear({
|
|
proxy_id: val.id,
|
|
})
|
|
|
|
if (result.success) {
|
|
const count =
|
|
result.data && "count" in result.data ? result.data.count : 0
|
|
toast.success(`清理过期连接成功,共 ${count} 条`)
|
|
} else {
|
|
toast.error(result.message || "清理过期连接失败,请稍后重试一下~")
|
|
}
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : error
|
|
toast.error(`清理过期连接失败: ${message}`)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const createPort = async (val: Gateway) => {
|
|
setLoading(true)
|
|
try {
|
|
const result = await createProxyPort({
|
|
id: val.id,
|
|
})
|
|
|
|
if (result.success) {
|
|
toast.success(`重建端口池成功!`)
|
|
} else {
|
|
toast.error(result.message || "请稍后重试一下~")
|
|
}
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : error
|
|
toast.error(`清理过期连接失败: ${message}`)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const createChains = async (val: Gateway) => {
|
|
setLoading(true)
|
|
try {
|
|
const result = await createProxyChains({
|
|
id: val.id,
|
|
})
|
|
|
|
if (result.success) {
|
|
toast.success(`重建代理链成功!!!`)
|
|
} else {
|
|
toast.error(result.message || "请稍后重试一下~")
|
|
}
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : error
|
|
toast.error(`清理过期连接失败: ${message}`)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Page>
|
|
<div className="flex justify-between items-stretch">
|
|
<div className="flex gap-3">
|
|
<Auth scope={ScopeProxyWrite}>
|
|
<CreatePage onSuccess={table.refresh} />
|
|
</Auth>
|
|
</div>
|
|
</div>
|
|
<Suspense>
|
|
<DataTable<Gateway>
|
|
{...table}
|
|
status={loading ? "load" : "done"}
|
|
columns={[
|
|
{
|
|
header: "域名",
|
|
accessorKey: "host",
|
|
},
|
|
{ header: "IP地址", accessorKey: "ip" },
|
|
{ header: "端口", accessorKey: "port" },
|
|
{
|
|
header: "MAC地址",
|
|
accessorKey: "mac",
|
|
},
|
|
{
|
|
header: "密钥",
|
|
accessorKey: "secret",
|
|
},
|
|
{
|
|
header: "类型",
|
|
accessorKey: "type",
|
|
cell: ({ row }) => {
|
|
const typeMap: Record<number, string> = {
|
|
1: "自有",
|
|
2: "白银",
|
|
3: "GOST",
|
|
}
|
|
return typeMap[row.original.type] ?? ""
|
|
},
|
|
},
|
|
{
|
|
header: "状态",
|
|
accessorKey: "status",
|
|
cell: ({ row }) => (row.original.status === 0 ? "离线" : "在线"),
|
|
},
|
|
{
|
|
header: "创建时间",
|
|
accessorKey: "created_at",
|
|
cell: ({ row }) =>
|
|
format(
|
|
new Date(row.original.created_at),
|
|
"yyyy-MM-dd HH:mm:ss",
|
|
),
|
|
},
|
|
{
|
|
id: "action",
|
|
meta: { pin: "right" },
|
|
header: "操作",
|
|
cell: ({ row }) => {
|
|
return (
|
|
<div className="flex gap-2 ">
|
|
<Button
|
|
onClick={() =>
|
|
handleToggle(row.original.id, row.original.status)
|
|
}
|
|
disabled={loading}
|
|
variant={
|
|
row.original.status === 0 ? "default" : "destructive"
|
|
}
|
|
size="sm"
|
|
>
|
|
{row.original.status === 0 ? "启用" : "停用"}
|
|
</Button>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button size="sm" variant="outline">
|
|
打开菜单
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-8">
|
|
<Auth scope={ScopeChannelWriteClearExpired}>
|
|
<DropdownMenuItem
|
|
onClick={() => clearExpired(row.original)}
|
|
>
|
|
清理过期连接
|
|
</DropdownMenuItem>
|
|
</Auth>
|
|
<Auth scope={ScopeProxyWrite}>
|
|
<DropdownMenuItem
|
|
onClick={() => createPort(row.original)}
|
|
>
|
|
重建端口池
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onClick={() => createChains(row.original)}
|
|
>
|
|
重建代理链
|
|
</DropdownMenuItem>
|
|
</Auth>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
)
|
|
},
|
|
},
|
|
]}
|
|
/>
|
|
</Suspense>
|
|
</Page>
|
|
)
|
|
}
|