mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-14 08:36:07 +00:00
1b436bb3e0
Read pageSize from defaultSettings and apply it to the clients table so the panel-wide pagination preference is respected. Widen the AntD size-changer trigger and its teleported popup so '100 / page' no longer truncates.
221 lines
6.7 KiB
JavaScript
221 lines
6.7 KiB
JavaScript
import { onMounted, ref, shallowRef } from 'vue';
|
|
import { HttpUtil } from '@/utils';
|
|
|
|
const JSON_HEADERS = { headers: { 'Content-Type': 'application/json' } };
|
|
|
|
export function useClients() {
|
|
const clients = shallowRef([]);
|
|
const inbounds = shallowRef([]);
|
|
const onlines = ref([]);
|
|
const loading = ref(false);
|
|
const fetched = ref(false);
|
|
const subSettings = ref({ enable: false, subURI: '', subJsonURI: '', subJsonEnable: false });
|
|
const ipLimitEnable = ref(false);
|
|
const tgBotEnable = ref(false);
|
|
const expireDiff = ref(0);
|
|
const trafficDiff = ref(0);
|
|
const pageSize = ref(0);
|
|
|
|
async function refresh() {
|
|
loading.value = true;
|
|
try {
|
|
const [clientsMsg, inboundsMsg] = await Promise.all([
|
|
HttpUtil.get('/panel/api/clients/list'),
|
|
HttpUtil.get('/panel/api/inbounds/options'),
|
|
]);
|
|
if (clientsMsg?.success) {
|
|
clients.value = Array.isArray(clientsMsg.obj) ? clientsMsg.obj : [];
|
|
}
|
|
if (inboundsMsg?.success) {
|
|
inbounds.value = Array.isArray(inboundsMsg.obj) ? inboundsMsg.obj : [];
|
|
}
|
|
fetched.value = true;
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
async function fetchSubSettings() {
|
|
const msg = await HttpUtil.post('/panel/setting/defaultSettings');
|
|
if (!msg?.success) return;
|
|
const s = msg.obj || {};
|
|
subSettings.value = {
|
|
enable: !!s.subEnable,
|
|
subURI: s.subURI || '',
|
|
subJsonURI: s.subJsonURI || '',
|
|
subJsonEnable: !!s.subJsonEnable,
|
|
};
|
|
ipLimitEnable.value = !!s.ipLimitEnable;
|
|
tgBotEnable.value = !!s.tgBotEnable;
|
|
expireDiff.value = (s.expireDiff ?? 0) * 86400000;
|
|
trafficDiff.value = (s.trafficDiff ?? 0) * 1073741824;
|
|
pageSize.value = s.pageSize ?? 0;
|
|
}
|
|
|
|
async function create(payload) {
|
|
const msg = await HttpUtil.post('/panel/api/clients/add', payload, JSON_HEADERS);
|
|
if (msg?.success) await refresh();
|
|
return msg;
|
|
}
|
|
|
|
async function update(email, client) {
|
|
if (!email) return null;
|
|
const encoded = encodeURIComponent(email);
|
|
const msg = await HttpUtil.post(`/panel/api/clients/update/${encoded}`, client, JSON_HEADERS);
|
|
if (msg?.success) await refresh();
|
|
return msg;
|
|
}
|
|
|
|
async function remove(email, keepTraffic = false) {
|
|
if (!email) return null;
|
|
const encoded = encodeURIComponent(email);
|
|
const url = keepTraffic
|
|
? `/panel/api/clients/del/${encoded}?keepTraffic=1`
|
|
: `/panel/api/clients/del/${encoded}`;
|
|
const msg = await HttpUtil.post(url);
|
|
if (msg?.success) await refresh();
|
|
return msg;
|
|
}
|
|
|
|
async function removeMany(emails, keepTraffic = false) {
|
|
if (!Array.isArray(emails) || emails.length === 0) return [];
|
|
const suffix = keepTraffic ? '?keepTraffic=1' : '';
|
|
const silentOpts = { silent: true };
|
|
const results = await Promise.all(emails.map((email) => {
|
|
const url = `/panel/api/clients/del/${encodeURIComponent(email)}${suffix}`;
|
|
return HttpUtil.post(url, undefined, silentOpts);
|
|
}));
|
|
await refresh();
|
|
return results;
|
|
}
|
|
|
|
async function attach(email, inboundIds) {
|
|
if (!email) return null;
|
|
const encoded = encodeURIComponent(email);
|
|
const msg = await HttpUtil.post(`/panel/api/clients/${encoded}/attach`, { inboundIds }, JSON_HEADERS);
|
|
if (msg?.success) await refresh();
|
|
return msg;
|
|
}
|
|
|
|
async function detach(email, inboundIds) {
|
|
if (!email) return null;
|
|
const encoded = encodeURIComponent(email);
|
|
const msg = await HttpUtil.post(`/panel/api/clients/${encoded}/detach`, { inboundIds }, JSON_HEADERS);
|
|
if (msg?.success) await refresh();
|
|
return msg;
|
|
}
|
|
|
|
async function resetTraffic(client) {
|
|
if (!client?.email) return null;
|
|
const url = `/panel/api/clients/resetTraffic/${encodeURIComponent(client.email)}`;
|
|
const msg = await HttpUtil.post(url);
|
|
if (msg?.success) await refresh();
|
|
return msg;
|
|
}
|
|
|
|
async function resetAllTraffics() {
|
|
const msg = await HttpUtil.post('/panel/api/clients/resetAllTraffics');
|
|
if (msg?.success) await refresh();
|
|
return msg;
|
|
}
|
|
|
|
async function delDepleted() {
|
|
const msg = await HttpUtil.post('/panel/api/clients/delDepleted');
|
|
if (msg?.success) await refresh();
|
|
return msg;
|
|
}
|
|
|
|
async function setEnable(client, enable) {
|
|
if (!client?.email) return null;
|
|
const payload = {
|
|
email: client.email,
|
|
subId: client.subId,
|
|
id: client.uuid,
|
|
password: client.password,
|
|
auth: client.auth,
|
|
totalGB: client.totalGB || 0,
|
|
expiryTime: client.expiryTime || 0,
|
|
limitIp: client.limitIp || 0,
|
|
comment: client.comment || '',
|
|
enable: !!enable,
|
|
};
|
|
return update(client.email, payload);
|
|
}
|
|
|
|
function applyTrafficEvent(payload) {
|
|
if (!payload || typeof payload !== 'object') return;
|
|
if (Array.isArray(payload.onlineClients)) {
|
|
onlines.value = payload.onlineClients;
|
|
}
|
|
}
|
|
|
|
function applyClientStatsEvent(payload) {
|
|
if (!payload || typeof payload !== 'object') return;
|
|
if (!Array.isArray(payload.clients) || payload.clients.length === 0) return;
|
|
const byEmail = new Map();
|
|
for (const row of payload.clients) {
|
|
if (row && row.email) byEmail.set(row.email, row);
|
|
}
|
|
let touched = false;
|
|
const next = clients.value || [];
|
|
for (let i = 0; i < next.length; i++) {
|
|
const row = next[i];
|
|
const upd = byEmail.get(row?.email);
|
|
if (!upd) continue;
|
|
const merged = { ...(row.traffic || {}) };
|
|
if (typeof upd.up === 'number') merged.up = upd.up;
|
|
if (typeof upd.down === 'number') merged.down = upd.down;
|
|
if (typeof upd.total === 'number') merged.total = upd.total;
|
|
if (typeof upd.expiryTime === 'number') merged.expiryTime = upd.expiryTime;
|
|
if (typeof upd.enable === 'boolean') merged.enable = upd.enable;
|
|
if (typeof upd.lastOnline === 'number') merged.lastOnline = upd.lastOnline;
|
|
next[i] = { ...row, traffic: merged };
|
|
touched = true;
|
|
}
|
|
if (touched) clients.value = [...next];
|
|
}
|
|
|
|
let invalidateTimer = null;
|
|
function applyInvalidate(payload) {
|
|
if (!payload || typeof payload !== 'object') return;
|
|
if (payload.type !== 'inbounds' && payload.type !== 'clients') return;
|
|
if (invalidateTimer) clearTimeout(invalidateTimer);
|
|
invalidateTimer = setTimeout(() => {
|
|
invalidateTimer = null;
|
|
refresh();
|
|
}, 200);
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await Promise.all([refresh(), fetchSubSettings()]);
|
|
});
|
|
|
|
return {
|
|
clients,
|
|
inbounds,
|
|
onlines,
|
|
loading,
|
|
fetched,
|
|
subSettings,
|
|
ipLimitEnable,
|
|
tgBotEnable,
|
|
expireDiff,
|
|
trafficDiff,
|
|
pageSize,
|
|
refresh,
|
|
create,
|
|
update,
|
|
remove,
|
|
removeMany,
|
|
attach,
|
|
detach,
|
|
resetTraffic,
|
|
resetAllTraffics,
|
|
delDepleted,
|
|
setEnable,
|
|
applyTrafficEvent,
|
|
applyClientStatsEvent,
|
|
applyInvalidate,
|
|
};
|
|
}
|