|
|
|
|
@@ -1,9 +1,9 @@
|
|
|
|
|
import { useState } from 'react'
|
|
|
|
|
import { useMemo, useState } from 'react'
|
|
|
|
|
import {
|
|
|
|
|
Alert, Button, Form, Input, InputNumber, Modal, Select, Space, Switch, Tag, Tooltip, Typography, message,
|
|
|
|
|
} from 'antd'
|
|
|
|
|
import type { ColumnsType } from 'antd/es/table'
|
|
|
|
|
import { CheckCircleOutlined, CloseCircleOutlined, CloudServerOutlined, PlusOutlined } from '@ant-design/icons'
|
|
|
|
|
import { ArrowDownOutlined, ArrowUpOutlined, CheckCircleOutlined, CloseCircleOutlined, CloudServerOutlined, PlusOutlined } from '@ant-design/icons'
|
|
|
|
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
|
|
|
import { useTranslation } from 'react-i18next'
|
|
|
|
|
|
|
|
|
|
@@ -97,6 +97,20 @@ export default function ForwardProxyPage() {
|
|
|
|
|
onError: (e: Error) => message.error(e.message),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const sortedACLs = useMemo(
|
|
|
|
|
() => [...(data ?? [])].sort((a, b) => a.priority - b.priority),
|
|
|
|
|
[data],
|
|
|
|
|
)
|
|
|
|
|
const swapACLs = useMutation({
|
|
|
|
|
mutationFn: async ({ a, b }: { a: ACL; b: ACL }) => {
|
|
|
|
|
const stripMeta = ({ id: _id, created_at: _ca, updated_at: _ua, ...r }: ACL) => r
|
|
|
|
|
await apiClient.put(`/forward-proxy/acls/${a.id}`, { ...stripMeta(a), priority: b.priority })
|
|
|
|
|
await apiClient.put(`/forward-proxy/acls/${b.id}`, { ...stripMeta(b), priority: a.priority })
|
|
|
|
|
},
|
|
|
|
|
onSuccess: () => { void qc.invalidateQueries({ queryKey: ['fwd-proxy', 'acls'] }) },
|
|
|
|
|
onError: (e: Error) => message.error(e.message),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const cols: ColumnsType<ACL> = [
|
|
|
|
|
{ title: t('fwd.priority'), dataIndex: 'priority', key: 'priority', width: 90 },
|
|
|
|
|
{ title: t('fwd.name'), dataIndex: 'name', key: 'name', render: (s: string) => <code>{s}</code> },
|
|
|
|
|
@@ -120,6 +134,27 @@ export default function ForwardProxyPage() {
|
|
|
|
|
/>
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '', key: 'move', width: 64,
|
|
|
|
|
render: (_, row) => {
|
|
|
|
|
const idx = sortedACLs.findIndex(r => r.id === row.id)
|
|
|
|
|
const swapping = swapACLs.isPending
|
|
|
|
|
return (
|
|
|
|
|
<Space size={2}>
|
|
|
|
|
<Tooltip title={t('fw.rule.moveUp')}>
|
|
|
|
|
<Button size="small" icon={<ArrowUpOutlined />}
|
|
|
|
|
disabled={isViewer || idx <= 0 || swapping}
|
|
|
|
|
onClick={() => swapACLs.mutate({ a: row, b: sortedACLs[idx - 1] })} />
|
|
|
|
|
</Tooltip>
|
|
|
|
|
<Tooltip title={t('fw.rule.moveDown')}>
|
|
|
|
|
<Button size="small" icon={<ArrowDownOutlined />}
|
|
|
|
|
disabled={isViewer || idx >= sortedACLs.length - 1 || swapping}
|
|
|
|
|
onClick={() => swapACLs.mutate({ a: row, b: sortedACLs[idx + 1] })} />
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</Space>
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t('common.actions'), key: 'actions',
|
|
|
|
|
render: (_, row) => (
|
|
|
|
|
|