UNPKG

@maniwrld/marzjs

Version:

A powerful and easy-to-use Node.js client for interacting with Marzban's VPN management API. It supports popular protocols like Xray, V2Ray, and more.

142 lines (128 loc) 5.49 kB
const axios = require('axios'); const qs = require('qs'); class MarzbanAPI { /** * Initializes the MarzbanAPI client with the provided configuration. * @param {Object} config - Configuration object for the MarzbanAPI client. * @param {string} config.domain - The domain for the API server. * @param {number} config.port - The port for the API server. * @param {boolean} [config.ssl=false] - Whether to use SSL (default is false). * @param {string} config.username - The username for authentication. * @param {string} config.password - The password for authentication. * @param {string|null} [config.token=null] - An optional pre-existing token for authentication. * @param {number} [config.timeout=10000] - Timeout for API requests (in ms). * @param {number} [config.retryDelay=1000] - Delay between retries (in ms). * @param {number} [config.maxRetries=3] - Maximum number of retry attempts. */ constructor({ domain, port, ssl = false, username, password, token = null, timeout = 10000, retryDelay = 1000, maxRetries = 3 }) { this.domain = `${ssl ? 'https' : 'http'}://${domain}:${port}/api`; this.wsDomain = `${ssl ? 'wss' : 'ws'}://${domain}:${port}/api`; this.credentials = { username, password }; this.token = token; this.timeout = timeout; this.retryDelay = retryDelay; this.maxRetries = maxRetries; } /** * Authenticate the client using the provided username and password. * @returns {Promise<string>} The access token for authenticated requests. * @throws {Error} Throws an error if the authentication request fails. */ async authenticate() { if (this.token) return this.token; const data = qs.stringify({ grant_type: 'password', username: this.credentials.username, password: this.credentials.password, }); const responseData = await this._retry(async () => axios.post(`${this.domain}/admin/token`, data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, timeout: this.timeout, }) ); this.token = responseData.access_token; return this.token; } /** * Handles retry logic for failed requests due to timeouts. * @param {Function} requestFn - The function that performs the HTTP request. * @param {number} [retryCount=0] - The current retry attempt count. * @returns {Promise<Object>} The response data from the request. * @throws {Error} Throws an error if retries are exhausted or if the error is not related to timeouts. */ async _retry(requestFn, retryCount = 0) { try { const response = await requestFn(); return response.data; } catch (error) { if (retryCount < this.maxRetries && error.code === 'ETIMEDOUT') { console.warn(`Request timed out. Retrying (${retryCount + 1}/${this.maxRetries})...`); await new Promise((resolve) => setTimeout(resolve, this.retryDelay)); return this._retry(requestFn, retryCount + 1); } throw error; } } /** * Sends an HTTP request to the API with the specified method. * @param {string} method - The HTTP method to use (e.g., 'get', 'post', 'put', 'delete'). * @param {string} endpoint - The endpoint path for the request. * @param {Object|null} [data=null] - Optional data to send with the request (for POST, PUT). * @param {Object} [params={}] - Optional query parameters for the request. * @param {number} [retryCount=0] - The current retry attempt count. * @returns {Promise<Object>} The response data from the request. * @throws {Error} Throws an error if the request fails after retries or if the token is invalid. */ async _request(method, endpoint, data = null, params = {}, retryCount = 0) { if (!this.token) { await this.authenticate(); } return this._retry(async () => axios({ timeout: this.timeout, method, url: `${endpoint.includes('sub') ? this.domain.replace(/api/, '') : this.domain}${endpoint}`, headers: { Authorization: `Bearer ${this.token}` }, params, data, }) ); } /** * Sends a GET request to the API. * @param {string} endpoint - The endpoint path for the GET request. * @param {Object} params - Optional query parameters for the request. * @returns {Promise<Object>} The response data from the GET request. */ async _get(endpoint, params) { return this._request('get', endpoint, null, params); } /** * Sends a POST request to the API. * @param {string} endpoint - The endpoint path for the POST request. * @param {Object} data - The data to send in the POST request. * @returns {Promise<Object>} The response data from the POST request. */ async _post(endpoint, data) { return this._request('post', endpoint, data); } /** * Sends a PUT request to the API. * @param {string} endpoint - The endpoint path for the PUT request. * @param {Object} data - The data to send in the PUT request. * @returns {Promise<Object>} The response data from the PUT request. */ async _put(endpoint, data) { return this._request('put', endpoint, data); } /** * Sends a DELETE request to the API. * @param {string} endpoint - The endpoint path for the DELETE request. * @returns {Promise<Object>} The response data from the DELETE request. */ async _delete(endpoint) { return this._request('delete', endpoint); } } module.exports = MarzbanAPI;