x3ui-api 1.0.6-3 → 1.0.8-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/README.md +1 -3
- package/package.json +1 -1
- package/src/core/X3UIClient.js +13 -19
- package/src/index.d.ts +0 -1
- package/src/protocols/vless/ClearBuilder.d.ts +51 -0
- package/src/protocols/vless/ClearBuilder.js +150 -0
- package/src/protocols/vless/ClearClientBuilder.d.ts +11 -0
- package/src/protocols/vless/ClearClientBuilder.js +20 -0
- package/src/protocols/vless/index.d.ts +2 -0
- package/src/protocols/vless/index.js +3 -1
package/README.md
CHANGED
|
@@ -29,8 +29,7 @@ const X3UIClient = require('x3ui-api');
|
|
|
29
29
|
|
|
30
30
|
// Initialize client
|
|
31
31
|
const client = new X3UIClient({
|
|
32
|
-
baseURL: 'http://your-x3ui-panel.com:54321'
|
|
33
|
-
parseJSONSettings: true // Default: true - automatically parse JSON settings
|
|
32
|
+
baseURL: 'http://your-x3ui-panel.com:54321'
|
|
34
33
|
});
|
|
35
34
|
|
|
36
35
|
// Login to panel
|
|
@@ -315,7 +314,6 @@ async function manageInbounds() {
|
|
|
315
314
|
new X3UIClient({
|
|
316
315
|
baseURL: string, // Required: URL to your x3ui panel
|
|
317
316
|
token?: string, // Optional: Authentication token (if already logged in)
|
|
318
|
-
parseJSONSettings?: boolean // Optional: Auto-parse JSON settings (default: true)
|
|
319
317
|
})
|
|
320
318
|
```
|
|
321
319
|
|
package/package.json
CHANGED
package/src/core/X3UIClient.js
CHANGED
|
@@ -7,7 +7,6 @@ module.exports = class X3UIClient {
|
|
|
7
7
|
|
|
8
8
|
constructor(config) {
|
|
9
9
|
this.config = config;
|
|
10
|
-
this.config.parseJSONSettings ??= true;
|
|
11
10
|
this.client = axios.create({
|
|
12
11
|
baseURL: config.baseURL,
|
|
13
12
|
headers: config.token ? {
|
|
@@ -44,7 +43,7 @@ module.exports = class X3UIClient {
|
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
async getSystemStats() {
|
|
47
|
-
if(!this.isAuthed) {
|
|
46
|
+
if (!this.isAuthed) {
|
|
48
47
|
await this.login();
|
|
49
48
|
}
|
|
50
49
|
const response = await this.client.post('/server/status');
|
|
@@ -70,24 +69,19 @@ module.exports = class X3UIClient {
|
|
|
70
69
|
* }>>}
|
|
71
70
|
*/
|
|
72
71
|
async getInbounds() {
|
|
73
|
-
if(!this.isAuthed) {
|
|
72
|
+
if (!this.isAuthed) {
|
|
74
73
|
await this.login();
|
|
75
74
|
}
|
|
76
75
|
const response = await this.client.get('/panel/api/inbounds/list');
|
|
77
76
|
let inbounds = response.data.obj;
|
|
78
|
-
|
|
79
|
-
inbounds = inbounds.map(inbound => this.parseInbound(inbound));
|
|
80
|
-
}
|
|
81
|
-
return inbounds;
|
|
77
|
+
return inbounds.map(inbound => this.parseInbound(inbound));
|
|
82
78
|
}
|
|
83
79
|
|
|
84
80
|
parseInbound(inbound) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
inbound.allocate = inbound.allocate && inbound.allocate.length > 0 ? JSON.parse(inbound.allocate) : {};
|
|
90
|
-
}
|
|
81
|
+
inbound.settings = inbound.settings && inbound.settings.length > 0 ? JSON.parse(inbound.settings) : {};
|
|
82
|
+
inbound.streamSettings = inbound.streamSettings && inbound.streamSettings.length > 0 ? JSON.parse(inbound.streamSettings) : {};
|
|
83
|
+
inbound.sniffing = inbound.sniffing && inbound.sniffing.length > 0 ? JSON.parse(inbound.sniffing) : {};
|
|
84
|
+
inbound.allocate = inbound.allocate && inbound.allocate.length > 0 ? JSON.parse(inbound.allocate) : {};
|
|
91
85
|
return inbound;
|
|
92
86
|
}
|
|
93
87
|
|
|
@@ -118,12 +112,12 @@ module.exports = class X3UIClient {
|
|
|
118
112
|
* @returns {Promise<void>}
|
|
119
113
|
*/
|
|
120
114
|
async addInbound(config) {
|
|
121
|
-
if(!this.isAuthed) {
|
|
115
|
+
if (!this.isAuthed) {
|
|
122
116
|
await this.login();
|
|
123
117
|
}
|
|
124
118
|
config = this.stringifyInbound(config);
|
|
125
119
|
const response = await this.client.post('/panel/api/inbounds/add', config);
|
|
126
|
-
if(!response.data.success)
|
|
120
|
+
if (!response.data.success)
|
|
127
121
|
throw new Error(response.data.msg);
|
|
128
122
|
return this.parseInbound(response.data.obj);
|
|
129
123
|
}
|
|
@@ -135,12 +129,12 @@ module.exports = class X3UIClient {
|
|
|
135
129
|
* @returns {Promise<void>}
|
|
136
130
|
*/
|
|
137
131
|
async updateInbound(id, config) {
|
|
138
|
-
if(!this.isAuthed) {
|
|
132
|
+
if (!this.isAuthed) {
|
|
139
133
|
await this.login();
|
|
140
134
|
}
|
|
141
135
|
config = this.stringifyInbound(config);
|
|
142
136
|
const response = await this.client.post(`/panel/api/inbounds/update/${id}`, config);
|
|
143
|
-
if(!response.data.success)
|
|
137
|
+
if (!response.data.success)
|
|
144
138
|
throw new Error(response.data.msg);
|
|
145
139
|
return this.parseInbound(response.data.obj);
|
|
146
140
|
}
|
|
@@ -151,7 +145,7 @@ module.exports = class X3UIClient {
|
|
|
151
145
|
* @returns {Promise<void>}
|
|
152
146
|
*/
|
|
153
147
|
async deleteInbound(id) {
|
|
154
|
-
if(!this.isAuthed) {
|
|
148
|
+
if (!this.isAuthed) {
|
|
155
149
|
await this.login();
|
|
156
150
|
}
|
|
157
151
|
await this.client.post(`/panel/api/inbounds/del/${id}`);
|
|
@@ -162,7 +156,7 @@ module.exports = class X3UIClient {
|
|
|
162
156
|
* @returns {Promise<{privateKey: string, publicKey: string}>} New X25519 key pair
|
|
163
157
|
*/
|
|
164
158
|
async getNewX25519Cert() {
|
|
165
|
-
if(!this.isAuthed) {
|
|
159
|
+
if (!this.isAuthed) {
|
|
166
160
|
await this.login();
|
|
167
161
|
}
|
|
168
162
|
const response = await this.client.post('/server/getNewX25519Cert');
|
package/src/index.d.ts
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import X3UIClient, {ClientSettings, InboundConfig, DeepPartial} from "../../index";
|
|
2
|
+
import RealityClientBuilder from "./RealityClientBuilder";
|
|
3
|
+
|
|
4
|
+
export default class ClearBuilder {
|
|
5
|
+
|
|
6
|
+
constructor(client: X3UIClient, inbound?: DeepPartial<InboundConfig>);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Set the port for the inbound. If not provided, will auto-generate unused port
|
|
10
|
+
*/
|
|
11
|
+
setPort(port: number): this;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Set the remark/name for the inbound
|
|
15
|
+
*/
|
|
16
|
+
setRemark(remark: string): this;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Set listen IP address. Defaults to empty string
|
|
20
|
+
*/
|
|
21
|
+
setListenIP(ip: string): this;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Set inbound expiry time. Defaults to 0 (no expiry)
|
|
25
|
+
*/
|
|
26
|
+
setExpiryTime(timestamp: number): this;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Add a new client to the inbound
|
|
30
|
+
*/
|
|
31
|
+
addClient(options?: DeepPartial<ClientSettings>): RealityClientBuilder;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get connection link for a client
|
|
35
|
+
* @param clientIndex Index of the client (defaults to 0)
|
|
36
|
+
* @param host Optional host address (defaults to listenIP or 'localhost')
|
|
37
|
+
*/
|
|
38
|
+
getClientLinkByIndex(clientIndex?: number, host?: string): string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get connection link for a client
|
|
42
|
+
* @param email Email of the client
|
|
43
|
+
* @param host Optional host address (defaults to listenIP or 'localhost')
|
|
44
|
+
*/
|
|
45
|
+
getClientLinkByEmail(email: string, host?: string): string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Build the final inbound config
|
|
49
|
+
*/
|
|
50
|
+
build(): Promise<InboundConfig>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
const ClearClientBuilder = require("./ClearClientBuilder");
|
|
2
|
+
|
|
3
|
+
module.exports = class ClearBuilder {
|
|
4
|
+
constructor(client, options = {}) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
// Initialize from InboundConfig
|
|
7
|
+
this.id = options.id || undefined;
|
|
8
|
+
this.port = options.port || 0;
|
|
9
|
+
this.remark = options.remark || '';
|
|
10
|
+
this.listenIP = options.listen || '';
|
|
11
|
+
this.expiryTime = options.expiryTime || 0;
|
|
12
|
+
this.enable = true;
|
|
13
|
+
|
|
14
|
+
// Initialize from StreamSettings and RealitySettings
|
|
15
|
+
const streamSettings = typeof options.streamSettings === 'string'
|
|
16
|
+
? JSON.parse(options.streamSettings)
|
|
17
|
+
: options.streamSettings || {};
|
|
18
|
+
|
|
19
|
+
// Initialize clients
|
|
20
|
+
this.clients = [];
|
|
21
|
+
const settings = typeof options.settings === 'string'
|
|
22
|
+
? JSON.parse(options.settings)
|
|
23
|
+
: options.settings;
|
|
24
|
+
|
|
25
|
+
if (settings?.clients) {
|
|
26
|
+
settings.clients.forEach(client => {
|
|
27
|
+
this.addClient(client);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setPort(port) {
|
|
33
|
+
this.port = port;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
setRemark(remark) {
|
|
38
|
+
this.remark = remark;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
setListenIP(ip) {
|
|
43
|
+
this.listenIP = ip;
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setExpiryTime(timestamp) {
|
|
48
|
+
this.expiryTime = timestamp;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
addClient(options = {}) {
|
|
53
|
+
const builder = new ClearClientBuilder(this);
|
|
54
|
+
if (options.id) builder.setId(options.id);
|
|
55
|
+
if (options.email) builder.setEmail(options.email);
|
|
56
|
+
if (options.totalGB) builder.setTotalGB(options.totalGB);
|
|
57
|
+
if (options.expiryTime) builder.setExpiryTime(options.expiryTime);
|
|
58
|
+
if (options.tgId) builder.setTgId(options.tgId);
|
|
59
|
+
this.clients.push(builder);
|
|
60
|
+
return builder;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getClientLinkByIndex(clientIndex = 0, host) {
|
|
64
|
+
if (clientIndex < 0 || clientIndex >= this.clients.length) {
|
|
65
|
+
throw new Error('Invalid client index');
|
|
66
|
+
}
|
|
67
|
+
const client = this.clients[clientIndex];
|
|
68
|
+
return client.getLink(host || this.listenIP || 'localhost', this.port);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getClientLinkByEmail(email, host) {
|
|
72
|
+
const client = this.clients.find(client => client.email === email);
|
|
73
|
+
if (!client) {
|
|
74
|
+
throw new Error('Client not found');
|
|
75
|
+
}
|
|
76
|
+
return client.getLink(host || this.listenIP || 'localhost', this.port);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
generateRandomPort() {
|
|
80
|
+
return Math.floor(Math.random() * (65535 - 1024) + 1024);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async build() {
|
|
84
|
+
if (!this.remark) {
|
|
85
|
+
throw new Error('Remark is required');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// If no port specified, find unused one
|
|
89
|
+
if (!this.port) {
|
|
90
|
+
const inbounds = await this.client.getInbounds();
|
|
91
|
+
const usedPorts = new Set(inbounds.map(i => i.port));
|
|
92
|
+
let port;
|
|
93
|
+
do {
|
|
94
|
+
port = this.generateRandomPort();
|
|
95
|
+
} while (usedPorts.has(port));
|
|
96
|
+
this.port = port;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// If no clients added, create one default client
|
|
100
|
+
if (this.clients.length === 0) {
|
|
101
|
+
this.addClient();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Build all clients
|
|
105
|
+
const clientConfigs = await Promise.all(this.clients.map(builder => builder.build()));
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
id: this.id,
|
|
109
|
+
up: 0,
|
|
110
|
+
down: 0,
|
|
111
|
+
total: 0,
|
|
112
|
+
remark: this.remark,
|
|
113
|
+
enable: true,
|
|
114
|
+
expiryTime: this.expiryTime,
|
|
115
|
+
listen: this.listenIP,
|
|
116
|
+
port: this.port,
|
|
117
|
+
protocol: 'vless',
|
|
118
|
+
settings: {
|
|
119
|
+
clients: clientConfigs,
|
|
120
|
+
decryption: 'none',
|
|
121
|
+
fallbacks: []
|
|
122
|
+
},
|
|
123
|
+
streamSettings: {
|
|
124
|
+
network: 'tcp',
|
|
125
|
+
security: 'none',
|
|
126
|
+
externalProxy: [],
|
|
127
|
+
tcpSettings: {
|
|
128
|
+
acceptProxyProtocol: false,
|
|
129
|
+
header: {
|
|
130
|
+
type: 'none'
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
tag: `inbound-${this.port}`,
|
|
135
|
+
sniffing: {
|
|
136
|
+
enabled: false,
|
|
137
|
+
destOverride: ['http', 'tls', 'quic', 'fakedns'],
|
|
138
|
+
metadataOnly: false,
|
|
139
|
+
routeOnly: false
|
|
140
|
+
},
|
|
141
|
+
allocate: {
|
|
142
|
+
strategy: 'always',
|
|
143
|
+
refresh: 5,
|
|
144
|
+
concurrency: 3
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports.default = module.exports;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import ClientBuilder from "../../core/ClientBuilder";
|
|
2
|
+
|
|
3
|
+
export default class ClearClientBuilder extends ClientBuilder {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate connection link for this client
|
|
7
|
+
* @param host Host address
|
|
8
|
+
* @param port Optional port number, defaults to parent's port
|
|
9
|
+
*/
|
|
10
|
+
getLink(host: string, port?: number): string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const ClientBuilder = require('../../core/ClientBuilder');
|
|
2
|
+
|
|
3
|
+
module.exports = class ClearClientBuilder extends ClientBuilder {
|
|
4
|
+
getLink(host, port) {
|
|
5
|
+
const id = this.id || crypto.randomUUID();
|
|
6
|
+
|
|
7
|
+
port = port || this.parent.port;
|
|
8
|
+
let protocol = this.parent.protocol || 'vless';
|
|
9
|
+
|
|
10
|
+
const params = new URLSearchParams({
|
|
11
|
+
security: this.parent.streamSettings?.security || 'none',
|
|
12
|
+
type: this.parent.streamSettings?.network || 'tcp',
|
|
13
|
+
encryption: this.parent.settings?.decryption || 'none'
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return `${protocol}://${id}@${host}:${port}?${params.toString()}#${encodeURIComponent(this.email || 'default')}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports.default = module.exports;
|
|
@@ -4,6 +4,8 @@ import RealityClientBuilder from "./RealityClientBuilder";
|
|
|
4
4
|
declare const _default: {
|
|
5
5
|
RealityBuilder: typeof RealityBuilder;
|
|
6
6
|
RealityClientBuilder: typeof RealityClientBuilder;
|
|
7
|
+
ClearBuilder: typeof RealityBuilder;
|
|
8
|
+
ClearClientBuilder: typeof RealityClientBuilder;
|
|
7
9
|
};
|
|
8
10
|
|
|
9
11
|
export default _default;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
RealityBuilder: require('./RealityBuilder'),
|
|
3
|
-
RealityClientBuilder: require('./RealityClientBuilder')
|
|
3
|
+
RealityClientBuilder: require('./RealityClientBuilder'),
|
|
4
|
+
ClearBuilder: require('./ClearBuilder'),
|
|
5
|
+
ClearClientBuilder: require('./ClearClientBuilder')
|
|
4
6
|
}
|
|
5
7
|
|
|
6
8
|
module.exports.default = module.exports
|