Files
3x-ui/frontend/src/pages/clients/useClients.js
T
MHSanaei 2bcf287cf1 feat(clients): add top-level Clients tab and CRUD API
Adds /panel/api/clients endpoints (list, get, add, update, del,
attach, detach) backed by ClientService methods that orchestrate
the per-inbound Add/Update/Del flows so a single client row is
created once and attached to many inbounds in one operation.

The frontend gains a dedicated Clients page (frontend/clients.html
+ src/pages/clients/) with an AntD table, multi-inbound attach
modal, and full CRUD. Axios interceptor learns to honour
Content-Type: application/json so the JSON endpoints work
alongside the legacy form-encoded ones.

The legacy per-inbound client modal stays untouched in this PR —
both flows now write to the same source of truth.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 07:28:55 +02:00

79 lines
2.1 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 loading = ref(false);
const fetched = ref(false);
async function refresh() {
loading.value = true;
try {
const [clientsMsg, inboundsMsg] = await Promise.all([
HttpUtil.get('/panel/api/clients/list'),
HttpUtil.get('/panel/api/inbounds/list'),
]);
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 create(payload) {
const msg = await HttpUtil.post('/panel/api/clients/add', payload, JSON_HEADERS);
if (msg?.success) await refresh();
return msg;
}
async function update(id, client) {
const msg = await HttpUtil.post(`/panel/api/clients/update/${id}`, client, JSON_HEADERS);
if (msg?.success) await refresh();
return msg;
}
async function remove(id, keepTraffic = false) {
const url = keepTraffic
? `/panel/api/clients/del/${id}?keepTraffic=1`
: `/panel/api/clients/del/${id}`;
const msg = await HttpUtil.post(url);
if (msg?.success) await refresh();
return msg;
}
async function attach(id, inboundIds) {
const msg = await HttpUtil.post(`/panel/api/clients/${id}/attach`, { inboundIds }, JSON_HEADERS);
if (msg?.success) await refresh();
return msg;
}
async function detach(id, inboundIds) {
const msg = await HttpUtil.post(`/panel/api/clients/${id}/detach`, { inboundIds }, JSON_HEADERS);
if (msg?.success) await refresh();
return msg;
}
onMounted(refresh);
return {
clients,
inbounds,
loading,
fetched,
refresh,
create,
update,
remove,
attach,
detach,
};
}