mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-15 00:56:08 +00:00
feat(groups): reset group traffic without touching client counters
The group page shows traffic counting per group, but the only reset available zeroed every member client's up/down counters (and their quotas) via bulkResetTraffic. Group traffic is a derived sum of client traffic, so zeroing the group display previously required mutating the clients themselves. Add a display-only baseline: ClientGroup gains reset_up/reset_down columns (additive, handled by AutoMigrate). ResetGroupTraffic snapshots the group's current up/down sum into the baseline, and ListGroups now reports max(0, sum - baseline). Client counters are left untouched and no Xray restart is triggered. A new POST /panel/api/clients/groups/ resetTraffic endpoint drives it, creating the client_groups row when the group exists only as a derived label. The groups page action now calls the new endpoint; confirm/success strings updated across all 13 locales to reflect group-only semantics.
This commit is contained in:
@@ -784,6 +784,13 @@ export const sections: readonly Section[] = [
|
||||
body: '{\n "name": "customer-a"\n}',
|
||||
response: '{\n "success": true,\n "obj": {\n "affected": 5\n }\n}',
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/panel/api/clients/groups/resetTraffic',
|
||||
summary: 'Reset only the group-level traffic counter shown on the groups page. Snapshots the current up/down sum of the group\'s members as a baseline so the group total reads zero, while leaving each client\'s own counters (and their quotas) untouched. No Xray restart is triggered. Creates the client_groups row if the group exists only as a derived label.',
|
||||
body: '{\n "name": "customer-a"\n}',
|
||||
response: '{\n "success": true,\n "obj": {\n "name": "customer-a"\n }\n}',
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/panel/api/clients/resetTraffic/:email',
|
||||
|
||||
@@ -126,9 +126,9 @@ export default function GroupsPage() {
|
||||
onSuccess: (msg) => { if (msg?.success) invalidate(); },
|
||||
});
|
||||
|
||||
const bulkResetMut = useMutation({
|
||||
mutationFn: (body: { emails: string[] }) =>
|
||||
HttpUtil.post('/panel/api/clients/bulkResetTraffic', body, JSON_HEADERS),
|
||||
const groupResetMut = useMutation({
|
||||
mutationFn: (body: { name: string }) =>
|
||||
HttpUtil.post('/panel/api/clients/groups/resetTraffic', body, JSON_HEADERS),
|
||||
onSuccess: (msg) => { if (msg?.success) invalidate(); },
|
||||
});
|
||||
|
||||
@@ -321,17 +321,14 @@ export default function GroupsPage() {
|
||||
}
|
||||
modal.confirm({
|
||||
title: t('pages.groups.resetConfirmTitle', { name: g.name }),
|
||||
content: t('pages.groups.resetConfirmContent', { count: g.clientCount }),
|
||||
content: t('pages.groups.resetConfirmContent'),
|
||||
okText: t('reset'),
|
||||
okType: 'danger',
|
||||
cancelText: t('cancel'),
|
||||
onOk: async () => {
|
||||
const emails = await fetchEmailsForGroup(g.name);
|
||||
if (emails.length === 0) return;
|
||||
const msg = await bulkResetMut.mutateAsync({ emails });
|
||||
const msg = await groupResetMut.mutateAsync({ name: g.name });
|
||||
if (msg?.success) {
|
||||
const affected = (msg.obj as { affected?: number } | undefined)?.affected ?? emails.length;
|
||||
messageApi.success(t('pages.groups.resetSuccess', { count: affected }));
|
||||
messageApi.success(t('pages.groups.resetSuccess', { name: g.name }));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user