3xui-api-client 2.1.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/index.d.ts +72 -1
- package/index.js +365 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.0.1] - 2026-06-07
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Re-release as 3.0.1 — version 3.0.0 was previously published and unpublished, making it permanently blocked on npm.
|
|
12
|
+
|
|
13
|
+
## [3.0.0] - 2026-06-07
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- 🔑 **API Token Authentication** — Pass `token` or `apiToken` in options to authenticate via Bearer token instead of cookie-based login. Resolves compatibility with 3x-ui v3.0.2+ where the legacy login mechanism changed. ([#1](https://github.com/iamhelitha/3xui-api-client/issues/1))
|
|
17
|
+
- 🆕 **Object-style constructor** — `new ThreeXUI(url, { username, password, token })` as an alternative to the positional `(url, user, pass)` signature.
|
|
18
|
+
- 📡 **48 new Modern API routes** (3x-ui v2.x/v3.x endpoints):
|
|
19
|
+
- **Clients** (25 routes): `getClients`, `getPagedClients`, `getClient`, `getClientTraffic`, `getSubLinks`, `getClientLinks`, `addModernClient`, `updateModernClient`, `deleteModernClient`, `attachClientToInbounds`, `detachClientFromInbounds`, `resetAllModernClientTraffics`, `deleteDepletedModernClients`, bulk operations (`bulkAdjust`, `bulkDel`, `bulkCreate`, `bulkAttach`, `bulkDetach`, `bulkResetTraffic`), `resetModernClientTrafficByEmail`, `updateModernClientTrafficByEmail`, `getModernClientIps`, `clearModernClientIps`, `getOnlines`, `getModernLastOnline`
|
|
20
|
+
- **Client Groups** (7 routes): `getGroups`, `getGroupEmails`, `createGroup`, `renameGroup`, `deleteGroup`, `bulkAddGroups`, `bulkRemoveGroups`
|
|
21
|
+
- **Nodes** (9 routes): `getNodes`, `getNode`, `getNodeHistory`, `addNode`, `updateNode`, `deleteNode`, `setNodeEnable`, `testNode`, `probeNode`
|
|
22
|
+
- **Custom Geo** (7 routes): `getCustomGeos`, `getGeoAliases`, `addCustomGeo`, `updateCustomGeo`, `deleteCustomGeo`, `downloadCustomGeo`, `updateAllCustomGeo`
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- Constructor signature extended to accept an options object as the second argument (fully backward compatible — existing `(url, user, pass)` usage is unchanged).
|
|
26
|
+
- `_request()` and `login()` skip cookie-based auth flow when a token is configured.
|
|
27
|
+
- `isSessionValid()` returns `true` immediately when token auth is active.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- ESLint errors (trailing whitespace, missing curly braces) introduced in new route additions.
|
|
31
|
+
- Indentation inconsistency in TypeScript declarations (`updateClientWithCredentials`).
|
|
32
|
+
|
|
33
|
+
### Backward Compatibility
|
|
34
|
+
- All existing `(url, username, password)` constructor calls continue to work without any changes.
|
|
35
|
+
- All 55 original API routes are unchanged.
|
|
36
|
+
- Verified against older 3x-ui servers — cookie-based login, session management, and all core routes function identically.
|
|
37
|
+
|
|
8
38
|
## [2.1.1] - 2025-11-30
|
|
9
39
|
|
|
10
40
|
### Fixed
|
package/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ declare module '3xui-api-client' {
|
|
|
16
16
|
isDevelopment?: boolean;
|
|
17
17
|
enableCSP?: boolean;
|
|
18
18
|
userAgent?: string;
|
|
19
|
+
token?: string;
|
|
20
|
+
apiToken?: string;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export interface LoginResponse {
|
|
@@ -226,6 +228,7 @@ declare module '3xui-api-client' {
|
|
|
226
228
|
|
|
227
229
|
export default class ThreeXUI {
|
|
228
230
|
constructor(baseURL: string, username: string, password: string, options?: ThreeXUIOptions);
|
|
231
|
+
constructor(baseURL: string, options: ThreeXUIOptions);
|
|
229
232
|
|
|
230
233
|
// Authentication
|
|
231
234
|
login(forceRefresh?: boolean): Promise<LoginResponse>;
|
|
@@ -252,14 +255,82 @@ declare module '3xui-api-client' {
|
|
|
252
255
|
|
|
253
256
|
// Enhanced Client Management
|
|
254
257
|
addClientWithCredentials(inboundId: number, protocol: string, options?: CredentialOptions): Promise<any>;
|
|
255
|
-
|
|
258
|
+
updateClientWithCredentials(clientId: string, inboundId: number, options?: CredentialOptions): Promise<any>;
|
|
256
259
|
|
|
257
260
|
// Session Management
|
|
258
261
|
getSessionStats(): Promise<any>;
|
|
259
262
|
clearAllSessions(): Promise<void>;
|
|
260
263
|
isSessionValid(): Promise<boolean>;
|
|
261
264
|
|
|
265
|
+
// ===========================================
|
|
266
|
+
// Modern API Methods (3X-UI >= 2.x)
|
|
267
|
+
// ===========================================
|
|
268
|
+
|
|
269
|
+
// --- Clients ---
|
|
270
|
+
getClients(): Promise<any>;
|
|
271
|
+
getPagedClients(params?: {
|
|
272
|
+
page?: number;
|
|
273
|
+
size?: number;
|
|
274
|
+
sort?: string;
|
|
275
|
+
order?: 'asc' | 'desc';
|
|
276
|
+
email?: string;
|
|
277
|
+
}): Promise<any>;
|
|
278
|
+
getClient(email: string): Promise<any>;
|
|
279
|
+
getClientTraffic(email: string): Promise<any>;
|
|
280
|
+
getSubLinks(subId: string): Promise<any>;
|
|
281
|
+
getClientLinks(email: string): Promise<any>;
|
|
282
|
+
addModernClient(data: any): Promise<any>;
|
|
283
|
+
updateModernClient(email: string, data: any): Promise<any>;
|
|
284
|
+
deleteModernClient(email: string): Promise<any>;
|
|
285
|
+
attachClientToInbounds(email: string, data: any): Promise<any>;
|
|
286
|
+
detachClientFromInbounds(email: string, data: any): Promise<any>;
|
|
287
|
+
resetAllModernClientTraffics(): Promise<any>;
|
|
288
|
+
deleteDepletedModernClients(): Promise<any>;
|
|
289
|
+
bulkAdjustModernClients(data: any): Promise<any>;
|
|
290
|
+
bulkDeleteModernClients(data: any): Promise<any>;
|
|
291
|
+
bulkCreateModernClients(data: any): Promise<any>;
|
|
292
|
+
bulkAttachModernClients(data: any): Promise<any>;
|
|
293
|
+
bulkDetachModernClients(data: any): Promise<any>;
|
|
294
|
+
bulkResetTrafficModernClients(data: any): Promise<any>;
|
|
295
|
+
resetModernClientTrafficByEmail(email: string): Promise<any>;
|
|
296
|
+
updateModernClientTrafficByEmail(email: string, data: any): Promise<any>;
|
|
297
|
+
getModernClientIps(email: string): Promise<any>;
|
|
298
|
+
clearModernClientIps(email: string): Promise<any>;
|
|
299
|
+
getOnlines(): Promise<any>;
|
|
300
|
+
getModernLastOnline(): Promise<any>;
|
|
301
|
+
|
|
302
|
+
// --- Client Groups ---
|
|
303
|
+
getGroups(): Promise<any>;
|
|
304
|
+
getGroupEmails(groupName: string): Promise<any>;
|
|
305
|
+
createGroup(data: any): Promise<any>;
|
|
306
|
+
renameGroup(data: any): Promise<any>;
|
|
307
|
+
deleteGroup(data: any): Promise<any>;
|
|
308
|
+
bulkAddGroups(data: any): Promise<any>;
|
|
309
|
+
bulkRemoveGroups(data: any): Promise<any>;
|
|
310
|
+
|
|
311
|
+
// --- Nodes ---
|
|
312
|
+
getNodes(): Promise<any>;
|
|
313
|
+
getNode(id: number | string): Promise<any>;
|
|
314
|
+
getNodeHistory(id: number | string, metric: string, bucket: string): Promise<any>;
|
|
315
|
+
addNode(data: any): Promise<any>;
|
|
316
|
+
updateNode(id: number | string, data: any): Promise<any>;
|
|
317
|
+
deleteNode(id: number | string): Promise<any>;
|
|
318
|
+
setNodeEnable(id: number | string): Promise<any>;
|
|
319
|
+
testNode(data: any): Promise<any>;
|
|
320
|
+
probeNode(id: number | string): Promise<any>;
|
|
321
|
+
|
|
322
|
+
// --- Custom Geo ---
|
|
323
|
+
getCustomGeos(): Promise<any>;
|
|
324
|
+
getGeoAliases(): Promise<any>;
|
|
325
|
+
addCustomGeo(data: any): Promise<any>;
|
|
326
|
+
updateCustomGeo(id: string | number, data: any): Promise<any>;
|
|
327
|
+
deleteCustomGeo(id: string | number): Promise<any>;
|
|
328
|
+
downloadCustomGeo(id: string | number): Promise<any>;
|
|
329
|
+
updateAllCustomGeo(): Promise<any>;
|
|
330
|
+
|
|
331
|
+
// ===========================================
|
|
262
332
|
// Original API Methods
|
|
333
|
+
// ===========================================
|
|
263
334
|
getInbounds(): Promise<any>;
|
|
264
335
|
getInbound(id: number): Promise<any>;
|
|
265
336
|
addInbound(inboundConfig: InboundConfig): Promise<any>;
|
package/index.js
CHANGED
|
@@ -32,16 +32,10 @@ class ThreeXUI {
|
|
|
32
32
|
* @param {number} options.timeout - Request timeout in milliseconds (default: 30000)
|
|
33
33
|
* @throws {Error} If baseURL, username, or password is missing
|
|
34
34
|
*/
|
|
35
|
-
constructor(baseURL,
|
|
35
|
+
constructor(baseURL, usernameOrOptions, password, options = {}) {
|
|
36
36
|
if (!baseURL) {
|
|
37
37
|
throw new Error('baseURL is required');
|
|
38
38
|
}
|
|
39
|
-
if (!username) {
|
|
40
|
-
throw new Error('username is required');
|
|
41
|
-
}
|
|
42
|
-
if (!password) {
|
|
43
|
-
throw new Error('password is required');
|
|
44
|
-
}
|
|
45
39
|
|
|
46
40
|
// Apply security validations
|
|
47
41
|
this.baseURL = InputValidator.validateURL(baseURL);
|
|
@@ -53,8 +47,32 @@ class ThreeXUI {
|
|
|
53
47
|
this.baseURL = this.baseURL.replace(/\/panel\/?$/, '');
|
|
54
48
|
}
|
|
55
49
|
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
// Object containing configs as 2nd parameter
|
|
51
|
+
if (typeof usernameOrOptions === 'object' && usernameOrOptions !== null) {
|
|
52
|
+
options = usernameOrOptions;
|
|
53
|
+
this.username = options.username;
|
|
54
|
+
this.password = options.password;
|
|
55
|
+
} else {
|
|
56
|
+
this.username = usernameOrOptions;
|
|
57
|
+
this.password = password;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.token = options.token || options.apiToken || null;
|
|
61
|
+
|
|
62
|
+
if (this.token) {
|
|
63
|
+
this.username = this.username || 'token-auth'; // prevent missing args error
|
|
64
|
+
this.password = this.password || 'token-auth';
|
|
65
|
+
} else {
|
|
66
|
+
if (!this.username) {
|
|
67
|
+
throw new Error('username is required');
|
|
68
|
+
}
|
|
69
|
+
if (!this.password) {
|
|
70
|
+
throw new Error('password is required');
|
|
71
|
+
}
|
|
72
|
+
this.username = InputValidator.validateUsername(this.username);
|
|
73
|
+
this.password = InputValidator.validatePassword(this.password);
|
|
74
|
+
}
|
|
75
|
+
|
|
58
76
|
this.cookie = null;
|
|
59
77
|
this.options = options;
|
|
60
78
|
this.loginMutex = false; // Add mutex to prevent concurrent logins
|
|
@@ -81,15 +99,21 @@ class ThreeXUI {
|
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
// Create axios instance with security best practices
|
|
102
|
+
const secureHeaders = SecureHeaders.getSecureHeaders({
|
|
103
|
+
userAgent: options.userAgent || '3xui-api-client/2.0.0 (Security-Enhanced)',
|
|
104
|
+
enableCSP: options.enableCSP || false
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (this.token) {
|
|
108
|
+
secureHeaders['Authorization'] = `Bearer ${this.token}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
84
111
|
this.api = axios.create({
|
|
85
112
|
baseURL: this.baseURL,
|
|
86
113
|
timeout: options.timeout || 30000, // 30 second timeout
|
|
87
114
|
maxRedirects: 5,
|
|
88
115
|
validateStatus: (status) => status >= 200 && status < 300,
|
|
89
|
-
headers:
|
|
90
|
-
userAgent: options.userAgent || '3xui-api-client/2.0.0 (Security-Enhanced)',
|
|
91
|
-
enableCSP: options.enableCSP || false
|
|
92
|
-
})
|
|
116
|
+
headers: secureHeaders
|
|
93
117
|
});
|
|
94
118
|
|
|
95
119
|
// Add request interceptor for security headers
|
|
@@ -120,6 +144,14 @@ class ThreeXUI {
|
|
|
120
144
|
* @returns {Object} Login response
|
|
121
145
|
*/
|
|
122
146
|
async login(forceRefresh = false) {
|
|
147
|
+
// If API token is configured, skip cookie auth
|
|
148
|
+
if (this.token) {
|
|
149
|
+
return {
|
|
150
|
+
success: true,
|
|
151
|
+
message: 'Authenticated successfully using API Token'
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
123
155
|
// Check rate limiting first
|
|
124
156
|
const identifier = CredentialSecurity.hashForLogging(this.username);
|
|
125
157
|
if (!this.securityMonitor.checkRateLimit(identifier, 'login')) {
|
|
@@ -238,11 +270,13 @@ class ThreeXUI {
|
|
|
238
270
|
}
|
|
239
271
|
|
|
240
272
|
async _request(method, path, data = {}) {
|
|
241
|
-
// Check session validity first with mutex protection
|
|
242
|
-
if (!this.
|
|
243
|
-
await this.
|
|
244
|
-
|
|
245
|
-
|
|
273
|
+
// Check session validity first with mutex protection if token is not provided
|
|
274
|
+
if (!this.token) {
|
|
275
|
+
if (!this.loginMutex && this.sessionManager && !await this.sessionManager.hasValidSession(this.baseURL, this.username)) {
|
|
276
|
+
await this._ensureAuthenticated();
|
|
277
|
+
} else if (!this.loginMutex && !this.cookie) {
|
|
278
|
+
await this._ensureAuthenticated();
|
|
279
|
+
}
|
|
246
280
|
}
|
|
247
281
|
|
|
248
282
|
try {
|
|
@@ -257,6 +291,9 @@ class ThreeXUI {
|
|
|
257
291
|
return response.data;
|
|
258
292
|
} catch (error) {
|
|
259
293
|
if (error.response && error.response.status === 401) {
|
|
294
|
+
if (this.token) {
|
|
295
|
+
throw new Error('API Token is invalid or expired. Please check your credentials.');
|
|
296
|
+
}
|
|
260
297
|
// Cookie might have expired, try to login again with retry limit
|
|
261
298
|
if (this.loginRetryCount < this.maxLoginRetries) {
|
|
262
299
|
await this._ensureAuthenticated(true); // Force refresh
|
|
@@ -533,12 +570,322 @@ class ThreeXUI {
|
|
|
533
570
|
* @returns {boolean} Session validity
|
|
534
571
|
*/
|
|
535
572
|
async isSessionValid() {
|
|
573
|
+
if (this.token) {
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
536
576
|
if (this.sessionManager) {
|
|
537
577
|
return await this.sessionManager.hasValidSession(this.baseURL, this.username);
|
|
538
578
|
}
|
|
539
579
|
return !!this.cookie;
|
|
540
580
|
}
|
|
541
581
|
|
|
582
|
+
// ===========================================
|
|
583
|
+
// MODERN API METHODS (3X-UI >= 2.x)
|
|
584
|
+
// ===========================================
|
|
585
|
+
|
|
586
|
+
// --- Clients ---
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Get list of all clients
|
|
590
|
+
* @returns {Promise<Object>} Formatted list of all clients
|
|
591
|
+
*/
|
|
592
|
+
getClients() {
|
|
593
|
+
return this._request('get', '/panel/api/clients/list');
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Get paginated list of clients
|
|
598
|
+
* @param {Object} params - Pagination parameters
|
|
599
|
+
* @param {number} params.page - Page number (default: 1)
|
|
600
|
+
* @param {number} params.size - Items per page (default: 10)
|
|
601
|
+
* @param {string} params.sort - Sort field (e.g., 'email', 'expireTime')
|
|
602
|
+
* @param {string} params.order - Sort order ('asc' or 'desc')
|
|
603
|
+
* @param {string} params.email - Filter by email
|
|
604
|
+
* @returns {Promise<Object>} Paginated clients
|
|
605
|
+
*/
|
|
606
|
+
getPagedClients(params = {}) {
|
|
607
|
+
const queryParams = new URLSearchParams();
|
|
608
|
+
if (params.page !== undefined) {
|
|
609
|
+
queryParams.append('page', params.page);
|
|
610
|
+
}
|
|
611
|
+
if (params.size !== undefined) {
|
|
612
|
+
queryParams.append('size', params.size);
|
|
613
|
+
}
|
|
614
|
+
if (params.sort !== undefined) {
|
|
615
|
+
queryParams.append('sort', params.sort);
|
|
616
|
+
}
|
|
617
|
+
if (params.order !== undefined) {
|
|
618
|
+
queryParams.append('order', params.order);
|
|
619
|
+
}
|
|
620
|
+
if (params.email !== undefined) {
|
|
621
|
+
queryParams.append('email', params.email);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const queryString = queryParams.toString();
|
|
625
|
+
const url = queryString ? `/panel/api/clients/list/paged?${queryString}` : '/panel/api/clients/list/paged';
|
|
626
|
+
|
|
627
|
+
return this._request('get', url);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Get client by email
|
|
632
|
+
* @param {string} email - Exact client email address
|
|
633
|
+
* @returns {Promise<Object>} Client metadata
|
|
634
|
+
*/
|
|
635
|
+
getClient(email) {
|
|
636
|
+
return this._request('get', `/panel/api/clients/get/${encodeURIComponent(email)}`);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Get client traffic by email
|
|
641
|
+
* @param {string} email - Exact client email
|
|
642
|
+
* @returns {Promise<Object>} Client traffic details
|
|
643
|
+
*/
|
|
644
|
+
getClientTraffic(email) {
|
|
645
|
+
return this._request('get', `/panel/api/clients/traffic/${encodeURIComponent(email)}`);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Get subscription links for a client by subscription ID
|
|
650
|
+
* @param {string} subId - Subscription ID (UUID)
|
|
651
|
+
* @returns {Promise<Object>} Subscription details and links
|
|
652
|
+
*/
|
|
653
|
+
getSubLinks(subId) {
|
|
654
|
+
return this._request('get', `/panel/api/clients/subLinks/${encodeURIComponent(subId)}`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Get generic client links by email
|
|
659
|
+
* @param {string} email - Exact client email
|
|
660
|
+
* @returns {Promise<Object>} Link strings
|
|
661
|
+
*/
|
|
662
|
+
getClientLinks(email) {
|
|
663
|
+
return this._request('get', `/panel/api/clients/links/${encodeURIComponent(email)}`);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Add a new client via Modern API
|
|
668
|
+
* @param {Object} data - Client payload
|
|
669
|
+
* @returns {Promise<Object>} Addition response
|
|
670
|
+
*/
|
|
671
|
+
addModernClient(data) {
|
|
672
|
+
return this._request('post', '/panel/api/clients/add', data);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Update client by email via Modern API
|
|
677
|
+
* @param {string} email - Exact client email
|
|
678
|
+
* @param {Object} data - Update payload
|
|
679
|
+
* @returns {Promise<Object>} Update response
|
|
680
|
+
*/
|
|
681
|
+
updateModernClient(email, data) {
|
|
682
|
+
return this._request('post', `/panel/api/clients/update/${encodeURIComponent(email)}`, data);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Delete client by email via Modern API
|
|
687
|
+
* @param {string} email - Exact client email
|
|
688
|
+
* @returns {Promise<Object>} Delete response
|
|
689
|
+
*/
|
|
690
|
+
deleteModernClient(email) {
|
|
691
|
+
return this._request('post', `/panel/api/clients/del/${encodeURIComponent(email)}`);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
attachClientToInbounds(email, data) {
|
|
695
|
+
return this._request('post', `/panel/api/clients/${encodeURIComponent(email)}/attach`, data);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
detachClientFromInbounds(email, data) {
|
|
699
|
+
return this._request('post', `/panel/api/clients/${encodeURIComponent(email)}/detach`, data);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
resetAllModernClientTraffics() {
|
|
703
|
+
return this._request('post', '/panel/api/clients/resetAllTraffics');
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
deleteDepletedModernClients() {
|
|
707
|
+
return this._request('post', '/panel/api/clients/delDepleted');
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
bulkAdjustModernClients(data) {
|
|
711
|
+
return this._request('post', '/panel/api/clients/bulkAdjust', data);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
bulkDeleteModernClients(data) {
|
|
715
|
+
return this._request('post', '/panel/api/clients/bulkDel', data);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
bulkCreateModernClients(data) {
|
|
719
|
+
return this._request('post', '/panel/api/clients/bulkCreate', data);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
bulkAttachModernClients(data) {
|
|
723
|
+
return this._request('post', '/panel/api/clients/bulkAttach', data);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
bulkDetachModernClients(data) {
|
|
727
|
+
return this._request('post', '/panel/api/clients/bulkDetach', data);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
bulkResetTrafficModernClients(data) {
|
|
731
|
+
return this._request('post', '/panel/api/clients/bulkResetTraffic', data);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
resetModernClientTrafficByEmail(email) {
|
|
735
|
+
return this._request('post', `/panel/api/clients/resetTraffic/${encodeURIComponent(email)}`);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
updateModernClientTrafficByEmail(email, data) {
|
|
739
|
+
return this._request('post', `/panel/api/clients/updateTraffic/${encodeURIComponent(email)}`, data);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
getModernClientIps(email) {
|
|
743
|
+
return this._request('post', `/panel/api/clients/ips/${encodeURIComponent(email)}`);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
clearModernClientIps(email) {
|
|
747
|
+
return this._request('post', `/panel/api/clients/clearIps/${encodeURIComponent(email)}`);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
getOnlines() {
|
|
751
|
+
return this._request('post', '/panel/api/clients/onlines');
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
getModernLastOnline() {
|
|
755
|
+
return this._request('post', '/panel/api/clients/lastOnline');
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// --- Client Groups ---
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Get list of all client groups
|
|
762
|
+
* @returns {Promise<Object>} List of client groups
|
|
763
|
+
*/
|
|
764
|
+
getGroups() {
|
|
765
|
+
return this._request('get', '/panel/api/clients/groups');
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Get list of emails belonging to a specific group
|
|
770
|
+
* @param {string} groupName - The name of the group
|
|
771
|
+
* @returns {Promise<Object>} List of emails in the group
|
|
772
|
+
*/
|
|
773
|
+
getGroupEmails(groupName) {
|
|
774
|
+
return this._request('get', `/panel/api/clients/groups/${encodeURIComponent(groupName)}/emails`);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
createGroup(data) {
|
|
778
|
+
return this._request('post', '/panel/api/clients/groups/create', data);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
renameGroup(data) {
|
|
782
|
+
return this._request('post', '/panel/api/clients/groups/rename', data);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
deleteGroup(data) {
|
|
786
|
+
return this._request('post', '/panel/api/clients/groups/delete', data);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
bulkAddGroups(data) {
|
|
790
|
+
return this._request('post', '/panel/api/clients/groups/bulkAdd', data);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
bulkRemoveGroups(data) {
|
|
794
|
+
return this._request('post', '/panel/api/clients/groups/bulkRemove', data);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// --- Nodes ---
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Get list of all nodes
|
|
801
|
+
* @returns {Promise<Object>} List of nodes
|
|
802
|
+
*/
|
|
803
|
+
getNodes() {
|
|
804
|
+
return this._request('get', '/panel/api/nodes/list');
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Get specific node by ID
|
|
809
|
+
* @param {number|string} id - Node ID
|
|
810
|
+
* @returns {Promise<Object>} Node details
|
|
811
|
+
*/
|
|
812
|
+
getNode(id) {
|
|
813
|
+
return this._request('get', `/panel/api/nodes/get/${encodeURIComponent(id)}`);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Get history metrics for a node
|
|
818
|
+
* @param {number|string} id - Node ID
|
|
819
|
+
* @param {string} metric - Metric name (e.g., 'cpu', 'memory')
|
|
820
|
+
* @param {string} bucket - Time bucket size
|
|
821
|
+
* @returns {Promise<Object>} Node history data
|
|
822
|
+
*/
|
|
823
|
+
getNodeHistory(id, metric, bucket) {
|
|
824
|
+
return this._request('get', `/panel/api/nodes/history/${encodeURIComponent(id)}/${encodeURIComponent(metric)}/${encodeURIComponent(bucket)}`);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
addNode(data) {
|
|
828
|
+
return this._request('post', '/panel/api/nodes/add', data);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
updateNode(id, data) {
|
|
832
|
+
return this._request('post', `/panel/api/nodes/update/${encodeURIComponent(id)}`, data);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
deleteNode(id) {
|
|
836
|
+
return this._request('post', `/panel/api/nodes/del/${encodeURIComponent(id)}`);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
setNodeEnable(id) {
|
|
840
|
+
return this._request('post', `/panel/api/nodes/setEnable/${encodeURIComponent(id)}`);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
testNode(data) {
|
|
844
|
+
return this._request('post', '/panel/api/nodes/test', data);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
probeNode(id) {
|
|
848
|
+
return this._request('post', `/panel/api/nodes/probe/${encodeURIComponent(id)}`);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// --- Custom Geo ---
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Get list of custom geo sites/ips
|
|
855
|
+
* @returns {Promise<Object>} List of custom geos
|
|
856
|
+
*/
|
|
857
|
+
getCustomGeos() {
|
|
858
|
+
return this._request('get', '/panel/api/custom-geo/list');
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Get aliases for custom geos
|
|
863
|
+
* @returns {Promise<Object>} Custom geo aliases
|
|
864
|
+
*/
|
|
865
|
+
getGeoAliases() {
|
|
866
|
+
return this._request('get', '/panel/api/custom-geo/aliases');
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
addCustomGeo(data) {
|
|
870
|
+
return this._request('post', '/panel/api/custom-geo/add', data);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
updateCustomGeo(id, data) {
|
|
874
|
+
return this._request('post', `/panel/api/custom-geo/update/${encodeURIComponent(id)}`, data);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
deleteCustomGeo(id) {
|
|
878
|
+
return this._request('post', `/panel/api/custom-geo/delete/${encodeURIComponent(id)}`);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
downloadCustomGeo(id) {
|
|
882
|
+
return this._request('post', `/panel/api/custom-geo/download/${encodeURIComponent(id)}`);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
updateAllCustomGeo() {
|
|
886
|
+
return this._request('post', '/panel/api/custom-geo/update-all');
|
|
887
|
+
}
|
|
888
|
+
|
|
542
889
|
// ===========================================
|
|
543
890
|
// ORIGINAL API METHODS (UNCHANGED)
|
|
544
891
|
// ===========================================
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "3xui-api-client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "A Node.js client library for 3x-ui panel API with built-in credential generation, session management, and web integration support",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|