mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-30 16:26:36 +08:00
feat: add forward flow reset action
This commit is contained in:
@@ -217,6 +217,8 @@ export const pauseForwardService = (forwardId: number) =>
|
|||||||
Network.post("/forward/pause", { id: forwardId });
|
Network.post("/forward/pause", { id: forwardId });
|
||||||
export const resumeForwardService = (forwardId: number) =>
|
export const resumeForwardService = (forwardId: number) =>
|
||||||
Network.post("/forward/resume", { id: forwardId });
|
Network.post("/forward/resume", { id: forwardId });
|
||||||
|
export const resetForwardFlow = (forwardId: number) =>
|
||||||
|
Network.post("/forward/reset-flow", { id: forwardId });
|
||||||
|
|
||||||
// 转发诊断操作
|
// 转发诊断操作
|
||||||
export const diagnoseForward = (forwardId: number) =>
|
export const diagnoseForward = (forwardId: number) =>
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ import {
|
|||||||
getNodeList,
|
getNodeList,
|
||||||
pauseForwardService,
|
pauseForwardService,
|
||||||
resumeForwardService,
|
resumeForwardService,
|
||||||
|
resetForwardFlow,
|
||||||
diagnoseForward,
|
diagnoseForward,
|
||||||
updateForwardOrder,
|
updateForwardOrder,
|
||||||
getConfigByName,
|
getConfigByName,
|
||||||
@@ -230,7 +231,7 @@ const FORWARD_GROUPED_TABLE_COLUMN_CLASS = {
|
|||||||
strategy: "w-[100px]",
|
strategy: "w-[100px]",
|
||||||
totalFlow: "w-[120px]",
|
totalFlow: "w-[120px]",
|
||||||
status: "w-[100px]",
|
status: "w-[100px]",
|
||||||
actions: "w-[144px] text-right",
|
actions: "w-[176px] text-right",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const normalizeForwardUserName = (userName?: string): string => {
|
const normalizeForwardUserName = (userName?: string): string => {
|
||||||
@@ -764,6 +765,7 @@ const SortableTableRow = ({
|
|||||||
handleEdit,
|
handleEdit,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
handleDiagnose,
|
handleDiagnose,
|
||||||
|
handleResetFlow,
|
||||||
showAddressModal,
|
showAddressModal,
|
||||||
formatFlow,
|
formatFlow,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
@@ -919,6 +921,29 @@ const SortableTableRow = ({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
isIconOnly
|
||||||
|
className="bg-secondary/10 text-secondary hover:bg-secondary/20"
|
||||||
|
isDisabled={(forward.inFlow || 0) + (forward.outFlow || 0) <= 0}
|
||||||
|
size="sm"
|
||||||
|
title="流量清零"
|
||||||
|
onPress={() => handleResetFlow(forward)}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
className="h-4 w-4"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4 4v6h6M20 20v-6h-6M20 9a8 8 0 00-13.657-3.657L4 8m16 8-2.343 2.657A8 8 0 014 15"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
className="bg-danger/10 text-danger hover:bg-danger/20"
|
className="bg-danger/10 text-danger hover:bg-danger/20"
|
||||||
@@ -958,6 +983,7 @@ const SortableCompactTableRow = ({
|
|||||||
handleEdit,
|
handleEdit,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
handleDiagnose,
|
handleDiagnose,
|
||||||
|
handleResetFlow,
|
||||||
showAddressModal,
|
showAddressModal,
|
||||||
hasMultipleAddresses,
|
hasMultipleAddresses,
|
||||||
formatFlow,
|
formatFlow,
|
||||||
@@ -1144,6 +1170,29 @@ const SortableCompactTableRow = ({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
isIconOnly
|
||||||
|
className="bg-secondary/10 text-secondary hover:bg-secondary/20"
|
||||||
|
isDisabled={(forward.inFlow || 0) + (forward.outFlow || 0) <= 0}
|
||||||
|
size="sm"
|
||||||
|
title="流量清零"
|
||||||
|
onPress={() => handleResetFlow(forward)}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
className="h-4 w-4"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4 4v6h6M20 20v-6h-6M20 9a8 8 0 00-13.657-3.657L4 8m16 8-2.343 2.657A8 8 0 014 15"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
className="bg-danger/10 text-danger hover:bg-danger/20"
|
className="bg-danger/10 text-danger hover:bg-danger/20"
|
||||||
@@ -1289,13 +1338,18 @@ export default function ForwardPage() {
|
|||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
// isFilterModalOpen removed
|
// isFilterModalOpen removed
|
||||||
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
||||||
|
const [resetFlowModalOpen, setResetFlowModalOpen] = useState(false);
|
||||||
const [addressModalOpen, setAddressModalOpen] = useState(false);
|
const [addressModalOpen, setAddressModalOpen] = useState(false);
|
||||||
const [diagnosisModalOpen, setDiagnosisModalOpen] = useState(false);
|
const [diagnosisModalOpen, setDiagnosisModalOpen] = useState(false);
|
||||||
const [isEdit, setIsEdit] = useState(false);
|
const [isEdit, setIsEdit] = useState(false);
|
||||||
const [submitLoading, setSubmitLoading] = useState(false);
|
const [submitLoading, setSubmitLoading] = useState(false);
|
||||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||||
|
const [resetFlowLoading, setResetFlowLoading] = useState(false);
|
||||||
const [diagnosisLoading, setDiagnosisLoading] = useState(false);
|
const [diagnosisLoading, setDiagnosisLoading] = useState(false);
|
||||||
const [forwardToDelete, setForwardToDelete] = useState<Forward | null>(null);
|
const [forwardToDelete, setForwardToDelete] = useState<Forward | null>(null);
|
||||||
|
const [forwardToResetFlow, setForwardToResetFlow] = useState<Forward | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
const [currentDiagnosisForward, setCurrentDiagnosisForward] =
|
const [currentDiagnosisForward, setCurrentDiagnosisForward] =
|
||||||
useState<Forward | null>(null);
|
useState<Forward | null>(null);
|
||||||
const [diagnosisResult, setDiagnosisResult] =
|
const [diagnosisResult, setDiagnosisResult] =
|
||||||
@@ -2171,7 +2225,8 @@ export default function ForwardPage() {
|
|||||||
maxConn: forward.maxConn ?? 0,
|
maxConn: forward.maxConn ?? 0,
|
||||||
proxyProtocol: forward.proxyProtocol ?? 0,
|
proxyProtocol: forward.proxyProtocol ?? 0,
|
||||||
proxyProtocolReceive: forward.proxyProtocolReceive ?? 0,
|
proxyProtocolReceive: forward.proxyProtocolReceive ?? 0,
|
||||||
proxyProtocolSend: forward.proxyProtocolSend ?? forward.proxyProtocol ?? 0,
|
proxyProtocolSend:
|
||||||
|
forward.proxyProtocolSend ?? forward.proxyProtocol ?? 0,
|
||||||
});
|
});
|
||||||
setErrors({});
|
setErrors({});
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
@@ -2183,6 +2238,46 @@ export default function ForwardPage() {
|
|||||||
setDeleteModalOpen(true);
|
setDeleteModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleResetFlow = (forward: Forward) => {
|
||||||
|
if ((forward.inFlow || 0) + (forward.outFlow || 0) <= 0) return;
|
||||||
|
|
||||||
|
setForwardToResetFlow(forward);
|
||||||
|
setResetFlowModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResetFlowModalOpenChange = (isOpen: boolean) => {
|
||||||
|
if (resetFlowLoading) return;
|
||||||
|
|
||||||
|
setResetFlowModalOpen(isOpen);
|
||||||
|
if (!isOpen) {
|
||||||
|
setForwardToResetFlow(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmResetFlow = async () => {
|
||||||
|
if (!forwardToResetFlow) return;
|
||||||
|
|
||||||
|
setResetFlowLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await resetForwardFlow(forwardToResetFlow.id);
|
||||||
|
|
||||||
|
if (res.code !== 0) {
|
||||||
|
toast.error(res.msg || "流量清零失败");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success("规则流量已清零");
|
||||||
|
setResetFlowModalOpen(false);
|
||||||
|
setForwardToResetFlow(null);
|
||||||
|
await refreshForwardList(false);
|
||||||
|
} catch {
|
||||||
|
toast.error("流量清零失败");
|
||||||
|
} finally {
|
||||||
|
setResetFlowLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 确认删除规则
|
// 确认删除规则
|
||||||
const confirmDelete = async () => {
|
const confirmDelete = async () => {
|
||||||
if (!forwardToDelete) return;
|
if (!forwardToDelete) return;
|
||||||
@@ -3957,7 +4052,7 @@ export default function ForwardPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-1.5 mt-3">
|
<div className="grid grid-cols-2 gap-1.5 mt-3">
|
||||||
<Button
|
<Button
|
||||||
className="flex-1 min-h-8"
|
className="flex-1 min-h-8"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -4000,6 +4095,32 @@ export default function ForwardPage() {
|
|||||||
>
|
>
|
||||||
诊断
|
诊断
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="flex-1 min-h-8"
|
||||||
|
color="secondary"
|
||||||
|
isDisabled={(forward.inFlow || 0) + (forward.outFlow || 0) <= 0}
|
||||||
|
size="sm"
|
||||||
|
startContent={
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
className="w-3 h-3"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4 4v6h6M20 20v-6h-6M20 9a8 8 0 00-13.657-3.657L4 8m16 8-2.343 2.657A8 8 0 014 15"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
}
|
||||||
|
variant="flat"
|
||||||
|
onPress={() => handleResetFlow(forward)}
|
||||||
|
>
|
||||||
|
清零
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="flex-1 min-h-8"
|
className="flex-1 min-h-8"
|
||||||
color="danger"
|
color="danger"
|
||||||
@@ -4302,7 +4423,7 @@ export default function ForwardPage() {
|
|||||||
<TableColumn className="w-[80px]">策略</TableColumn>
|
<TableColumn className="w-[80px]">策略</TableColumn>
|
||||||
<TableColumn className="w-[100px]">用量</TableColumn>
|
<TableColumn className="w-[100px]">用量</TableColumn>
|
||||||
<TableColumn className="w-[80px]">状态</TableColumn>
|
<TableColumn className="w-[80px]">状态</TableColumn>
|
||||||
<TableColumn align="left" className="w-[120px] pl-4">
|
<TableColumn align="left" className="w-[160px] pl-4">
|
||||||
操作
|
操作
|
||||||
</TableColumn>
|
</TableColumn>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
@@ -4321,6 +4442,7 @@ export default function ForwardPage() {
|
|||||||
handleDelete={handleDelete}
|
handleDelete={handleDelete}
|
||||||
handleDiagnose={handleDiagnose}
|
handleDiagnose={handleDiagnose}
|
||||||
handleEdit={handleEdit}
|
handleEdit={handleEdit}
|
||||||
|
handleResetFlow={handleResetFlow}
|
||||||
handleServiceToggle={handleServiceToggle}
|
handleServiceToggle={handleServiceToggle}
|
||||||
hasMultipleAddresses={hasMultipleAddresses}
|
hasMultipleAddresses={hasMultipleAddresses}
|
||||||
selectMode={selectMode}
|
selectMode={selectMode}
|
||||||
@@ -4616,6 +4738,7 @@ export default function ForwardPage() {
|
|||||||
handleDelete={handleDelete}
|
handleDelete={handleDelete}
|
||||||
handleDiagnose={handleDiagnose}
|
handleDiagnose={handleDiagnose}
|
||||||
handleEdit={handleEdit}
|
handleEdit={handleEdit}
|
||||||
|
handleResetFlow={handleResetFlow}
|
||||||
handleServiceToggle={
|
handleServiceToggle={
|
||||||
handleServiceToggle
|
handleServiceToggle
|
||||||
}
|
}
|
||||||
@@ -5172,6 +5295,59 @@ export default function ForwardPage() {
|
|||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{/* 规则流量清零确认模态框 */}
|
||||||
|
<Modal
|
||||||
|
backdrop="blur"
|
||||||
|
classNames={{
|
||||||
|
base: "!w-[calc(100%-32px)] !mx-auto sm:!w-full rounded-2xl overflow-hidden",
|
||||||
|
}}
|
||||||
|
isOpen={resetFlowModalOpen}
|
||||||
|
placement="center"
|
||||||
|
scrollBehavior="inside"
|
||||||
|
size="lg"
|
||||||
|
onOpenChange={handleResetFlowModalOpenChange}
|
||||||
|
>
|
||||||
|
<ModalContent>
|
||||||
|
{(onClose) => (
|
||||||
|
<>
|
||||||
|
<ModalHeader className="flex flex-col gap-1">
|
||||||
|
<h2 className="text-lg font-bold text-secondary">
|
||||||
|
确认流量清零
|
||||||
|
</h2>
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<p className="text-default-600">
|
||||||
|
确定要清零规则{" "}
|
||||||
|
<span className="font-semibold text-foreground">
|
||||||
|
"{forwardToResetFlow?.name}"
|
||||||
|
</span>{" "}
|
||||||
|
当前显示的上传和下载流量吗?
|
||||||
|
</p>
|
||||||
|
<p className="text-small text-default-500 mt-2">
|
||||||
|
此操作不可撤销,但不会影响用户总流量、用户隧道配额和历史统计。
|
||||||
|
</p>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button
|
||||||
|
isDisabled={resetFlowLoading}
|
||||||
|
variant="light"
|
||||||
|
onPress={onClose}
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="secondary"
|
||||||
|
isLoading={resetFlowLoading}
|
||||||
|
onPress={confirmResetFlow}
|
||||||
|
>
|
||||||
|
确认清零
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
{/* 地址列表弹窗 */}
|
{/* 地址列表弹窗 */}
|
||||||
<Modal
|
<Modal
|
||||||
classNames={{
|
classNames={{
|
||||||
|
|||||||
Reference in New Issue
Block a user