x3ui-api 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # x3ui-api
2
2
 
3
- A Node.js client for interacting with the x3ui panel API.
3
+ A Node.js client for interacting with the x3ui panel API. This library provides a simple interface to manage X-UI inbounds, clients, and system statistics.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,6 +8,163 @@ A Node.js client for interacting with the x3ui panel API.
8
8
  npm install x3ui-api
9
9
  ```
10
10
 
11
+ ## Features
12
+
13
+ - Authentication with x3ui panel
14
+ - Get system statistics (CPU, memory, network, etc.)
15
+ - Manage inbounds (list, add, update, delete)
16
+ - Convenient Reality inbound builder
17
+ - Client management with fluent API
18
+ - TypeScript support
19
+
20
+ ## Usage
21
+
22
+ ### Basic Usage
23
+
24
+ ```javascript
25
+ const X3UIClient = require('x3ui-api');
26
+
27
+ // Initialize client
28
+ const client = new X3UIClient({
29
+ baseURL: 'http://your-x3ui-panel.com:54321',
30
+ parseJSONSettings: true // Default: true - automatically parse JSON settings
31
+ });
32
+
33
+ // Login to panel
34
+ async function main() {
35
+ try {
36
+ const loginResult = await client.login('admin', 'password');
37
+ console.log('Login successful:', loginResult.success);
38
+
39
+ // Get system stats
40
+ const stats = await client.getSystemStats();
41
+ console.log('CPU Usage:', stats.cpu + '%');
42
+ console.log('Memory:', Math.round(stats.mem.current / stats.mem.total * 100) + '%');
43
+
44
+ // List all inbounds
45
+ const inbounds = await client.getInbounds();
46
+ console.log('Inbounds:', inbounds.length);
47
+
48
+ // First inbound details
49
+ if (inbounds.length > 0) {
50
+ console.log('First inbound:', inbounds[0].remark);
51
+ console.log('Protocol:', inbounds[0].protocol);
52
+ console.log('Port:', inbounds[0].port);
53
+ }
54
+ } catch (error) {
55
+ console.error('Error:', error.message);
56
+ }
57
+ }
58
+
59
+ main();
60
+ ```
61
+
62
+ ### Creating a Reality Inbound
63
+
64
+ The library provides a convenient builder pattern for creating Reality inbounds:
65
+
66
+ ```javascript
67
+ async function createRealityInbound() {
68
+ try {
69
+ // Create a Reality inbound builder
70
+ const builder = client.createRealityBuilder({
71
+ remark: 'My Reality Inbound',
72
+ port: 8443 // Optional, will auto-generate if not provided
73
+ });
74
+
75
+ // Configure Reality settings
76
+ builder
77
+ .setDest('yahoo.com:443')
78
+ .setServerNames(['yahoo.com', 'www.yahoo.com'])
79
+ .setFingerprint('chrome');
80
+
81
+ // Add a client
82
+ const clientBuilder = builder.addClient()
83
+ .setEmail('user@example.com')
84
+ .setTotalGB(100) // 100 GB traffic limit
85
+ .setExpiryTime(Date.now() + 30 * 24 * 60 * 60 * 1000); // 30 days
86
+
87
+ // Build and add the inbound
88
+ const inbound = await builder.build();
89
+ const result = await client.addInbound(inbound);
90
+
91
+ console.log('Inbound created:', result);
92
+
93
+ // Get connection link for the client
94
+ const link = builder.getClientLink(0, 'your-server-ip.com');
95
+ console.log('Client connection link:', link);
96
+ } catch (error) {
97
+ console.error('Error creating inbound:', error.message);
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### Managing Existing Inbounds
103
+
104
+ ```javascript
105
+ async function manageInbounds() {
106
+ // Get all inbounds
107
+ const inbounds = await client.getInbounds();
108
+
109
+ if (inbounds.length > 0) {
110
+ const inboundId = inbounds[0].id;
111
+
112
+ // Update an inbound
113
+ await client.updateInbound(inboundId, {
114
+ remark: 'Updated Inbound Name',
115
+ enable: true
116
+ });
117
+
118
+ // Delete an inbound
119
+ await client.deleteInbound(inboundId);
120
+ }
121
+ }
122
+ ```
123
+
124
+ ## API Reference
125
+
126
+ ### X3UIClient
127
+
128
+ #### Constructor
129
+
130
+ ```javascript
131
+ new X3UIClient({
132
+ baseURL: string, // Required: URL to your x3ui panel
133
+ token?: string, // Optional: Authentication token (if already logged in)
134
+ parseJSONSettings?: boolean // Optional: Auto-parse JSON settings (default: true)
135
+ })
136
+ ```
137
+
138
+ #### Methods
139
+
140
+ - `login(username: string, password: string)` - Authenticate with the panel
141
+ - `getSystemStats()` - Get system statistics
142
+ - `getInbounds()` - Get all configured inbounds
143
+ - `addInbound(config)` - Add a new inbound
144
+ - `updateInbound(id, config)` - Update an existing inbound
145
+ - `deleteInbound(id)` - Delete an inbound
146
+ - `getNewX25519Cert()` - Generate a new X25519 certificate
147
+ - `createRealityBuilder(options)` - Create a Reality inbound builder
148
+
149
+ ### RealityBuilder
150
+
151
+ Builder class for creating Reality inbounds with a fluent API.
152
+
153
+ #### Methods
154
+
155
+ - `setPort(port)` - Set the port for the inbound
156
+ - `setRemark(remark)` - Set the name/remark for the inbound
157
+ - `setDest(dest)` - Set the destination address (e.g., "yahoo.com:443")
158
+ - `setServerNames(names)` - Set server names for SNI
159
+ - `setKeyPair(privateKey, publicKey)` - Set Reality keypair
160
+ - `setShortIds(ids)` - Set short IDs for Reality
161
+ - `setFingerprint(fingerprint)` - Set browser fingerprint
162
+ - `setListenIP(ip)` - Set listen IP address
163
+ - `setExpiryTime(timestamp)` - Set inbound expiry time
164
+ - `addClient(options)` - Add a new client to the inbound
165
+ - `getClientLink(clientIndex, host)` - Get connection link for a client
166
+ - `build()` - Build the final inbound config
167
+
11
168
  ## License
12
169
 
13
170
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x3ui-api",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "API client for x3ui panel",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  "author": "",
12
12
  "license": "MIT",
13
13
  "dependencies": {
14
- "axios": "^1.6.5",
14
+ "axios": "^1.8.2",
15
15
  "form-data": "^4.0.0"
16
16
  },
17
17
  "devDependencies": {
package/src/index.d.ts CHANGED
@@ -201,9 +201,17 @@ export interface ClientBuilder {
201
201
  setTgId(id: string): this;
202
202
 
203
203
  /**
204
- * Build client configuration
204
+ * Build the client configuration
205
205
  */
206
- build(): Promise<ClientSettings>;
206
+ build(): ClientSettings;
207
+
208
+ /**
209
+ * Generate connection link for this client
210
+ * @param host Host address
211
+ * @param port Optional port number, defaults to parent's port
212
+ * @param protocol Optional protocol, defaults to parent's protocol or 'vless'
213
+ */
214
+ getLink(host: string, port?: number, protocol?: string): string;
207
215
  }
208
216
 
209
217
  export interface RealityBuilder {
@@ -257,6 +265,13 @@ export interface RealityBuilder {
257
265
  */
258
266
  addClient(options?: Partial<ClientSettings>): ClientBuilder;
259
267
 
268
+ /**
269
+ * Get connection link for a client
270
+ * @param clientIndex Index of the client (defaults to 0)
271
+ * @param host Optional host address (defaults to listenIP or 'localhost')
272
+ */
273
+ getClientLink(clientIndex?: number, host?: string): string;
274
+
260
275
  /**
261
276
  * Build the final inbound config
262
277
  */
package/src/index.js CHANGED
@@ -72,9 +72,9 @@ module.exports = class X3UIClient {
72
72
 
73
73
  parseInbound(inbound) {
74
74
  if (this.parseJSONSettings) {
75
- inbound.settings = JSON.parse(inbound.settings);
76
- inbound.streamSettings = JSON.parse(inbound.streamSettings);
77
- inbound.sniffing = JSON.parse(inbound.sniffing);
75
+ inbound.settings = inbound.settings && inbound.settings.length > 0 ? JSON.parse(inbound.settings) : {};
76
+ inbound.streamSettings = inbound.streamSettings && inbound.streamSettings.length > 0 ? JSON.parse(inbound.streamSettings) : {};
77
+ inbound.sniffing = inbound.sniffing && inbound.sniffing.length > 0 ? JSON.parse(inbound.sniffing) : {};
78
78
  inbound.allocate = inbound.allocate && inbound.allocate.length > 0 ? JSON.parse(inbound.allocate) : {};
79
79
  }
80
80
  return inbound;
@@ -196,7 +196,7 @@ class ClientBuilder {
196
196
  return this;
197
197
  }
198
198
 
199
- async build() {
199
+ build() {
200
200
  return {
201
201
  id: this.id || crypto.randomUUID(),
202
202
  flow: this.flow,
@@ -210,6 +210,28 @@ class ClientBuilder {
210
210
  reset: this.reset
211
211
  };
212
212
  }
213
+
214
+ getLink(host, port, protocol) {
215
+ const id = this.id || crypto.randomUUID();
216
+ const settings = this.parent.streamSettings?.realitySettings;
217
+ if (!settings) throw new Error('Reality settings not found');
218
+
219
+ port = port || this.parent.port;
220
+ protocol = protocol || this.parent.protocol || 'vless';
221
+
222
+ const params = new URLSearchParams({
223
+ security: this.parent.streamSettings?.security || 'reality',
224
+ sni: settings.serverNames[0],
225
+ fp: settings.settings.fingerprint,
226
+ pbk: settings.settings.publicKey,
227
+ sid: settings.shortIds[0],
228
+ spx: settings.settings.spiderX || '/',
229
+ type: this.parent.streamSettings?.network || 'tcp',
230
+ encryption: this.parent.settings?.decryption || 'none'
231
+ });
232
+
233
+ return `${protocol}://${id}@${host}:${port}?${params.toString()}#${encodeURIComponent(this.email || 'default')}`;
234
+ }
213
235
  }
214
236
 
215
237
  class RealityBuilder {
@@ -303,10 +325,29 @@ class RealityBuilder {
303
325
  if (options.totalGB) builder.setTotalGB(options.totalGB);
304
326
  if (options.expiryTime) builder.setExpiryTime(options.expiryTime);
305
327
  if (options.tgId) builder.setTgId(options.tgId);
328
+ builder.parent.streamSettings = {
329
+ realitySettings: {
330
+ serverNames: this.serverNames,
331
+ settings: {
332
+ fingerprint: this.fingerprint,
333
+ publicKey: this.publicKey,
334
+ spiderX: '/'
335
+ },
336
+ shortIds: this.shortIds
337
+ }
338
+ };
306
339
  this.clients.push(builder);
307
340
  return builder;
308
341
  }
309
342
 
343
+ getClientLink(clientIndex = 0, host) {
344
+ if (clientIndex < 0 || clientIndex >= this.clients.length) {
345
+ throw new Error('Invalid client index');
346
+ }
347
+ const client = this.clients[clientIndex];
348
+ return client.getLink(host || this.listenIP || 'localhost', this.port);
349
+ }
350
+
310
351
  generateRandomPort() {
311
352
  return Math.floor(Math.random() * (65535 - 1024) + 1024);
312
353
  }